System Architecture
DevCell is a single-host control plane for development machines. The host is trusted; repositories, build tools, and coding agents are not. A typed Go API turns client intent into durable state, then a backend establishes the runtime boundary and a guest agent manages the workspace.
The clearest mental model is nesting: each outer ring owns policy and lifecycle for everything inside it. The project’s devcontainer stays the innermost, familiar layer—it never becomes the trust boundary.
Nested runtime layers
Section titled “Nested runtime layers”Read the rings from the outside in. Isolation and authority live on the outer rings; developer tools and project code live in the center.
cell-agent, and exposes the typed GuestService over vsock.CellOS is not a developer distro and does not replace the project’s environment. It is the thin trusted supervisor that runs the project’s environment. The container backend collapses rings 02–03 into a Docker-managed, shared-kernel runtime for the current demo; the API, durable state, guest contract, and user workflow stay the same.
Product workflow DAG
Section titled “Product workflow DAG”Lifecycle is a directed graph, not a single happy path. Create and start are linear; once a cell is ready, work, exposure, and branching fan out. Checkpoint and fork produce a new cell identity without mutating the parent.
Every long-running edge above is an operation: durable steps and events in SQLite. Clients get IDs immediately, watch progress, and can reconnect after a daemon restart. Reconciliation compares desired cell records with backend reality instead of blindly replaying interrupted work.
System at a glance
Section titled “System at a glance”The same nesting drawn as trust lanes. Clients talk only to the host; the host talks to the guest; the guest runs the project.
Connect / protobuf over HTTPS
repository-owned code and dependencies
Technology map
Section titled “Technology map”| Layer | Technology | Responsibility |
|---|---|---|
| Client | Swift/SwiftUI and Go | Pair hosts, drive cells, render operations, open IDE sessions |
| Public API | protobuf + Connect | Versioned Host, Cell, Operation, Snapshot, Terminal, Port, Capability, Secret, and Guest services |
| Control plane | Go celld |
Authentication, lifecycle orchestration, policy, event recording, reconciliation |
| Durable state | SQLite | Cells, machines, operations, steps, events, clients, grants, ports, volumes, snapshots, lineage |
| Host image | NixOS + Disko + Nix flakes | Reproducible appliance, partition layout, services, Firecracker and devcontainer tooling |
| Machine backend | Firecracker/KVM or Docker | Establish and own the runtime boundary |
| Host↔guest transport | AF_VSOCK / Firecracker UDS; HTTP in container mode | Typed calls from celld to cell-agent |
| Guest supervisor | CellOS + Go cell-agent |
Workspace init, exec, services, ports, grants, snapshot hooks |
| Project runtime | Docker + Dev Container CLI | Build and run the repository-defined development environment |
| Private network | Tailscale + host HTTP proxy | Paired access to the appliance and workspace preview routes |
Create-cell request DAG
Section titled “Create-cell request DAG”A create request fans into durable state first, then into planning and machine provisioning. The client never waits on a single opaque RPC for the whole lifecycle.
celld persists the operation before expensive or privileged work begins. If the daemon restarts mid-flight, interrupted operations are marked failed rather than silently replayed.
The backend contract
Section titled “The backend contract”Every runtime implements one deliberately small Go interface:
type Backend interface { Create(context.Context, MachineSpec) (Machine, error) Start(context.Context, string) (Machine, error) Stop(context.Context, string) (Machine, error) Destroy(context.Context, string) (Machine, error) Inspect(context.Context, string) (Machine, error) Reconcile(context.Context, []Machine) error}Optional interfaces report capabilities, expose the guest endpoint, publish runtime descriptors, and checkpoint volumes. This keeps client semantics stable without pretending every backend has the same isolation or snapshot support.
| Backend | Boundary | Guest path | Snapshot behavior |
|---|---|---|---|
| Fake | In-memory simulation | None | Metadata and lineage only |
| Container | Docker process, network namespace, cgroup; shared host kernel | Loopback HTTP to cell-agent |
Quiesce + workspace copy |
| Firecracker | KVM microVM with dedicated guest kernel | vsock via Firecracker UDS | Metadata today; block and memory snapshots next |
Firecracker launch pipeline
Section titled “Firecracker launch pipeline”The machine path is more than spawning a binary. celld derives and owns every host artifact required to start and later clean up the cell.
On stop or destroy, the backend terminates the microVM and removes TAP devices, vsock sockets, and per-cell runtime state. On daemon restart, it reconstructs live machine records and cleans orphaned host resources.
Inside the cell
Section titled “Inside the cell”Cross-section of rings 03–05: CellOS supervises; Docker packages; the workspace does the work.
GuestService clientinit · exec · ports · grants · quiesceimage · Dockerfile · lifecycle commandsThe container demo runs the same cell-agent contract over HTTP and launches the resolved devcontainer directly on the appliance. The Firecracker host dial path is implemented; a freshly built CellOS rootfs containing the vsock-listening agent completes live guest exec in the microVM path.
Private networking and ports
Section titled “Private networking and ports”inside the cellcell-agent reports service/cells/:id/ports/5173Mac · browser · IDEThe cell requests exposure; the host owns publication. HTTP services receive stable host routes, while direct TCP endpoints use the appliance’s private Tailscale address. Credentials can follow the same pattern: the host stores and decrypts material, then supplies a scoped lease or brokered request rather than exposing an ambient credential directory.
State, checkpoints, and forks
Section titled “State, checkpoints, and forks”These layers are intentionally distinct. Container sleep is checkpoint plus stop, followed by a cold start. It does not preserve RAM. A fork gets a new cell identity and lineage record; on the container path its workspace is restored from the checkpoint copy. Firecracker block/diff and memory snapshots are the remaining capacity layer.
Where to read the code
Section titled “Where to read the code”| Concern | Source |
|---|---|
| API contract | api/proto/devcell/v1/devcell.proto |
| Control-plane orchestration | internal/celld/ |
| Durable state | internal/store/ |
| Backend contract | internal/hypervisor/hypervisor.go |
| Container runtime | internal/hypervisor/container/ |
| Firecracker runtime | internal/hypervisor/firecracker/ |
| Guest supervisor | internal/cellagent/ and cmd/cell-agent/ |
| Workspace resolution | internal/workspace/ |
| Appliance and CellOS images | nix/hosts/, nix/images/, and nix/modules/ |
| Swift client | macos/DevCell/ |
Continue with CellOS Contract for guest RPCs, Storage and Snapshots for checkpoint semantics, or Security Model for the trust rules.