Services
ShippingThe long-running half of the system: Nix-declared, schema-validated, sandboxed components that hold state and capabilities — so workflows can stay stateless and disposable.
Workflows do the work and then disappear. Everything that must stay — state, credentials, listeners, project context — lives in a service: a long-running component the daemon supervises through a declared lifecycle. Thirty-three of them ship packaged today, from databases and proxies to per-project agent instances.
Declared, not deployed Shipping
A service is a Nix artifact under the same flake roots as everything else
(services/ next to skills/, agents/, harnesses/). Its
configuration is not a bag of strings: every service carries a settings
schema, and settings are type-checked against it before the service ever
starts. Images are built by nix2container without archives — layers come
straight from the store, so a service image is exactly its closure, bit
for bit, and rebuilds only when its inputs change.
Install a flake, and its services appear in the daemon’s inventory with their schemas, dependencies, and capabilities — deployment is discovery, not a runbook.
Three runtimes, one contract Shipping
Each service declares how much isolation its trust level demands:
- Rootless OCI container (youki) — the default. Zero ambient authority, no default network, the same sandbox stack workflow steps use.
- MicroVM (cloud-hypervisor) — a hardware boundary for services that handle other people’s code or data.
- Native process — for the rare component that must sit directly on the host, supervised but unconfined.
Store access is isolated in all three: Landlock confines a service to its own closure where the kernel supports it, with bind-mount isolation as the fallback — the mechanism degrades, the property doesn’t. Network egress goes through the same filtered proxy as workflow steps, so a compromised service leaks through the same choke point every other escape has to pass.
Per-project instances Shipping
The pattern that makes services feel personal: a service can run as one
instance per project, mounted into a single repository. Our maintainer
service is the reference case — each repo gets its own container with an
embedded agent, the project mounted under /workspace/, and agent state
on a FUSE filesystem where every write is captured into a per-instance
history. The agent can triage issues, run tests, and propose fixes for
that project, with that project’s context, and nothing else’s.
That per-project instance is also where a repo’s workflow recipes live — the maintainer service and the workflow library grew up together, as the development story tells.
The gateway is a service too Shipping
The LLM gateway — the component every agent step speaks through — is itself just a service: workspace-scoped credentials, per-run accounting, and the privacy filter on its routes. It is workflow- and service-aware — a request arrives as this step of this run, so routing, budgets, and credentials follow declared identity, and agents authenticate with short-lived scoped tokens while the provider keys never enter the sandbox. So is the egress proxy, and so are the databases under the daemon. There is no privileged layer that lives outside the rules; the platform’s own plumbing runs under the same declarations, schemas, and sandboxes as anything you install.
Services hold the state so workflows don’t have to. That division is what lets a workflow be killed, retried, memoized, or moved — the trust machinery certifies what they build, and the services keep everything the next run needs to pick up where the last one stopped.