Use when building, running, debugging, inspecting, validating, or generating clients for scenery applications. scenery is a Go-native service runtime and CLI using .scenery.json/.config.json app config, //scenery directives, typed endpoints, local dev supervision, logs, traces, metrics, workers, and TypeScript client generation.
Resources
17Install
npx skillscat add scenery-sh/scenery Install via the SkillsCat registry.
scenery
scenery is a Go-native service runtime and local development platform. It runs app-root dev runtimes, exposes capabilities for inspection and action, and hides backing substrate details unless you intentionally debug them. Apps are ordinary Go modules with a .scenery.json app config, or .config.json alias, at the app root and //scenery: directives in Go source.
This skill is the portable agent entrypoint. It teaches shared scenery behavior, but it does not replace app-local instructions. Client apps should also keep a small AGENTS.md with app root, frontend roots, generated client paths, required environment names, validation commands, and product invariants. In target apps, read the root AGENTS.md and every child AGENTS.md on the path to files you expect to touch before editing non-trivial changes.
Read next when needed:
docs/agent-guide.mdfor agent workflow, capabilities, generated artifacts, and client-app integration.docs/local-contract.mdfor exact CLI grammar, JSON schemas, artifact paths, and stability labels.docs/app-development-cookbook.mdfor app recipes.docs/ui-agent-contract.mdbefore UI work.
Agent Fast Path
scenery check --json
scenery doctor --json
scenery inspect app --json
scenery inspect routes --json
scenery inspect endpoints --json
scenery inspect models --json
scenery inspect views --json
scenery inspect wire --json
scenery inspect storage --json
scenery system toolchain verify --json
scenery logs --jsonl --limit 200
scenery inspect observability --json
scenery logs query --json --since 15m --query 'error OR panic'
scenery harness --json --write
scenery validate quick --json --writePrefer JSON output for agent decisions. Prefer scenery up for local development. Use scenery serve for headless API execution. Use scenery task for configured and code tasks. Use scenery validate for app-owned quality gates. Use scenery worker for worker-only cron/Temporal execution.
Run scenery doctor --json before deep app debugging when local readiness is in doubt. It is read-only and reports host resources, Go version, Docker engine reachability/details, optional tools, and app-sensitive dependency hints without building or starting services.
scenery inspect docs --json exposes summary.review_due_count, document-level review_due and stale fields, discovered AGENTS.md scopes, and Child Agent Index drift. For scenery repo changes, scenery harness self --summary --write surfaces those docs knowledge signals in compact validation summaries and leaves full evidence in .scenery/harness/ artifacts. When docs and behavior disagree, the same PR must either fix the affected docs or open/update an ExecPlan that records the drift.
Mental Model
.scenery.jsonmarks the app root;.config.jsonis accepted as an alias when.scenery.jsonis absent.- App-required Go build tags or build-time flags belong in app config as
build.go_flags, for example["-tags=roofmapnet_native"]; Scenery applies them to app builds and generated-workspace tests. - App-owned non-runtime trees that should remain Git-tracked but stay out of
scenery uprebuilds belong in app configwatch.ignore, for example["reference/"]. - Go source is the app model.
scenery upstarts the supervised local platform: app process, rebuild/restart loop, dashboard, API Explorer, logs, traces, metrics, managed dev services when configured, and optional frontend routing through the local agent.- Storage is a Scenery-owned app capability when app config declares
storage. The app-facingscenery.sh/storageAPI, reserved runtime routes, and generated browser helpers are production-supported only with an explicit operator-providedSCENERY_STORAGE_CONFIGwhose stores usekind: "proxy"andproxy_socket; headlessscenery serveand standalonescenery workerreject missing or local-root storage config. Managed ZeroFS behindscenery up, storage inspect/status/Web UI, andscenery storage status|webui|ls|stat|put|get|rm|cleanup --jsonremain beta local-dev/operator surfaces. In agent-backed dev sessions, app storage calls go through Scenery's session-local proxy to the managed ZeroFS 9P socket; apps should not inspect that socket directly. Declared managed ZeroFS is required forscenery up; missing or unsyncable pinnedzerofstoolchain artifacts fail during managed-dev-service preflight with ascenery system toolchain sync --tool zerofsrepair action and ascenery.dev.failure.v1evidence artifact under.scenery/evidence/. ZeroFS process readiness uses a bounded two-minute window and reports the last probe, output tail, config/log paths, sockets, Web UI address, PID, source, cell ID, and session-root evidence artifact on failure. Private stores are internal-only: external storage routes deny them, while app/runtime helpers and Scenery's private route table may use them. Tenant-scoped stores keep caller-visible keys unchanged, derive tenants from standard auth on external routes, and requirestorage.WithTenantID(ctx, tenantID)or standard-auth context for private/internal calls.PutOptions.ContentTypeandPutOptions.Metadataare returned byHead,Get, andList; browser/proxy routes carry metadata throughX-Scenery-Storage-Meta-*headers. Inspect/status includesstorage.runtimewith lease ownership when an agent-managed ZeroFS substrate is attached.scenery downreleases only the current session's ZeroFS lease and preserves shared storage-cell data;scenery storage cleanup --jsonis dry-run by default and requires--yesafter live-lease verification. Treat ZeroFS sockets, object directories, and agent storage roots as substrate details unless intentionally debugging storage. scenery servestarts a headless API-role server and does not start dashboard, proxy, or watch mode.- Public and auth endpoints are externally reachable. Private endpoints are internal-only and called through generated helpers.
- Typed endpoints decode path, query, header, cookie, and JSON body inputs into Go values.
- Generated internal calls preserve routing, private access, auth context, tracing, and error semantics.
Minimal App
{"name":"hello"}module example.com/hello
go 1.26.3
require scenery.sh v0.0.0package service
import "context"
type HelloResponse struct {
Message string `json:"message"`
}
//scenery:api public path=/hello/:name method=GET
func Hello(ctx context.Context, name string) (*HelloResponse, error) {
return &HelloResponse{Message: "hello " + name}, nil
}scenery check --json
scenery serve
curl http://127.0.0.1:4000/hello/worldDirectives
//scenery:api public|auth|private [raw] [path=/...] [method=GET,POST]
//scenery:service
//scenery:authhandlerStandard auth can be enabled from app config without app-local wrapper endpoints. Its tenant tables are framework-owned in PostgreSQL schema scenery_auth including scenery_auth.tenants; app-local tenants services or tables are product-domain concerns only.
Typed endpoint shape:
func Endpoint(ctx context.Context, pathParam string, req *Request) (*Response, error)Raw endpoint shape:
func Endpoint(w http.ResponseWriter, req *http.Request)Struct tags:
- request decoding:
json,header,query,qs,cookie - scenery tags:
scenery:"optional",scenery:"httpstatus"
Public Go Packages
scenery.shscenery.sh/authscenery.sh/errsscenery.sh/middlewarescenery.sh/temporalscenery.sh/cronscenery.sh/storagescenery.sh/dbscenery.sh/pgxpoolscenery.sh/et
Local Development
scenery up
scenery up --json
scenery up --detach
scenery logs --follow
scenery console
scenery downscenery up --json emits JSONL. scenery up --detach starts the app root's agent-backed dev runtime. scenery logs --follow follows that runtime's logs. scenery console opens the source-aware terminal console. Agent routes are canonical; configured friendly hosts appear separately in aliases only for the live app root that owns the free alias. If an app explicitly configures proxy.route_base_domain, scenery up requires the local edge to be ready and fails loudly with DNS, privileged listener, Caddy, and router diagnostics instead of publishing internal :9440 router URLs as user-facing routes. Use scenery up --claim-aliases only for intentional live alias transfer.
Use scenery system edge dns install, scenery system edge privileged install, scenery system edge install, then scenery system edge trust when a browser needs trusted wildcard local HTTPS on 127.0.0.1:443. The DNS command owns wildcard local.dev resolution through managed dnsmasq; the privileged helper owns only the default HTTPS loopback listener and forwards raw TCP to user-owned Caddy on an unprivileged loopback port. Do not run Caddy, the agent router, or scenery system edge install as root. scenery system edge uses managed dnsmasq and Caddy from the toolchain. scenery system edge trust uses a temporary admin-only Caddy process and does not require the port-443 edge to already be running.
For managed Postgres, app processes, setup commands, DB setup, and workers receive the configured app database URL env (DatabaseURL by default) as the app database authority. Scenery does not inject DATABASE_URL into those app-facing environments; treat SCENERY_MANAGED_DATABASE_URL as tooling/debug metadata. The shared Postgres substrate records only physical-server metadata; the runtime database URL/name is a runtime env lease, not a global substrate key. To use an explicit external DB with declared managed Postgres, set SCENERY_DEV_POSTGRES_EXTERNAL=1 and provide the configured app database URL env; DATABASE_URL is ignored.
For Electric-backed frontend writes, generated TypeScript WithMeta methods include parsed txid metadata. Use observeAPIResponseTxid around the app's Electric/TanStack observer so a post-commit sync timeout is reported as SyncObservationError instead of an API mutation failure.
UI Work
Read docs/ui-agent-contract.md before dashboard or app UI work. Use scenery-owned primitives and the @scenery registry; add registry components with commands such as bun run shadcn:add @scenery/button.
The browser UI harness is implemented; use it for dashboard route validation when UI behavior changes. Prefer --write when debugging so screenshots, DOM snapshots, console JSONL, and network JSONL are available under .scenery/harness/ui/.
cd ui
bun run typecheck
bun run test
bun run build
cd ..
scenery harness ui --json --writeDebugging
scenery check --json
scenery inspect app --json
scenery inspect routes --json
scenery inspect endpoints --json
scenery inspect paths --json
scenery inspect storage --json
scenery logs --jsonl --limit 200
scenery logs --source api --level error --jsonl --limit 200
scenery inspect observability --json
scenery logs query --json --since 15m --query 'error OR panic'
scenery traces list --json --since 15m --slowest
scenery metrics list --json --since 1h
scenery metrics query --json --since 15m --step 5s --promql 'scenery_request_duration_seconds'
scenery system toolchain list --json
scenery system toolchain verify --jsonScenery-managed tools live under .scenery/toolchain/, ~/.scenery/toolchain/ for machine-level edge tools, or SCENERY_TOOLCHAIN_DIR. Treat managed dnsmasq, Caddy, Grafana, Victoria, Temporal CLI, and ZeroFS details as substrate unless intentionally debugging them. Agents should not rely on system PATH binaries for those issues; use scenery system toolchain sync --json for app-root tools, scenery system edge dns install for wildcard local DNS, or scenery system edge install for Caddy edge. Use scenery upgrade to replace a prebuilt local Scenery binary with the latest verified release; it then syncs managed toolchain entries already present locally, and --toolchain all pulls every frozen tool/image from the upgraded manifest. Shared substrate failures appear in scenery ps --json under substrates with last_exit, component_exits, and stdout/stderr log paths. Dead registered runtime children such as managed frontend processes appear as session degraded status with status_reason; managed Vite/Astro frontend processes are restarted by scenery up when their dev-server process exits unexpectedly.
Do not introduce new scenery-owned production environment variables by default. Prefer app config, explicit CLI flags, or checked-in manifests; when an env variable is truly required, update docs/environment.registry.json, docs/environment.md, and tests together.
Generated TypeScript Client
scenery inspect endpoints --json
scenery inspect models --json
scenery inspect views --json
scenery inspect wire --json
scenery generate client --lang typescript --output ./src/scenery-client.tsRegenerate committed clients after endpoint, request/response, auth, or wire-capability changes.
Generated model CRUD endpoints are beta and appear in scenery inspect endpoints --json
with "generated": true; generated stores use the configured app database URL
env, defaulting to DatabaseURL, or Scenery's managed database env and target
the app-owned service schema rather than public. Generated CRUD endpoints default to auth for every
action. Generated CRUD route bases default to /<service>/<table>
and scenery check reports collisions with reserved route prefixes
(/__scenery, /api, /sync) or handwritten/generated app routes.
Use model.ExistingTable(schema, table) for read-only generated pages/endpoints
over an existing physical table; inspect models exposes source metadata, schema/seed
generation skips that table, and generated mutations or model.Seed(...) rows are rejected.
Generated Atlas HCL uses schema-qualified resource labels such astable "<service>" "<table>" so app-owned schemas can coexist with handwritten
multi-schema HCL.scenery generate data --dry-run --json
also writes beta generated frontend packages under .scenery/gen/web/<frontend>/
for configured frontends with static collection pages, including runtime adapter
factories, page projection records in projections.ts, static page filter/sort/display metadata, default page components, and route registration helpers for app-owned Electric/TanStack/layout-kit wiring;
generated Electric shape metadata uses the same schema-qualified table as the DB artifacts.
To mount a generated page, declare the entity/page in Go, run scenery generate data --dry-run --json, point a frontend alias such as @scenery/generated at .scenery/gen/web/<frontend>/index.ts, import the generated page or route from that alias, mount it, and run the host typecheck/render or build command.
When an app configures generators, prefer:
scenery inspect generators --json
scenery generate --dry-run --json
scenery generateKeep scenery generate for file generation only. scenery generate sqlc may refresh generated schema SQL and run sqlc generate, but it must not apply database schema or seed data.
Use scenery db apply for schema/app database mutation only. Use scenery db seed for initial data such as SERVICE/db/seed.sql and generated model seed files under .scenery/gen/db/<service>/seed.sql; changed previously-applied seeds and destructive seed SQL fail closed with path/line diagnostics. Use scenery db setup for apply then seed. scenery up runs this setup lifecycle before app startup when DB setup inputs exist, then skips it on ordinary rebuilds until database.apply config or seed file hashes change.
Generated model CRUD endpoints default to auth-only. If a generated entity has a convention tenant field (TenantID or tenant_id), Scenery derives it from standard-auth tenant data, keeps it out of create/patch payloads, and currently supports string, named string types, or github.com/google/uuid.UUID tenant fields.
Generated list endpoints are bounded: default limit=100, maximum limit=500, and non-negative offset.
Generated create/patch payloads accept response field names such as CreatedAt as well as DB-column JSON names such as created_at; time.Time values should be RFC3339 JSON timestamps and malformed values fail decoding.
For managed Postgres branch work, use scenery db postgres status --json to inspect the shared local Postgres dev cell, scenery db postgres start --json/stop --json to manage it, scenery db branch status --json to inspect .scenery/worktree-db.json, and scenery db branch list --json to inspect Scenery-owned local branch leases in branches.json under the agent Postgres state root. The provider supports dev.services.postgres.kind: "postgres", mode: "local", and isolation: "database" through template database cloning. checkout creates or reuses a branch database from the protected parent template database and records a ready endpoint without persisting raw connection URLs. reset recreates the branch from the parent template, delete drops the branch database and removes its lease, expire updates lease metadata, prune removes expired non-current branch databases when the Postgres admin substrate is reachable, and restore currently maps to template reset. scenery up, scenery db psql, DB setup, and Electric consume ready branch endpoints and fail explicitly when the lease is missing, expired, protected, or endpoint-less. The default scenery harness self --json --write path includes the live Postgres branch lifecycle proof; use --quick for the smaller self-harness mode.
Tasks
Use scenery task for configured repo tasks and app-local code tasks. Configured tasks use plain names from app config. Code tasks use <domain>:<name> and run from the app root without requiring the app model to parse cleanly.
scenery task list --json
scenery task inspect <target> --json
scenery task run <name>
scenery task run <domain>:<name> -- [task args...]Single-file Go code tasks should live under a domain tasks directory and start with //go:build ignore, for example:
billing/tasks/reconcile.task.go
billing/tasks/reconcile/main.goCommand Reference
Use docs/local-contract.md for the full grammar. Common agent commands:
scenery up [--app-root <path>] [--claim-aliases] [--json] [--detach]
scenery logs --follow [--app-root <path>] [--jsonl|--json]
scenery console [--app-root <path>]
scenery system edge install|trust|status|restart|uninstall|dns|privileged [--json]
scenery help <command>|all|--json
scenery ps [--json] [--app-root <path>] [--watch]
scenery down [--app-root <path>] [--json]
scenery serve [--app-root <path>] [--env <name>] [--log-format text|json]
scenery worker [--task-queue <name>[,<name>...]]... [--app-root <path>] [--env <name>]
scenery worker temporal prune --stale [--yes] [--app-root <path>] [--json]
scenery version --json
scenery upgrade [--version latest|vX.Y.Z] [--target <path>] [--toolchain installed|all|none] [--force] [--dry-run] [--json]
scenery system toolchain list [--json] [--include-source-locks] [--all] [--tool <name>] [--platform <goos/goarch>] [--images]
scenery system toolchain sync [--json] [--all] [--tool <name>] [--platform <goos/goarch>] [--images]
scenery system toolchain verify [--json] [--all] [--tool <name>] [--platform <goos/goarch>] [--images] [--strict]
scenery system toolchain path [--json] --tool <name> [--platform <goos/goarch>]
scenery build [--app-root <path>] [-o <path>]
scenery check [--app-root <path>] --json
scenery generate [--app-root <path>] [--dry-run] [--json]
scenery task list [--app-root <path>] [--json]
scenery task inspect <target> [--app-root <path>] [--lang go|typescript] [--json]
scenery task run <name> [--app-root <path>]
scenery task run [--app-root <path>] [--env <name>] [--lang go|typescript] <domain>:<name> [-- task args...]
scenery task graph --json [--app-root <path>]
scenery validate [<profile>] [--app-root <path>] [--json] [--write] [--dry-run]
scenery validate list [--app-root <path>] [--json]
scenery validate inspect <profile> [--app-root <path>] [--json]
scenery validate graph [<profile>] [--app-root <path>] --json
scenery validate changed [--base <ref>] [--app-root <path>] [--json] [--write] [--dry-run]
scenery harness [--app-root <path>] --json --write
scenery harness self [--repo-root <path>] --summary --write [--fresh-tests]
scenery storage status [--app-root <path>] --json
scenery storage webui [--app-root <path>] --json
scenery storage ls <store> [--prefix <prefix>] [--cursor <cursor>] [--limit <n>] [--app-root <path>] --json
scenery storage stat <store> <key> [--app-root <path>] --json
scenery storage put <store> <key> <file> [--app-root <path>] --json
scenery storage get <store> <key> --output <file> [--app-root <path>] --json
scenery storage rm <store> <key> [--recursive] [--app-root <path>] --json
scenery storage cleanup [--yes] [--app-root <path>] --json
scenery inspect app|routes|services|endpoints|models|views|wire|build|paths|generators|temporal|storage|observability --json [--app-root <path>]
scenery inspect docs --json [--repo-root <path>]
scenery traces list --json [--app-root <path>]
scenery metrics list --json [--app-root <path>]
scenery logs query [--app-root <path>] --query <logsql> [--json]
scenery logs tail [--app-root <path>] --query <logsql> [--since <duration>] [--jsonl]
scenery metrics query --json [--app-root <path>] --promql <query>
scenery metrics labels --json [--app-root <path>] [--match <selector>]
scenery metrics series --json [--app-root <path>] --match <selector>
scenery traces clear --json [--app-root <path>]
scenery logs [--app-root <path>] [--limit <n>] [--jsonl|--json]
scenery test [--app-root <path>] [go test flags/packages...]
scenery generate client [<app-id>] --lang typescript --output <path> [--app-root <path>]
scenery db psql [--app-root <path>] [psql args...]
scenery db apply [--app-root <path>] [--json]
scenery db seed [--app-root <path>] [--dry-run] [--json]
scenery db setup [--app-root <path>] [--json]
scenery db reset|drop|snapshot [--app-root <path>]
scenery db branch status|list [--app-root <path>] [--json]
scenery db branch checkout <name> [--app-root <path>] [--json]
scenery db branch reset [--app-root <path>] [--yes]
scenery db branch delete <name> [--app-root <path>] [--force]
scenery db branch restore --at <timestamp-or-lsn> [--app-root <path>] [--yes]
scenery db branch diff <branch> [--app-root <path>] [--json]
scenery db branch expire [<name>] --after <duration> [--app-root <path>] [--json]
scenery db branch prune [--older-than <duration>] [--app-root <path>] [--json]
scenery db postgres install|start|status|logs|stop|restart|uninstall [--json]
scenery worktree create <name> [--from <branch>] [--app-root <path>] [--json]
scenery worktree list [--app-root <path>] [--json]
scenery worktree remove <name> [--app-root <path>] [--db] [--json]scenery up warns once when the current dev session's Temporal task queues contain open workflows from an older session. Use scenery worker temporal prune --stale --json to inspect candidates; add --yes only when you intentionally want Scenery to terminate those stale workflows.
Self-harness Go test steps use the Go test result cache by default. Pass--fresh-tests when a fresh -count=1 run is intentionally required.
Validation Before Finishing
For app changes:
scenery check --json
go test ./...
scenery harness --json --write
scenery validate quick --json --writeFor scenery repo changes:
go test ./...
go test ./cmd/scenery
scenery harness self --summary --writeDo not run go install ./cmd/scenery unless the human explicitly asks. Multiple
worktrees can share one installed binary; self-harness builds a worktree-local.scenery/harness/bin/scenery for freshness checks.