pihme / hermetarium

Hermetarium

The world an agent wakes up in: an OCI image it may use freely as root, behind a wall with Squid as the only, logged way out.

Get startedGitHubReadme

CI · releases tagged hermetarium/vX.Y.Z and porter/vX.Y.Z

#What it is

An agent that can run a shell and install packages should not run on the operator’s machine. Hermetarium is the world the agent wakes up in: an inhabitant boots inside an OCI image and may use its Linux freely (shell, package managers, rewriting files), as root. It is not a per-command sandbox and not a tool the agent calls.

The wall is outside the image. A Go supervisor boots the image behind either a weak (Docker/runc) or strong (Firecracker microVM) wall, starts Squid as the only network path with an operator-supplied ACL, and turns Squid’s access log into an I/O log. If that path cannot be applied, the habitat does not start (fail closed). API keys never live in the image; Squid injects them from the host-side ACL.

#Current status

Last updated 26 September 2026 · main@2c2bbf3

The full test suite passes on the main branch in continuous integration, on a machine with Docker and hardware virtualization. It shows the core promise working on both walls, the lighter Docker container and the stronger Firecracker virtual machine with its own kernel. Traffic the operator has not allowed is blocked, allowed traffic passes through the Squid proxy and is recorded in the log, and the operator can reach the program inside. A small example agent runs on both walls with its API key added by the proxy, never stored in the image. The ready-made images for Claude Code, Grok Build and DeepSeek Harness build, and their command-line tools run with no keys inside. The latest releases are hermetarium/v0.4.0 and porter/v0.1.0.

Chat with a real AI model is not part of the regular tests: the ready-made images reject the test stand-in for the vendor, and tests with real keys run only when started by hand. The strong wall always gets one CPU, 512 MiB of memory and 1 GiB of disk, and needs an x86-64 host with hardware virtualization. Release binaries exist for 64-bit x86 Linux only. The log records metadata about each connection, not its contents. Chat is one request and one reply at a time, and DeepSeek Harness does not keep a conversation between turns. Four issues are open: the hello-world example uses a placeholder image, the usage guide mentions larger virtual-machine sizes that cannot be set, the supervisor pins an older Alpine helper image in its code, and the spec mentions a snapshot feature that does not exist.

#Key concepts

Supervisor

supervisor/: the only operator-facing command. Drives Docker, Firecracker and Squid as processes via os/exec, not their SDKs.

Two walls

--wall weak (default): Docker/runc, host kernel shared. --wall strong: Firecracker microVM with its own guest kernel; needs /dev/kvm and x86_64. Same image either way.

Squid, the logged path

A pulled Squid image runs as a sibling (not linked; GPLv2 stays in Squid). Default deny; allowlists and API-key header injection live in the --acl file.

I/O log

hermetarium logs <id> syncs Squid’s access.log into io.jsonl: time, direction, protocol, destination, port, method, allowed, bytes. Bodies are off.

Porter

porter/: tiny HTTP adapter inside inhabitant images. Listens on :8080; each POST is one CLI turn for claude, grok or dsh.

Inhabitants

inhabitants/: Dockerfile + squid.conf templates for Claude Code, Grok Build and DeepSeek Harness. Copy and adapt; the contract is root, TCP 8080, iproute2, vendor URL, dummy key. One possible inhabitant from outside the repo is Fregoli, whose own image serves HTTP on 8080 directly; it currently binds 127.0.0.1 only (fregoli#1).

#Getting started

Needs Docker and Go 1.24+ on PATH. The strong wall also needs /dev/kvm and x86_64 (Firecracker is fetched on first use and runs in a privileged helper container; no host sudo).

From source

git clone https://github.com/pihme/hermetarium.git
cd hermetarium
make build
./bin/hermetarium version

Or download hermetarium-linux-amd64 from the releases.

Hello world

make test
id=$(./bin/hermetarium create --wall weak --image myorg/box:1 --acl examples/echo/squid.conf)
./bin/hermetarium url "$id"
curl -sS -d 'hello' "$(./bin/hermetarium url "$id")"
./bin/hermetarium logs "$id"
./bin/hermetarium destroy "$id"

--image is any local or pullable OCI image that listens on TCP 8080 (myorg/box:1 and myorg/claude:dev below are placeholders for your own images); url is the host address that reaches it through Squid. --wall strong works the same.

An official CLI as inhabitant

sed 's/__VENDOR_KEY__/sk-ant-.../' inhabitants/claude-code/squid.conf > /tmp/claude.acl
id=$(./bin/hermetarium create --wall weak --image myorg/claude:dev --acl /tmp/claude.acl)
curl -sS -d 'Run id -u' "$(./bin/hermetarium url "$id")"

CLI

hermetarium create --wall weak|strong --image NAME --acl FILE
hermetarium url <id>
hermetarium exec <id> -- <cmd>     # weak wall only, not the product path
hermetarium logs <id>
hermetarium destroy <id>
hermetarium version

Tests: make test (mock vendor API, CI default) and make test-live (real vendor keys via HERMETARIUM_*_API_KEY, optional). Walls, Squid contract, logs and custom images: usage guide.

#Architecture & layout

Wall: runc container or Firecracker VMOperatorcurl · browserSupervisorhermetarium · os/execSquid (gate):18081 inbound · :3128 interceptInhabitantyour OCI image · listens :8080var/<id>/access.log→ io.jsonl via hermetarium logsVendor API (HTTPS)only if allowlisted in --aclcreateHTTP to urlstartsstartsreverse proxyegresswriteskey injected

The operator reaches the inhabitant only through Squid (hermetarium url prints a 127.0.0.1 port published from the gate). Box HTTP is intercepted to :3128; the --acl file is expected to deny by default and allowlists and key-injects vendor hosts. Squid’s access.log lives in the host instance directory.

PathContents
supervisor/Go supervisor; entry point supervisor/cmd/hermetarium
firecracker-helper/TAP + Firecracker scripts for the strong wall (embedded in the binary)
porter/HTTP → CLI-turn adapter copied into inhabitant images
inhabitants/Claude Code, Grok Build, DeepSeek Harness templates (Dockerfile + squid.conf)
examples/Test stand-ins: echo (HTTP echo) and agentd (tool loop against a mock API)
tests/Integration tests (hello-world, agentd, official-CLI smoke), harness, vendor mock and probe
docs/images/Diagrams used in the readme and on this site
var/<id>/Instance state under the data root (HERMETARIUM_ROOT, the checkout, or ~/.local/share/hermetarium)