scenery-sh

scenery

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.

scenery-sh 2 Updated 3w ago

Resources

17
GitHub

Install

npx skillscat add scenery-sh/scenery

Install via the SkillsCat registry.

SKILL.md

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.md for agent workflow, capabilities, generated artifacts, and client-app integration.
  • docs/local-contract.md for exact CLI grammar, JSON schemas, artifact paths, and stability labels.
  • docs/app-development-cookbook.md for app recipes.
  • docs/ui-agent-contract.md before 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 --write

Prefer 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.json marks the app root; .config.json is accepted as an alias when .scenery.json is 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 up rebuilds belong in app config watch.ignore, for example ["reference/"].
  • Go source is the app model.
  • scenery up starts 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-facing scenery.sh/storage API, reserved runtime routes, and generated browser helpers are production-supported only with an explicit operator-provided SCENERY_STORAGE_CONFIG whose stores use kind: "proxy" and proxy_socket; headless scenery serve and standalone scenery worker reject missing or local-root storage config. Managed ZeroFS behind scenery up, storage inspect/status/Web UI, and scenery storage status|webui|ls|stat|put|get|rm|cleanup --json remain 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 for scenery up; missing or unsyncable pinned zerofs toolchain artifacts fail during managed-dev-service preflight with a scenery system toolchain sync --tool zerofs repair action and a scenery.dev.failure.v1 evidence 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 require storage.WithTenantID(ctx, tenantID) or standard-auth context for private/internal calls. PutOptions.ContentType and PutOptions.Metadata are returned by Head, Get, and List; browser/proxy routes carry metadata through X-Scenery-Storage-Meta-* headers. Inspect/status includes storage.runtime with lease ownership when an agent-managed ZeroFS substrate is attached. scenery down releases only the current session's ZeroFS lease and preserves shared storage-cell data; scenery storage cleanup --json is dry-run by default and requires --yes after live-lease verification. Treat ZeroFS sockets, object directories, and agent storage roots as substrate details unless intentionally debugging storage.
  • scenery serve starts 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.0
package 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/world

Directives

//scenery:api public|auth|private [raw] [path=/...] [method=GET,POST]
//scenery:service
//scenery:authhandler

Standard 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.sh
  • scenery.sh/auth
  • scenery.sh/errs
  • scenery.sh/middleware
  • scenery.sh/temporal
  • scenery.sh/cron
  • scenery.sh/storage
  • scenery.sh/db
  • scenery.sh/pgxpool
  • scenery.sh/et

Local Development

scenery up
scenery up --json
scenery up --detach
scenery logs --follow
scenery console
scenery down

scenery 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 --write

Debugging

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 --json

Scenery-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.ts

Regenerate 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 as
table "<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 generate

Keep 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.go

Command 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 --write

For scenery repo changes:

go test ./...
go test ./cmd/scenery
scenery harness self --summary --write

Do 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.