CLI reference
Every spky command, grouped by what you're trying to do. Run spky <command> --help for full flags.
Commands read sp00ky.yml from the current directory. Pass --path <dir> to point at a project
somewhere else, and spky <command> --help for the flags of any single command.
Project & dev loop
| Command | What it does |
|---|---|
spky init | Scaffold a new project: schema, app, config, first migration. |
spky dev | Start the local stack: SurrealDB, SSP, scheduler, your apps. A Worker backend runs from its dev command at its baseUrl, not under workerd. With sync.queryAllowlist on, passes the mode to the SSP and publishes the allowlist JSON as version dev on every start. Docs |
spky generate (gen) | Generate client types from sp00ky.yml, or a single file via --input/--output. --format json|typescript|dart|surql, --all, --append, --no-header, --mode, --endpoint, --secret, --modules-dir. For a typescript entry with queries, also records the query allowlist by running the query module through @spooky-sync/query-allowlist (the app’s node_modules/.bin/spooky-query-allowlist, else npx) and writes <queries>.allowlist.json; commit it. Docs |
spky migrate | Database migrations: create, apply, prod, status, fix. prod migrates the cloud deployment without deploying. apply --endpoint <url> --secret <secret> also installs the sync params a self-hosted database needs; --mode defaults to mode in sp00ky.yml. Docs |
spky lint | Validate sp00ky.yml, including each Worker backend’s entry and expose patterns. |
spky doctor [--json] | Project health: config, codegen freshness, migrations, Docker. Docs |
spky verify [--fix] | Check the SSP/scheduler snapshot against upstream SurrealDB and print the scheduler’s own drift verdict; --fix re-clones the replica when its counts are off, else forces every SSP to re-bootstrap. |
spky query [sql] | Run SurrealQL once, or open a REPL. --cloud targets the deployment, --json prints raw JSON instead of a table. |
spky version | Print version information. |
Repository performance benchmark
The repository also contains spooky-bench, a separate developer tool in
apps/benchmark. Its whitepawn suite measures scheduler and SSP bootstrap,
50-row live windows with related player names, scrolling with twenty retained
windows, and propagation during steady writes and import bursts. It uses
deterministic synthetic chess-game libraries and real database changefeeds.
Measured startup, latency, throughput, memory and backlog results are published
in the WhitePawn performance baseline,
including completed cases and resource-limited tiers.
--smoke runs compact and PGN-heavy 10K-game cases with short load phases.
--profile standard runs 100K compact, 1M compact and 1M PGN-heavy cases. The
default large profile adds 10M compact games. Each case starts a disposable
SurrealDB container and fresh scheduler/SSP processes; Docker must be running.
The benchmark records registration and publication latency separately and checks
published root/child references and record versions. Scroll evictions remove
both published references and durable registration metadata. Bounded checks
between phases verify the retained registration ids and graph/follower counts.
Raw reports include CPU, memory, write and readiness samples. Capacity checks
include games added by load phases and reserve disk and memory headroom.
Without a smaller measured PGN-heavy case, PGN projections use measured compact
costs plus payload allowances as a floor, keeping the larger value for each
resource against the initial PGN estimate. A preflight resource-limited result
records a conservative estimate, not an observed out-of-memory failure, and
produces a nonzero exit rather than a successful benchmark result.
Use whitepawn --help for binary paths, image, readiness timeout and output
options. The default image is surrealdb/surrealdb:v3.1.5.
--startup-timeout-secs bounds startup and publication-readiness waits,
including propagation probes and load catch-up, at 3,600 seconds per wait by
default. It is separate from the overall run duration. --schema accepts a
custom source schema compatible with the chess fixture. The repository’s
apps/benchmark/README.md describes the
dataset matrix, load phases, measurements and existing focused benchmark
suites. This command is invoked with spooky-bench, not spky.
Adding things to a project
| Command | What it does |
|---|---|
spky api add | Register a backend and generate its outbox table. Docs |
spky bucket | add a file bucket, or configure its storage backend. Docs |
spky flag | Feature flags: list, create, get, delete, enable, disable, set, unset, eval. set adds a targeting rule, eval dry-runs one user. Docs |
spky admin | Operator roster (_00_admin): list (ls), add <user>, remove <user>. Grants access to the feature-flag editor and the admin dashboard. Root-only, so nobody promotes themselves. |
spky recipe <name> | Render a cookbook snippet (live-list, optimistic-mutation, crdt-text-field). spky recipe list for the index. |
spky agents init | Generate AGENTS.md, parameterized by your schema. Docs |
spky mcp | MCP server for editors and agents: serve, token, tokens, revoke, install. Docs |
Running unattended: -y / --yes
Every subcommand accepts -y / --yes, and SPKY_YES=1 does the same from the environment.
It answers confirmation prompts without asking, so a command never sits waiting for a keypress.
This is the flag a CI job or a coding agent should pass. A pipe is easy for the CLI to detect, but an agent usually runs commands on a pseudo-terminal, where the prompt renders normally and then waits forever for input nothing can send.
The two kinds of question get different answers, because “yes to everything” is not what anyone means:
| Question | Example | With --yes | No terminal, no --yes |
|---|---|---|---|
| Consent to something consequential | Apply to PRODUCTION? Restart SurrealDB? Overwrite this directory? | Yes, and the log says so | Refuses, naming the flag |
| Preference with a default | Initialize git? Cache the vault key? Create a backup first? | The default | The default |
| Irreversible | Destroy the project. Reset its database. | Not covered | Refuses |
The last row is deliberate. --yes is the flag that gets added to every command by reflex, which
makes it the wrong thing to arm “delete everything” with. Those two take the project’s name instead:
--yes also switches the guided flows (spky login, picking or creating a project, billing setup)
to their unattended path. They lead into pickers and browser logins that no flag can answer, so
instead of opening one they stop with the exact command or environment variable to use.
Consent without a terminal used to be inconsistent: some commands hung, some failed with a bare
“Failed to read confirmation”, and spky migrate prod and spky link disconnect skipped the
question and went ahead. They now refuse like everything else. If a script relied on that,
add --yes to it.
Operating a running system
| Command | What it does |
|---|---|
spky jobs | Background jobs: list, get, kill, retry, clear. clear -A/--all also drops queued jobs; running ones are never deleted. No subcommand opens the interactive dashboard. Docs |
spky schedules | Server-side schedules: list, get, pause, resume, trigger, release, runs, sync. release <name> <key> lets a quarantined forEach key fire again. Docs |
spky workflows | Workflow DAGs: list, show, watch, runs, kill. show renders the graph, watch follows a run live. Docs |
spky push | Web Push: status, send, devices, sync. send pushes to users directly and waits for the delivery result. Flags |
spky logs | Tail or browse logs. --filter, --split, --since, --until, --grep, -i/--interactive (TUI), -F/--follow. Docs |
spky stats | Live CPU, memory, disk and network graphs. --window 1h, --filter backend,surrealdb. |
spky status | Deployment version, status and running VMs. |
spky restart [TARGET...] | Restart deployment containers. With no target: the scheduler and SSPs. Flags |
spky scale ssp <n> | Scale SSP instances. |
Cloud
| Command | What it does |
|---|---|
spky login, spky logout | Authenticate with Sp00ky Cloud. |
spky project | Manage cloud projects: create, list, credentials, move, destroy. create --plan takes payg (Cloudflare, pay as you go; the default), starter or pro (VMs). credentials --raw prints just the password, for piping. destroy deletes the project with its data and its backups, and frees the slug; a new project with that slug starts empty. move puts a Pro project that runs on the shared host onto VMs of its own: it copies the data with the project running, then waits for move cutover unless --no-hold is given (--no-wait returns at once). Subcommands: status, watch, cutover, abort, rollback. cutover and rollback ask first, because the project goes down. A pay-as-you-go project’s own database gets a root login like a VM project’s; on the retired free plan’s shared database, credentials prints a namespace-level login (user owner in the namespace named after the slug): sign in at the namespace level, not as root. Docs |
spky deploy | Build, upload, provision, migrate, deploy. On a Cloudflare project it bundles each Worker backend with esbuild and uploads it with the SSP Worker instead of building a Docker image (Node.js with npx required). With sync.queryAllowlist on, also publishes the committed allowlist JSON as _00_query_allowlist:<app>__<version> (the frontend’s package.json version) and keeps the previous version’s row; under enforce, a missing or stale JSON (sourceHash no longer matches the query module) fails the deploy, under warn it warns. Docs |
spky push (no subcommand) | Push schema to a Cloudflare project’s SSP node. VM plans apply schema during deploy. With a subcommand it is the Web Push tool. |
spky release <app> | Announce a new version to running clients without deploying. --mandatory, --cache-bust. Also publishes the app’s allowlist JSON under the announced version, so a regenerated query allowlist goes live without a restart. |
spky env | Encrypted variables and the vault: set, list, rm, pull, import, unlock, passphrase, share-ci, reset. set --file <path> reads a multi-line value (a PEM key) from disk, scoped with --dev or --prod. reset takes request, approve <email>, complete, list. Docs |
spky domain | Custom domains: add, list, remove. add --app <name> picks which app serves it (a frontend, an exposed backend, or a backend on a dedicated machine), add --status attaches it to the public uptime page. |
spky backup | Database backups: list, create, restore, delete, configure, reset. Docs |
spky db upgrade | Upgrade SurrealDB one step: to the newest 3.x, then from there to 4.x. --no-cutover stops before the cutover, --soak-minutes sets the watch window after a 4.x cutover (default 60), --confirm <project> goes ahead on a 4.x step without being asked, --no-wait returns at once. Subcommands: status, watch, cutover, cancel, finish, rollback. Docs |
spky host | A Pro project’s own VMs. status prints each VM’s kind, type, location and status, and any upgrade waiting on the project host. upgrade upgrades the project host now instead of in its quiet window; it asks first, because web, API and live sync are down for about two minutes (the database is not touched), and prints the reason when it is refused. Docs |
spky link | GitHub push-to-deploy: connect, status, settings, disconnect, trigger, runs. Docs |
spky token | API tokens for CI: create, list, revoke. |
spky team | Members and invitations: list, invite, invites, revoke, remove, rename. Docs |
spky billing | Billing: usage, plan, cap <dollars>. No subcommand opens the portal. usage in a pay-as-you-go project shows the month so far, priced, and its spend cap; cap sets that cap (base fee included, at least $10). plan switches between Starter and Pro on a VM project; a pay-as-you-go project has one plan, and moving between Cloudflare and VM hosting needs a new project. See Plans & billing. |
spky notice [message] | Post a notice to the public uptime page. list and remove manage existing ones. |
Restarting containers
spky restart recreates containers in place. With no target it restarts the scheduler and its
SSPs, leaving backends, frontends and SurrealDB running.
| Input | What it does |
|---|---|
TARGET... | Roles (db or surrealdb, scheduler, ssp, frontend) or the name of a single app from sp00ky.yml. Several may be given at once. |
--all-backends | Restart every backend app. |
--upgrade | Move the scheduler and SSP to the newest published build before restarting. A role that sp00ky.yml pins to an exact version restarts on its pin instead, with a warning. |
--clean | Also wipe the scheduler’s persistent volume, its replica and write-ahead log. SurrealDB data is untouched. |
--surreal (--db) | Also restart SurrealDB. A process restart rather than a wipe, so data on the volume survives, but the whole deployment is briefly unavailable. Equivalent to adding db to the targets. |
-y, --yes | The global flag: confirms the prompt a SurrealDB target triggers. Without it, and without a terminal, the restart is refused. |
What --upgrade, --clean and --surreal do at the container level is spelled out in
Admin dashboard, which offers the same three actions from a
browser.
Web Push
spky push inspects and drives Web Push as root, against the
local dev database by default or the cloud deployment with --cloud (or any database with --url,
--namespace, --database, --username, --password), like spky schedules.
| Command | What it does |
|---|---|
spky push status [--json] | Whether a host has published its VAPID key (and its kid), whether push.apns / push.fcm have a stored credential and how many devices each reaches, the rules stored in _00_push_config, devices per user (disabled ones, and web ones subscribed under an older key), and direct messages by status with the most recent failures. |
spky push send --to <user> ... | Creates a _00_push_message for the given users and waits up to 40 s for the delivery result: how many devices accepted it, or why it failed. With no notification flags it sends a nudge. On --cloud it asks first (-y answers). |
spky push devices <user> [--json] | A user’s devices, newest first: label or user agent, where it is reached (a browser push service, APNs sandbox · <bundle id>, FCM · android · <package>), last success, failures, disabled reason, rule filter. Never the encryption keys; --json includes the full endpoint. |
spky push sync | Store push: from sp00ky.yml without a deploy (spky deploy and spky migrate do it anyway), and the native credentials it references: { vault: .. } resolves against the production vault with --cloud, the dev vault otherwise. |
send takes:
| Flag | What it does |
|---|---|
--to <user> | Recipient, as $auth.id reads (user:abc). Repeat it or comma-separate. Required. |
--title, --body | The visible notification. A notification needs a --title. |
--link <url> | Opened when the notification is clicked (notification.url). Not --url: that one picks the database. |
--icon, --tag | Notification icon, and a tag that replaces an earlier notification with the same tag on screen. |
--topic <key> | Collapse key: an undelivered push with the same topic is replaced at the push service. |
--at <when> | Send later: an RFC 3339 time (2026-10-01T09:00:00Z) or a delay (10m, 2h, 1d). |
--ttl <duration> | How long the push service keeps it for a device that is offline (30s, 1h, 1d). |
--urgency <u> | very-low, low, normal or high. |
--data <json> | A JSON object handed to the service worker as payload.data. |
--no-wait | Return once the message is queued. |
--json | Print the outcome as JSON (id, status, delivered, error). |
Migrating a deployment
spky migrate prod applies pending migrations plus the internal Sp00ky schema to your cloud
deployment, without deploying anything else. It resolves the SurrealDB URL and root password from
your login and the slug in sp00ky.yml, and reads the mode, namespace and database from
sp00ky.yml too, so there are no connection flags to pass.
A pay-as-you-go project’s own database is reached as root, like a VM project’s. A project on the
retired free plan has no root login: its data lives in a namespace of the shared free database.
There migrate prod, spky release and every --cloud command (migrate, query, jobs,
flag, schedules, workflows, push) sign in as the project’s namespace-level owner user, the one
spky project credentials prints, and work in the namespace and database the project was given
there. --namespace cannot reach outside it.
| Flag | What it does |
|---|---|
-y, --yes | The global flag: confirms applying to production. Without it, and without a terminal, the migration is refused rather than run unasked. |
--force-schema | Re-apply the internal schema and remote functions even when unchanged, bypassing the schema-hash skip. Also accepted by spky migrate apply. Use it for drift recovery. |
--migrations-dir <dir> | Override the migrations directory from sp00ky.yml. |
--endpoint <url>, --secret <s> | Override the SSP or scheduler endpoint and auth secret used for the internal schema. |
--config <path> | Point at a sp00ky.yml somewhere else. |
Raise SPKY_DB_HTTP_TIMEOUT_SECS when a large migration exceeds the 120 second per-request
deadline. Migrations
Flags worth memorising
spky doctor --json is the designed integration point for scripts and AI agents: a stable list of
checks, each with a severity and a fix command. See
AI coding agents.