Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rullst Studio 📊

Important

This page describes the v13 prerelease, published as 13.0.0-alpha.4. Cargo never selects a prerelease on its own, so its dependency example pins that version exactly. The v12 line (rullst-studio@12) reached end of life on 16 October 2026; see the release record. Use a path dependency only for checkout-local review.

rullst-studio is the built-in, local-first administration and monitoring dashboard for Rullst. It exposes bounded database, queue, cache and telemetry views from the sources explicitly supplied by the application.

✨ Features

  • Database inspector: Read and filter configured SQLx tables, edit bounded primitive non-key values, delete one complete-primary-key-selected row with exact confirmation, and inspect a live ER diagram. Tables whose key includes a column outside the ASCII identifier boundary stay read-only, and each write commits only when exactly one row changed. Views show 25 rows, cut cell text to 256 characters in the database and accept search terms up to 256 bytes. SQLite, PostgreSQL, MySQL and MariaDB run executable mutation contracts; PostgreSQL runs under both the default sqlx::Any build and strict-postgres.
  • API reference: Render the operations of an OpenApi document explicitly supplied by the application at /studio/api (server-side, no script or remote asset) and serve the document at /studio/api/openapi.json; Studio does not infer arbitrary Axum routes or send requests for you.
  • Worker queue monitoring: Inspect up to 50 records exposed by a supplied Rullst queue and request retries. Records come from the bounded v13 list_job_previews projection: at most 2 KiB of each payload and error leaves the SQLite or Redis store, and previews cut there are marked …. SQLite removes successful jobs, so the view is not durable completion history.
  • Safe configuration view: Environment values are deny-by-default redacted; typed runtime configuration is projected without URLs, paths, or secrets.
  • Feature flags manager: Toggle database-backed flags and immediately invalidate already-warm DbFeatureDriver caches in the same process. Studio never creates the rullst_feature_flags table; a missing table is reported with the schema to add in a migration.
  • Distributed diagnostics: Visualize in-process sources plus bounded, attribute-free v1 spans from a separately mounted HMAC-authenticated push endpoint. Slow-query and repeated-label findings are heuristics; no SQL text, bindings, attributes, headers, bodies or error details are accepted.
  • Cache inspector: An explicitly supplied memory or Redis Cache exposes bounded metadata and individual invalidation through opaque process-bound tokens. Values, exact keys and bulk flush remain unavailable in the UI.
  • Local-first security: The supported launcher binds to loopback, verifies the direct peer and local Host authority on every request, and requires a same-origin Origin header for mutations.
  • Self-contained interface: Compiled CSS and the bounded request-stream client are served from the Studio router itself. The core operator pages do not require Google Fonts, HTMX, Mermaid, a browser-side CSS compiler, or a third-party CDN.

🚀 Quickstart

Install the v13 prerelease with an exact pin: cargo add rullst-studio@=13.0.0-alpha.4.

Launching the Studio

The supported mode is a standalone debug server. run_studio and Studio::into_router(LocalStudioAccess::loopback_only()) reject release builds and requests whose direct peer is not verified as loopback. Servers composing the router manually must preserve Axum ConnectInfo<SocketAddr>. The access capability also rejects DNS-rebinding-style non-local Host values, cross-origin requests, and unsafe requests without an Origin header. Responses use Referrer-Policy: same-origin, so browsers send the real origin on Studio’s own form posts. Origin: null passes only together with Sec-Fetch-Site: same-origin, as browsers send it when a host layer imposes no-referrer; a bare null origin or a same-site document on another local port is rejected. Database, queue, cache and feature-flag writes also require a crate-private marker that only this capability installs, so raw subrouters mounted elsewhere return 403 for them.

Remote projects (added in 13.0.0-alpha.3)

The rullst-studio binary (cargo install rullst-studio --version =13.0.0-alpha.4 --features remote --locked, then rullst-studio [--port PORT], or cargo rullst studio --remote; library users enable the non-default remote feature) serves Studio on 127.0.0.1:5556 without an application process and reads the read-only /_rullst/monitor/v1 document of the projects you add: overview, routes, queues and N+1 pages with a project switcher. It keeps the loopback Host, same-origin and marker checks above, adds a per-process form token, and runs in release builds because it serves no local data: the data browser, .env viewer, AI and API playgrounds and migration pages are not mounted. Projects are stored owner-only in the user’s configuration directory, with each token AES-256-GCM encrypted under a key in the data directory and bound to its name and base URL. Requests require https:// (or http:// to loopback), follow no redirects, ignore proxy variables and read at most 1 MiB in 5 seconds. See the production monitoring guide.

