Tour: the durable shapes
Thirty minutes with a local instance, one shape at a time: durable steps, timers, human gates, signals, events, and child workflows - plus the part that makes them worth having, which is killing the engine mid-run and watching nothing break.
You need the cryo binary
(curl -fsSL https://cryosleep.io/install.sh | sh) and nothing
else. Everything below runs locally.
0. A local instance
Section titled “0. A local instance”cryo devOne process: server, an embedded agent, and the web UI at
http://localhost:18753. Leave it running in this terminal; the
rest of the tour happens in a second one. (cryo run <file> is the
even shorter path - a one-shot instance for a single run - but the
tour wants one instance across several runs.)
1. Steps are checkpoints
Section titled “1. Steps are checkpoints”Save and submit this:
jobs: work: steps: - name: fetch run: echo "fetched 1200 records" - name: transform run: | echo "transforming..." sleep 5 echo "done"cryo submit steps.yaml --followWhile transform’s five seconds tick, kill cryo dev with Ctrl-C,
then start it again. The server resumes the in-flight run
(resumed 1 in-flight run(s) in the startup log) and the work job
runs again from its first step - a shell job is ONE durable
boundary, so its steps re-run together. That’s deliberate honesty at
this point in the tour: per-step checkpoints that survive a restart
are the polyglot primitive (cryo step), and section 5 shows the
same kill leaving completed steps untouched.
2. Timers park, they don’t wait
Section titled “2. Timers park, they don’t wait”jobs: deploy: steps: - name: ship run: echo "deployed v2" soak: depends_on: [deploy] wait: duration: 2m verify: depends_on: [soak] steps: - name: check run: echo "metrics clean, keeping v2"Submit it and look at the runs page: during the two minutes, the run
is parked. No agent is held, no process is sleeping on your behalf -
the run is rows in a database until the timer fires. Change 2m to
30d and nothing about the mechanics changes; restart cryo dev
during the wait and the wake-up still happens on time.
3. Approvals: a human in the DAG
Section titled “3. Approvals: a human in the DAG”jobs: build: steps: - name: build run: echo "built v2" review: depends_on: [build] outputs: env: env approval: prompt: "Ship v2 where?" fields: - { name: env, type: select, options: [staging, production] } ship: depends_on: [review] steps: - name: ship run: echo "shipping to ${{ steps.review.output.env }}"The run parks at review - same mechanics as the timer, so it can
sit for a week without cost. Open the run in the web UI and the
approval form is inline; submit it and ship templates against what
the human chose.
From a terminal it’s the same form, asked one field at a time:
cryo approve <run-id>The gate name is optional because the run is parked on exactly one
thing. cryo status <run-id> shows what that is - an awaiting_signal
substate named after the approval job.
cryo follow <run-id> gets you the same form without leaving the
terminal you’re watching the run in: select the parked wait, press a.
4. Signals and events: the outside world
Section titled “4. Signals and events: the outside world”A wait: {signal: ...} job parks until something delivers a named
signal:
cryo signal <run-id> go --payload '{"reason":"maintenance window open"}'Signals target one run by id. Events are the broadcast version - any
run (or cryo emit from your terminal) can publish on the project
bus, and runs subscribe by type:
cancel_on: events: [pr.closed]jobs: deploy: steps: - name: up run: echo "preview environment up" watch: depends_on: [deploy] wait_for_event: event: deploy.approved promote: depends_on: [watch] steps: - name: promote run: echo "promoted"Submit it, then from your terminal:
cryo emit deploy.approved# or end the run early instead:cryo emit pr.closedwait_for_event resumes the run when the event lands; cancel_on
tears the whole run down. Both are registered at submit, so an event
arriving before the waiting job starts still counts.
When a run is started for something - a push to a repo, a specific PR -
it carries that as its subject, and
these reactions correlate to it: a preview started for acme/web’s PR
cancels on that PR’s pr.closed, not another’s. The unkeyed
cryo emit pr.closed above is a broadcast, so it still ends any run
watching for one - handy from a terminal.
5. Polyglot: the same shapes in a program
Section titled “5. Polyglot: the same shapes in a program”Everything above was YAML. The same primitives are available inside a normal program, where checkpoints can be as fine as you like:
before="$(cryo step baseline -- date +%s)"cryo sleep 30safter="$(cryo step recheck -- date +%s)"echo "healthy at $before, still healthy $(( after - before ))s later"(In a real canary those are curl -fsS calls against your service
returning a metric; a non-zero exit fails the step, and the run.)
cryo submit canary.sh --followKill cryo dev during the cryo sleep. On restart the script re-runs
from the top and baseline returns its recorded timestamp rather than
calling date again, so the subtraction still measures the gap the
canary actually soaked for - not the gap since the restart. Steps
capture values; everything outside them is recomputed on every pass.
That re-run-from-the-top model has rules to understand before you write something real - read the authoring model, and see workflows in your language for the same thing in Python, TypeScript, Go, and Rust.
6. Workflows spawning workflows
Section titled “6. Workflows spawning workflows”A pipeline can embed a polyglot child as a job, and the child gets its own run, its own checkpoint timeline, and its own page in the UI - while its structured output flows back into the parent’s DAG:
jobs: canary: outputs: verdict: verdict workflow: type: cryosleep/script/v1 script: | cryo step probe -- echo "canary healthy" cryo sleep 10s cryo output set verdict=ok ship: depends_on: [canary] steps: - name: ship run: echo "canary said ${{ steps.canary.output.verdict }} - shipping"The other direction works too: a script calls cryo spawn to start
any workflow as a child run. See composition for
where each is the right tool.
Where to go next
Section titled “Where to go next”- Concepts - the vocabulary, in nine terms.
- YAML reference - every field of every job
kind, plus
concurrency:andcancel_on:. - CI from a repo - webhook in, statuses back: the CI layer on the same substrate.
- Deploying agents - put runners where your workloads are.