Studio Quickstart

CrateStack Studio is a single binary you launch locally to inspect data behind one or more .cstack schemas. You describe the workspace once in a studio.toml — schemas, database URLs, and (optionally) deployed service endpoints — and cratestack studio run serves a web UI against those targets. Studio replaces the older generate-studio codegen scaffold from the 0.3.x line. If you were generating a Leptos+Axum project per schema set, see the migration section.
Phase 1d + 4 (current) complete the rewrite. Phase 1d retires the one-text-box-per-field create + edit forms in favor of typed inputs (enum dropdowns, JSON textarea, datetime-local, numeric inputs). Phase 4 ships SQL preview, drift detection, CSV/JSON export, schema search, an audit log, and a SQLSTATE → VALIDATION_ERROR mapping so unique / foreign-key violations come back through the same per-field envelope as the in-process validators.Reference pages: Studio Read API, Studio Write API, Studio UI, Studio Power Tools, Studio Eject.

1. Install The CLI

If you already have cratestack from the workspace build, you’re set. Otherwise from a checkout of the framework repo:
cargo build -p cratestack-cli --bin cratestack
Verify:
cratestack studio --help

2. Seed studio.toml

cratestack studio init
This writes a starter studio.toml in the current directory. Pass --out <dir> to target a different directory; pass --force to overwrite an existing file. The starter file is heavily commented. The minimum a real target needs is a key, a schema path, and one of [target.db] / [target.api]:
[workspace]
name = "acme"
default_mode = "ro"             # "ro" | "rw" — applied when a target omits `mode`

[[target]]
key = "catalog"                 # URL-safe; used in routes and the target switcher
display_name = "Catalog"
schema = "schemas/catalog.cstack"
mode = "rw"                     # overrides workspace default for this target

[target.db]
url = "env:CATALOG_DB_URL"      # `env:NAME` resolves at boot; literals also work
driver = "postgres"             # "postgres" (sqlx) or "sqlite" (rusqlite)
max_connections = 5             # Postgres only; SQLite uses a single connection

[target.api]
base_url = "https://catalog.internal"
auth = { kind = "bearer", token = "env:CATALOG_TOKEN" }
prefer_for = ["procedures"]     # which operations to route here instead of the DB
A target with neither [target.db] nor [target.api] is rejected at load time — Studio has no other way to reach data.

3. Run It

cratestack studio run
By default Studio binds to 127.0.0.1:7878. Override with --bind:
cratestack studio run --config infra/studio.toml --bind 0.0.0.0:9000
While Studio is running:
EndpointPhase 0 behavior
GET /Stub page with a link to /api/health.
GET /api/healthJSON: workspace name + per-target has_db / has_api / mode.
Quick smoke test:
curl http://127.0.0.1:7878/api/health
# {"ok":true,"workspace":"acme","targets":[{"key":"catalog","has_db":true,"has_api":true,"mode":"rw"}]}

4. Multiple Schemas

Studio is designed around multi-target workspaces — one studio.toml can describe every .cstack in a monorepo:
[workspace]
name = "platform"
default_mode = "ro"

[[target]]
key = "catalog"
schema = "services/catalog-service/schema/catalog.cstack"
[target.db]
url = "env:CATALOG_DB_URL"
driver = "postgres"

[[target]]
key = "accounts"
schema = "services/accounts-service/schema/accounts.cstack"
[target.db]
url = "env:ACCOUNTS_DB_URL"
driver = "postgres"

[[target]]
key = "inventory"
schema = "services/inventory-service/schema/inventory.cstack"
mode = "rw"
[target.db]
url = "env:INVENTORY_DB_URL"
driver = "postgres"
[target.api]
base_url = "https://inventory.internal"
auth = { kind = "bearer", token = "env:INVENTORY_TOKEN" }
Each target gets its own entry in the UI’s target switcher.

SQLite targets

Studio talks to SQLite databases through rusqlite. The url accepts sqlite:, sqlite://, sqlite::memory:, and bare file paths.
[[target]]
key = "embedded"
schema = "schemas/embedded.cstack"

[target.db]
url = "sqlite:./local-state.db"
driver = "sqlite"

[[target]]
key = "scratch"
schema = "schemas/scratch.cstack"

[target.db]
url = "sqlite::memory:"
driver = "sqlite"
Studio uses rusqlite rather than sqlx-sqlite so it coexists with the rest of the framework’s SQLite path (cratestack-rusqlite).

Direct DB vs deployed service

A target can declare a [target.db] block, a [target.api] block, or both:
  • [target.db] only — Studio opens its own sqlx pool. Best for schemas without a running service, or when you want to bypass application-level RBAC for debugging.
  • [target.api] only — Studio proxies through your deployed CrateStack service. Same auth and policy the service enforces in production. Pick this for shared environments where direct DB access is undesirable.
  • Both — Studio uses the DB for browsing and routes procedures (and anything in prefer_for) through the API. The DB pool is read-only unless the target also has mode = "rw".

Read-only vs read/write

mode controls whether Studio renders edit affordances and accepts mutation calls for a target:
  • "ro" (workspace default) — list, paginate, follow relations, copy generated Rust queries. No edit/delete UI. Mutation endpoints reject with 403.
  • "rw" — adds inline edits, create flows, delete confirmations, and the validation-error pass-through from cratestack-policy. Lands in Phase 3.
Set the workspace default in [workspace] default_mode, then override per target via mode = "...".

Migration from generate-studio

If you were on the 0.3.x scaffold generator:
  1. Delete the generated studio/ workspace from your repo — Studio is no longer code you check in.
  2. Run cratestack studio init and translate your old flags into [[target]] blocks. Roughly:
    Old flagNew studio.toml field
    --schema <path>target.schema
    --service-url <url>target.api.base_url
    --context <key>target.key
    --name <studio-name>workspace.name
    --mount-path <path>(gone — Studio is its own server)
    --profile dev / --profile prod(gone — runtime-only behavior now)
  3. Add a [target.db] block if you want direct DB browsing. The old generator’s reverse-proxy mode mapped to [target.api] exclusively.
If you forked the Leptos UI, you’ll want to wait for Phase 2 — cratestack studio eject --out ./fork will then copy Studio’s own sources into a workspace you can customize. In Phase 0 / 1 the eject subcommand returns NotImplemented.

Phase roadmap

PhaseScopeStatus
0Skeleton: CLI, config loader, target validation, health endpointShipped
1aRead API: six JSON endpoints + Postgres source + env:/file: secrets + typed snippet generatorShipped
1bSQLite via rusqlite, relation follow, API-backed list/get, Leptos+Trunk UI, dev CORSShipped
1cTyped relation picker in the UI, drawer polish, JSON viewerShipped
2studio eject (writable fork of the UI) + embed-ui cargo feature (single-binary distribution)Shipped
3Write API: POST / PATCH / DELETE, validator pass-through, mode gate, inline create/edit/delete UIShipped
1dTyped pickers for enums, rich editors for JSON / DateTime / Decimal in the create + edit formsShipped
4SQL preview, drift, CSV/JSON export, schema search, audit log, SQLSTATE → VALIDATION_ERROR mappingShipped