Local and the server
spool dev is the whole engine on your machine: the server, an agent, the web
UI, and a database file beside your work. A workflow you write against it is
the same workflow the server runs, so replay, timers, approvals, events,
schedules, artifacts and secrets can all be built and debugged before any of
it leaves your laptop. This page says what each side does for you.
Everything you can try locally
Section titled “Everything you can try locally”spool dev --db ./spool.dbOne process, one file, a UI at http://localhost:18753. Add --bind 0.0.0.0
to reach it from the rest of your network - the admin token is then the only
thing in front of it. From there:
- Runs are durable. Steps record their values once, sleeps end the process
and wake it later, and a
kill -9mid-run picks up from the last checkpoint - the crash test is that model in one move. - Every authoring surface works: YAML pipelines, polyglot scripts, workflows in your language, and graphs you draw on the canvas and save.
- Runs can wait on the world - signals, approvals,
wait_for_event:,cancel_on:, child workflows, and durable state cells shared across runs. - Schedules tick, concurrency groups queue, and event handlers fire on the project bus.
- Debugging is the same: load a finished run on the canvas and each node shows its In / Out / Logs, with run from here to re-enter the graph at one node. The TUI shows the same three faces.
- Artifacts keep their index in the database and their blobs in a directory, logs and the timeline persist, and annotations behave as they do anywhere.
- Secrets and credentials are encrypted with a master key written next to the database, and resolve by name the way a project on the server resolves them.
You can take a workflow from first line to survived-a-crash without deciding where it will eventually live.
Two things need setting up rather than being missing. Connecting an account through a provider’s consent screen wants an OAuth app you register yourself, pointed at your local callback (see configuring providers). Pipelines driven by a repo want a URL the sender can reach, so a webhook from GitHub means exposing your machine or running a server somewhere.
What local keeps, and what it forgets
Section titled “What local keeps, and what it forgets”State goes into an embedded database file - the one you name with --db, or a
default under your state directory. Runs, steps, schedules, artifacts,
secrets and event subscriptions all live there and survive a restart. The
server prints which stores sit on which tier as it boots, so you can see it
rather than guess.
A few things stay in memory only:
-
Pending dispatches. Runs themselves come back on restart, re-driven from their recorded history, so this reads as work being handed out again rather than work going missing.
-
The bus event history. Handlers and waiters survive; the record of what was emitted does not.
-
Users, orgs, projects and their tokens.
spool devre-creates itsdevorg anddefaultproject on every start, which is why your data is still there. Extra projects, members or personal tokens you make locally are gone on the next start.Agent tokens are not among them. A bearer you issue with
spool agents issueis kept in the database and works after a restart, because the runner holding it is a separate process somewhere else - losing it would mean going round the fleet re-tokening every agent, where losing a login costs you a login. Revoking one sticks, too.Two things follow from orgs still being in memory. A token scoped to an org outlives that org, so recreating one by the same slug later hands an old scoped token its runs - revoke the tokens you are done with rather than leaving them to be inherited. And a token pinned through
HYPERSPOOL_AGENT_TOKEN_*_PINNEDis re-imported from the environment on every boot, so revoking one lasts until the next restart; unset the variable as well.The admin token it prints is the exception: it’s kept in
<db>.tokenbeside your database (readable only by you) and handed back on every later start, so a restart leaves your web UI session, an exportedHYPERSPOOL_TOKENand any agent you ran by hand still working. Delete that file to roll it. A--db :memory:instance has nowhere to keep one, so it mints a fresh token each time like everything else it holds.
Old runs are never aged out either, so a local database holds every run you have ever done until you delete the file.
What the server adds
Section titled “What the server adds”- People: sign in with your identity provider, hold several orgs and projects, invite members with roles, and issue personal access tokens that still work tomorrow.
- Machines that aren’t yours. An agent fleet picks up work while your laptop is shut, across whatever mix of containers and VMs the jobs need.
- Runs measured in weeks. A 30-day sleep, or an approval that waits for someone to come back from leave, needs something that stays up for it.
- Deploys that don’t drop runs. The control plane runs as several replicas, each run is owned by exactly one of them, and following a run’s logs works from any of them.
- Repos: register the app once and pushes and pull requests get pipelines, with branch routing and fork pull requests held to the untrusted rules.
- Providers already set up, so connecting an account is the consent screen and nothing else.
- Storage that scales: logs and artifact blobs go to object storage, and retention policies age out old runs and events instead of growing forever.
- Usage: what each project consumed, recorded per org.
The one thing the server asks of you
Section titled “The one thing the server asks of you”Locally the engine and an agent are the same process, so every node kind
works with nothing to set up. spool dev and spool run embed an agent
advertising shell, polyglot and connector.
The server separates them: the control plane is ours, the machines that run your jobs are yours. Nothing executes until an agent that advertises what a job asks for is running.
| What you author | Runs on | Capability |
|---|---|---|
A steps: job, or a shell node |
an agent | shell |
A script: job, or a code node |
an agent | polyglot |
An http node or a connector action |
an agent | connector |
A matrix: job, or a fan_out node |
an agent, once per item | whatever its body needs |
wait, approval, switch, transform, state, emit, event |
the engine | none |
A workflow: job or node |
the engine starts the child | the child’s own nodes decide |
So a graph of connector and transform nodes needs one connector-capable agent, and a build pipeline needs a machine with your toolchain on it.
A run with nowhere to go says which of the two it is. With no agent
running at all it reports waiting_for_agent, because an empty fleet
reads the same as a busy one. With agents running but none advertising
what the job asks for, it reports unroutable and names the missing
capabilities, since more waiting will not help. Following the run
prints either, and spool agents ls shows what the fleet advertises.
Deploying agents covers minting tokens and running one as a service.
Moving between them
Section titled “Moving between them”The workflow files don’t change. spool login points the CLI at a server and
resolves your org and project; --project and spool where handle the rest.
Local runs stay local: nothing is uploaded from spool dev, and a run id
carries the org and project it belongs to, so the two never get mixed up.