History and alerts: projects marked as watched are polled every 60 s while the binary runs; their aggregates go to a bounded owner-only SQLite file (seven days) charted on /studio/history, and per-project rules (unreachable, 5xx rate, p95, pending jobs, queue failures) fire with a cool-down on /studio/alerts, optionally posted once to an encrypted https:// webhook.

Studio uses the ORM pool that the application initialized. When none exists, its first database view initializes one from the resolver shared by Server and Artisan (process DATABASE_URL, then ./.env, then [database].url in Rullst.toml). Without a configured database it reports the database tools as unavailable and creates nothing; there is no SQLite fallback.

The earlier StudioLayer embedded-production idea was never implemented. Keeping an authenticated shared Studio is worthwhile, but it needs its own explicit identity/RBAC/TLS policy before it can become a supported mode.

Since 12.1.0, the raw data_browser::router() also serves the assets and Cache routes its layout uses (12.0.0 omits them; the full local builder is not affected). When upgrading from 12.0 with application-level workaround routes, see the 12.1.0 composition fix and migration checklist.

CLI Launch:

Inside a project, the CLI runs the application with the studio argument (cargo run -- studio):

cargo rullst studio

Core’s Artisan handler then serves a smaller local compatibility UI (database, AI, telemetry, security, capital and span pages) on 127.0.0.1:5555, not the rullst-studio router described on this page.

Authenticated trace producers

#![allow(unused)]
fn main() {
use rullst_studio::distributed_traces::{
    DistributedTraceStore, TraceIngestionKey, TraceIngestor,
};
use rullst_studio::{LocalStudioAccess, Studio};

fn build() -> Result<(), Box<dyn std::error::Error>> {
let store = DistributedTraceStore::new(2_048)?;
let key = TraceIngestionKey::new(std::env::var("RULLST_TRACE_INGESTION_KEY")?)?;
let ingestion = TraceIngestor::new(store.clone(), "api-1", key)?;
let _push_only_application_router = ingestion.router();
let _local_viewer = Studio::new()
    .with_distributed_traces(store)
    .into_router(LocalStudioAccess::loopback_only())?;
Ok(())
}
}

Each ingestor binds one exact producer name to one key; mount separate producer endpoints over the same store when needed. TraceBatchSigner::new binds the same pair and produces the byte-identical JSON body plus source, timestamp, nonce and signature headers. Mount the push-only router under an application path; it exposes no Studio reads or administrative mutations. The application/deployment still owns TLS, network policy, key distribution and rotation, clock synchronization, availability and label redaction. The store is bounded process memory, not OTLP or durable trace storage.

🔐 Security Audit

rullst-studio currently supports verified-loopback development access. It does not provide a built-in shared-intranet or production authentication mode. Do not expose raw subrouters publicly; a future shared mode must fail closed behind application-owned authentication, administrator authorization, TLS, and network policy.

The built-in migration page intentionally links to cargo rullst db:* commands. The compatibility HTTP mutation handlers return 501 Not Implemented because the standalone Studio has no configured migration or seeder registry. Queue and revenue panels likewise show only data supplied by the selected driver or application; unsupported operations return errors instead of simulated success.

The SSE request view records method, URI, status and latency only. It does not capture bodies or headers by default because those can contain authentication, session, payment, and personal data. A successful Studio toggle invalidates all already-warm DbFeatureDriver entries in the same process. Other processes and direct database writers remain visible through the configured TTL unless the application supplies distributed invalidation.

Studio::with_cache opts one Cache into metadata-only inspection. The page shows a keyed opaque identifier, UTF-8 value byte length and remaining TTL for at most 100 entries. It never returns the value or logical key, offers no bulk flush, and requires the verified local mutation marker to invalidate one entry. Memory and Redis implement this contract; custom drivers return an explicit unsupported state unless they implement bounded inspection.

📚 Documentation

For supported usage and security boundaries, see this book and the capability ledger.