Architecture
Overalls is a monorepo of small services, each named after something or someone, with one NixOS deployment that wires them together. Marshall holds the record of what should exist. Lilly hosts make it physically true and report back. The other services sit at the edges: SSH, DNS, egress, inference and integrations.
Components
| Name | Role | What it gives you |
|---|---|---|
Marshallmarshall/ | Web app and control plane (Elixir, Phoenix, Ash) | Accounts with magic links and passkeys, Cedar authorization, machine lifecycle, scheduling, sharing, teams, the browser terminal and agent threads. |
Lillylilly/ | Host provisioner (Elixir) | Runs Firecracker guests on ZFS behind jailed, isolated networks, with a durable actor for each VM. Guests keep running when Lilly restarts. |
Porscheporsche/ | SSH management gateway | ssh <gateway> <command> manages machines with the same account and permissions as the web. |
Audiaudi/ | DNS and ingress | Authoritative DNS, guest SSH, and public HTTPS for each machine's web apps. |
Bradybrady/ | Egress proxy and cache | Guest downloads go through verified upstreams, and public anonymous responses are cached. |
Enricoenrico/ | Private GitHub mirror | Speeds up checkouts and powers the repository browser. |
Galileogalileo/ | Integration gateway | Named integrations at <name>.integration.internal; credentials are added at the edge. |
Ziggyziggy/ | Inference proxy | llm.internal for guests, with no provider keys inside the VM and usage recorded per account. |
Lyralyra/ | CLI and guest tools (Rust) | The overalls CLI: wildcard SSH, open for repos, agent prompts. It also runs the agent's tools inside the guest. |
Browserbrowser/ | Toolbar extension | Opens the repository page you're on in your Overalls deployment. |
Protocolprotocol/ | Shared wire schemas | Validates the messages services exchange, so their interfaces stay consistent. |
Fleet and Nixfleet/ nix/ | Declarative infrastructure | NixOS modules, host inventory, guest images, secrets, and the host install and enroll tools. |
Control plane and hosts
You can run one to a hundred Marshall instances against a shared PostgreSQL. Machine capacity is admitted atomically, so concurrent requests can't overbook a host. Every lifecycle change is a durable operation with an audit trail. Each host's Lilly dials out to Marshall with its own revocable credential, sends capacity reports and receives commands. A generation fence stops a delayed launch from starting a guest after a stop has already been requested.
┌──────────────────────────────────────────┐
browser ─HTTPS─▶│ HAProxy TLS, ACME, verified backends │
└────────────────────┬─────────────────────┘
▼
ssh ──────────▶ Porsche ───▶ ┌────────────────┐ ┌────────────┐
overalls CLI ──────────────▶ │ Marshall × 1…N │◀───▶│ PostgreSQL │
│ Phoenix · Ash │ └────────────┘
│ Cedar · Oban │
└───────▲────────┘
│ HTTPS, per-host credential
│ reports up · commands down
┌─────────────┴─────────────┐
┌──────┴──────┐ ┌──────┴──────┐
│ Lilly host │ · · · │ Lilly host │
│ ZFS · KVM │ │ ZFS · KVM │
└─────────────┘ └─────────────┘
Regions
We recommend running Marshall in a single region. Its instances share one PostgreSQL, and admission, sharing and lifecycle decisions all depend on that database, so keep them close to it. Run several instances in that region for high availability.
Lilly hosts have no such constraint. Each one only needs to reach Marshall over HTTPS, so you can put hosts in every region where your developers work.
Where the traffic goes depends on how you connect. SSH, VS Code over Remote-SSH, and the web apps a machine shares enter through Audi on the machine's own host and stay in that region. The browser terminal and browser editor are relayed through Marshall, over the connection the host dialled out, so they pay the round trip to the control-plane region.
┌─ control plane · one region ───────────────────────────────┐
│ │
│ HAProxy ─▶ Marshall · Marshall · Marshall ◀─▶ PostgreSQL │
│ several Marshall instances for high availability │
│ │
└──────────────────────────────┬─────────────────────────────┘
▲
│ HTTPS, dialled out by every host
│
┌────────────────────────────┼────────────────────────────┐
┌──────────┴──────────┐ ┌──────────┴──────────┐ ┌──────────┴──────────┐
│ WASHINGTON DC │ │ LONDON │ │ SINGAPORE │
│ Lilly Lilly Lilly │ │ Lilly Lilly │ │ Lilly Lilly │
│ guests on ZFS │ │ guests on ZFS │ │ guests on ZFS │
│ Audi: SSH, web apps │ │ Audi: SSH, web apps │ │ Audi: SSH, web apps │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
▲ ▲ ▲
developers in developers in developers in
the Americas Europe Asia-Pacific
┌─ control plane · one region ─────────────────────────────────────────┐
│ HAProxy ─▶ Marshall · Marshall · Marshall ◀─▶ PostgreSQL │
└───────▲──────────────────────────────────────────────────────▲───────┘
│ web terminal and relay over HTTPS, │
│ browser editor dialled out by Lilly │
│ │
┌────────────┴────────────┐ ┌──────────────┴─────────────┐
│ DEVELOPER IN SINGAPORE │ │ HOST IN SINGAPORE │
│ browser │ │ │
│ ssh, VS Code, │ direct, same region │ │
│ shared web apps ────────┼─────────────────────────▶│ Audi ─▶ Lilly ─▶ guest │
└─────────────────────────┘ └────────────────────────────┘
A machine's life
A machine is an identity first and a process second. Its UUID, name, private address, sharing and disk outlive any particular Firecracker process, so stopping, resizing and relocating it don't create a new machine. Cloning does: the copy is private, gets a fresh address and starts without the source's RAM.
new ─▶ [pending] ─▶ [running] ◀─── start ─── [stopped]
│ │ ▲ │
│ └─── stop ───────────┘ │
│ ├─ relocate ─▶ another host
resize ├─ clone ────▶ new machine
stop · apply · restart ├─ resize ───▶ at next boot
└─ rm ───────▶ disk deleted
From inside a guest
Guests reach the outside world only through services that can check, cache and record the traffic. Lateral traffic between guests and to private address ranges is blocked by Lilly's nftables policy. The locked-down customer profile removes outbound networking entirely.
┌─ guest ──────────────────────────┐
│ apt · nix · pip · npm ──────────┼──▶ Brady proxy, cache ──▶ internet
│ git clone ──────────────────────┼──▶ Enrico GitHub mirror
│ https://llm.internal ───────────┼──▶ Ziggy ──▶ OpenAI, Anthropic
│ https://x.integration.internal ─┼──▶ Galileo adds credentials
└──────────────────────────────────┘
secrets stay outside the guest; usage is recorded per account
Principles
- Declared, reviewed, then applied. Hosts, images, users and secret recipients live in Git. Hosts install from a reviewed plan and update from a pinned branch.
- Secrets never travel in the clear. SOPS and age documents are encrypted to each host's SSH-derived key and to named recovery users. Private keys are generated where they're used.
- Authority is checked at execution time. Cedar policies are evaluated when each action runs, not when a page was loaded, so revoked access takes effect on pages that are already open.
- Uncertainty is explicit. If a request might not have reached a host, the system asks for deliberate reconciliation rather than guessing.
- Text over pictures. The web interface uses the same monospace character grid as this site.
Decision records
Every design decision is written up as an architecture decision record (ADR), with a review recording the evidence. There are over 170 of them. Some good places to start:
- ADR-0018 · Persisted sharing and machine access
- ADR-0022 · Browser terminal and live login checks
- ADR-0030 · NixOS deployment and TLS
- ADR-0036 · Automatic host updates
- ADR-0043 · Declarative host provisioning
- ADR-0048 · Single-host service deployment
- ADR-0055 · Queued machine cloning
- ADR-0077 · Teams and shared resources
- ADR-0128 · Repository launch links
- ADR-0153 · Official-source browser editor
- ADR-0155 · Stopped-only relocation
- ADR-0161 · Monospace Web presentation