This release is a pre-release and may not be stable for production use.
Satay Runtime
Satay is a local-first durable-execution runtime for async Python. You write
ordinary async def workflows and tasks; Satay records every durable call to an
append-only journal and, on crash, replays the workflow from the top — reusing
recorded results and re-executing only what never finished. No external
infrastructure: it runs in one process over SQLite.
import satay
@satay.task()
async def charge(amount: int) -> str: ...
@satay.workflow
async def checkout(order):
receipt = await charge(order["total"])
return receipt
The debugger — Satay Studio, a local web UI over an HTTP read API — ships in the
optional satay[studio] extra, so applications that embed Satay never carry the
FastAPI/uvicorn/JS stack into production.
Documentation: leejianrong.github.io/satay-runtime — the tutorial, the cookbook, the Studio guide, and the honest account of what the runtime does not do.
Status
The MVP is built and the full suite is green (V1–V8). Trust the code over the docs:
docs/ describes the intended full system, and where docs and code disagree the code
wins. uv run pytest -q prints the current test count.
What works today:
- Durable core + replay. Workflows replay from the top against an append-only
SQLite journal (
PRAGMA user_versionschema, forward-only migrations, WAL); recorded results are reused and only unfinished work re-executes. - Execution guarantees. Retries with capped exponential backoff and jittered delays
off an injected clock, at-least-once task execution, runtime-derived idempotency keys
readable inside a task body, replay nondeterminism detection (strict by default — a
divergent replay raises rather than returning a plausible wrong answer), and a separate
effect_safetypolicy that guards retryable side-effecting tasks. - Time and events. Durable
sleep,wait_for_event/send_eventover a persistent inbox, and a timer + event poll loop with FIFO delivery and event-wins-over-timeout. - Composition.
map,gather, andstart_child, each item a keyed durable call, so a crash mid-fan-out resumes with completed items reused and only unresolved items re-run. - Control plane. An HTTP control + read API (start, cancel, send event, fork; run list, timeline, tree, task detail, compare) with a redactor applied to every read and a loopback/token security guard.
- Studio. The Satay Studio SPA ships as a built bundle served by the same process.
- Forking and versioning. Fork a run from a prefix, compare two runs call-by-call, and a code-version stamp with a strict/warn/off mismatch policy on resume.
satay dev. One command brings up the lock, store, worker, and Studio server, and tears them down in reverse.- Payload spill. Encoded payloads over 256 KiB spill to content-addressed blob files transparently on write and rehydrate on read.
Deliberate MVP gaps, so the honesty survives contact:
- No blob GC. No run deletion and no compaction; blobs accumulate under
./.satay/and removal is manual (ADR-0004). A future GC has to be reference-aware, since forks share blobs with their source run. - Fan-out is fail-fast only. No collect /
return_exceptionsmode formap/gather/start_child; the first failure raises and sibling results are discarded (ADR-0020). satay runs showis frozen at the V1 event subset (ADR-0016). Timer, event, cancellation, and fork events render as bare type lines; Studio covers the rest.- Fork only accepts terminal runs (ADR-0004) — completed, failed, or cancelled.
- One process, one writer. No PostgreSQL backend, no multi-worker or distributed
execution. The cross-process data-dir lock is POSIX
flockand onlysatay devtakes it. - Async only. Sync (non-async) workflows and tasks are unsupported.
- Nondeterminism detection is runtime-only and compares the durable-call schedule, not arguments; there is no static analysis of workflow bodies, and no automatic migration of long-running workflows across code versions.
- Windows is best-effort and SQLite on network filesystems is unsupported (ADR-0019).
See docs/ for the specs and CLAUDE.md for the build brief.
Requirements
- Python 3.12 or 3.13
uvfor environment and dependency management- Linux/macOS first-class; Windows best-effort (SQLite on local disk only)
Dev quickstart
uv sync # create the venv, install deps + dev group
uv run ruff check . # lint
uv run ruff format --check . # format check
uv run mypy src # type-check (strict)
uv run pytest tests/unit -q # unit tests
The integration and e2e tests that cover Studio and the HTTP API need the studio extra;
without it they skip themselves and the reported count silently drops:
uv sync --extra studio # then run the full suite
uv run pytest -q
Or via the Makefile:
make dev # uv sync
make check # ruff + mypy
make test # unit tests
make ci # everything CI runs
Install the pre-push hook (runs the cheap gates before every push):
make install-hooks # or: ./scripts/install-hooks.sh
Layout
src/satay/ runtime core (pure Python, near-zero dependencies)
api/ author decorators, primitives, run handle, TaskContext
replay/ replay engine, identity, nondeterminism
journal/ event model + Store seam + codec
executor/ TaskExecutor seam + LocalTaskExecutor
timers/ timer + event poll loop
control/ HTTP control + read API (satay[studio])
versioning/ code-version stamper
blobs/ payload spill
devstack/ `satay dev` orchestrator (satay[studio])
testing/ fault injection, manual clock, seeded RNG, fixtures
cli/ core argparse CLI (`satay runs show`)
tests/{unit,integration,e2e}/
docs/ specs + ADRs
License
Apache-2.0. See LICENSE.
Release files for satay 0.1.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| satay-0.1.0a2.tar.gz | 123.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| satay-0.1.0a2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 270.3 kB
Release files / satay-0.1.0a2.tar.gz
| Download URL | satay-0.1.0a2.tar.gz |
|---|---|
| Size | 123.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4642ef77de143a0b6baffdc2f5c614cbe8d7b5b23fabb7dd97ef42b7bababaef
|
|
BLAKE2b-256 checksum How to use checksums |
9194a13584523b969589b207662c42292783578393c8a0b8dd72ac410d69533e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / satay-0.1.0a2-py3-none-any.whl
| Download URL | satay-0.1.0a2-py3-none-any.whl |
|---|---|
| Size | 147.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
336616525cdd69b28c394cdeb6c863ce089a7df21ef569f685078ea48b2c2424
|
|
BLAKE2b-256 checksum How to use checksums |
04df24bac2c082b644c87452ca69bcea337933a206802148458d8198a2626e5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency log