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::Anybuild andstrict-postgres. - API reference: Render the operations of an
OpenApidocument 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_previewsprojection: 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
DbFeatureDrivercaches in the same process. Studio never creates therullst_feature_flagstable; 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
Cacheexposes 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
Hostauthority on every request, and requires a same-originOriginheader 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.