Skip to content

DevTools ​

In-app debug bar for Sova HTML apps — Symfony / Clockwork style. Inspect the last request, SQL, logs, outbound HTTP, mail, session, and a live site-wide timeline — without leaving the browser.

Development only

DevTools is off in release builds (cargo build --release). Toml or .enabled(true) cannot turn it on there. Ops escape hatch: SOVA_DEVTOOLS=1.

DevTools tour

Install ​

bash
cargo add sova --features "web,devtools"
rust
use sova::{App, DevTools, Parser, ServerArgs};

#[tokio::main]
async fn main() -> sova::Result<()> {
    let args = ServerArgs::parse();
    args.init_tracing();

    let mut app = App::web()
        .site("App")
        .public_url("http://127.0.0.1:3000")
        .into_app();

    app.install(DevTools::new()); // on in debug / development by default
    app.get("/", || async { sova::Html("<html><body><h1>hi</h1></body></html>") });
    app.run().await
}

Demo app in the repo:

bash
cargo run -p devtools_demo
# http://127.0.0.1:3030/

What you get ​

SurfacePurpose
Bottom barStatus / time / SQL / errors chips; click to dock the panel
Dock panelFull Vue SPA in an iframe (/_devtools/app?embed=1)
New tabOpen the same SPA in a browser tab
SSE feedLive request.finished, custom, memory.sample
JSON APISnapshots, logs, custom events, memory, config under /_devtools/*

Tabs ​

In New tab / shell mode (/_devtools/app) tabs are grouped in the left sidebar:

GroupTabs
InspectRequest, Timeline
StorageDB, Cache, Jobs
MessagingRedis, Rabbit
APIsHTTP, GraphQL, gRPC
RuntimeLogs, Mail, Auth, Events, Memory, Config

The dock iframe keeps a compact horizontal tab bar (space-constrained).

TabContents
RequestMethod, path, status, duration, route / locale / CSRF / rate-limit / encoding
TimelineRecent requests (SSE); click to load a snapshot
DBSQL queries for the selected request (bindings redacted)
CacheAppStore / Cache ops (sova.store) + KV console
RedisRedis ops (sova.redis) + Redis console (Messaging group)
Logstracing / console lines (per-request + site-wide)
HTTPHTTP client (replay) + outbound client traces
MailFakeMail / last messages (with mail feature)
JobsTask enqueue / worker (sova.tasks)
AuthSession keys + user / email / roles; Session console (edit keys)
EventsCustom hub.emit + forwarded domain EventBus (auth/mail/csrf/session/tasks/…)
MemoryProcess RSS sparkline + current/peak/min (Linux + macOS; ~2s)
ConfigProfile + compiled DevTools feature flags

What comes from where ​

SignalSourceNeeds
Route patternMatchedRouteCapture soft-hookalways
LocaleLocaleCodedevtools-i18n / sova-devtools/i18n
CSRF presentCsrfTokendevtools-csrf
Rate-limit headersratelimit-* / x-ratelimit-* on responsealways (headers)
Content-Encodingresponse headeralways
Session / userSession + CurrentUserdevtools (auth)
SQLsqlx / SeaORM logsdevtools (db) + sqlx logging
Outbound HTTPhttp.client spanshttp-client + sova-devtools/http
MailFakeMail bag + MailSent EventBusmail
Custom eventsDevToolsHub::emit / auth EventBusalways (hub) / auth
Memory RSSbackground sampler (cross-platform)always when DevTools enabled
Cache / KVtracing target: "sova.store"devtools-store (instrumented store)
Redis pub/queuetarget: "sova.redis"devtools-redis
Jobstarget: "sova.tasks"devtools (tasks)

Facade features: devtools, devtools-store, devtools-redis, devtools-i18n, devtools-csrf, devtools-passport, devtools-rate-limit.

Request tab

Timeline

DB queries

Logs

Outbound HTTP

How it works ​

  1. Middleware opens a collector bag for every non-/_devtools request (needs request_id).
  2. Soft hooks attach SQL / HTTP / mail / session data while the request runs.
  3. On finish, a snapshot is stored and broadcast over SSE.
  4. HTML responses get a tiny host marker + bridge.js (not the full SPA).
  5. The bar toggles a dock iframe; New tab opens /_devtools/app.

Access logs for /_devtools/* are skipped via logger_skip_path("/_devtools") so the panel does not pollute the console or its own Logs tab.

Enable / disable ​

ContextBehavior
cargo run / debug build, development profileOn by default
Debug build + SOVA_PROFILE=productionOff, unless .enabled(true) or SOVA_DEVTOOLS=1
cargo build --releaseOff, unless SOVA_DEVTOOLS=1
SOVA_DEVTOOLS=0Always off
toml
[development.devtools]
enabled = true

[production.devtools]
enabled = false
bash
SOVA_DEVTOOLS=1   # force on (including release)
SOVA_DEVTOOLS=0   # force off
rust
app.install(DevTools::new());                 // default
app.install(DevTools::new().enabled(true));   // debug only — ignored in release
app.install(DevTools::new().request_cap(200).log_cap(1000));

Filling the tabs ​

Install related plugins before DevTools when you want those panels populated:

rust
app.install(Mail::from_env());       // Mail tab (fake backend)
app.install(OutboundHttp::new());    // HTTP tab
app.install(DevTools::new());

For Cache / Redis / Jobs, use instrumented plugins (sova-store / sova-redis / sova-tasks) and enable the matching facade features (devtools-store, devtools-redis; jobs come with devtools).

SQL (SeaORM): enable sqlx logging, e.g. Db::from_env().sqlx_logging(true) and/or RUST_LOG=sqlx=debug.

Skip other noisy routes from access logs:

rust
sova::logger_skip_path("/healthz");

Responsive UI ​

The dock uses the host window width for breakpoints. The UI playground (Vite) embeds the same SPA in an iframe so phone/tablet presets exercise real media queries:

bash
npm --prefix plugins/sova-devtools/ui run playground
# http://localhost:5175/playground.html

Desktop playground

Tablet

Mobile

Console (control panel) ​

With the console feature (included in facade devtools / devtools-console), DevTools provides interactive consoles:

UIAction domainEndpoint
HTTP tabHTTP replay (app + external) + outbound tracesPOST /_devtools/actions/http
Redis tabRedis GET/SET/DEL/TTL/SCAN/PUBLISH + SSEPOST /_devtools/actions/redis, GET /_devtools/stream/redis
Cache tabAppStore namespace opsPOST /_devtools/actions/store
GraphQL tabIn-process schema executePOST /_devtools/actions/graphql
Jobs tabEnqueue taskPOST /_devtools/actions/tasks
Mail tabFake composePOST /_devtools/actions/mail
Events tabEmit custom eventPOST /_devtools/actions/events
Auth tabSession list/set/del/regeneratePOST /_devtools/actions/session (devtools-console-session)

Console POST requests must include CSRF token + cookies when the app uses the web preset. The UI loads csrf_token from GET /_devtools/config on boot and sends X-XSRF-TOKEN on every console action.

The HTTP tab combines a Postman-like client (method/path, query params, headers, body, response, history) with outbound trace lines for the selected request. Double-click a request in the timeline → prefill + navigate to HTTP.

Redis in demo: Redis::fake() — in-memory, no Docker:

rust
app.install(Redis::fake(FakeRedis::new()));
app.state(AppDispatch::default());
app.install(DevTools::new().console(true));

Facade: devtools-console, devtools-console-redis, devtools-console-store, devtools-console-graphql, devtools-console-tasks, devtools-console-mail, devtools-console-http-external, devtools-console-events, devtools-console-rabbit, devtools-console-session.

Security notes ​

  • /_devtools/* is a debug surface (GET reads + POST console actions) — do not expose it on the public internet.
  • Release builds keep the plugin inert even if you forget to strip the feature from Cargo.toml.
  • Session values and SQL bindings are redacted/masked in the UI.
  • HTML pages that get the bar send Cache-Control: no-store so browser Back is not served from bfcache (otherwise no server hit → empty Timeline).