The layers
The cleanest mental model is four layers. Each has one job.
┌──────────────────────────────────────────────────────────────┐
│ Hermes Box │
│ Product policy, desired state, lifecycle, safety, recovery │
├──────────────────────────────────────────────────────────────┤
│ Lima │
│ VM creation, persistent disks, SSH transport, port tunnels │
├──────────────────────────────────────────────────────────────┤
│ Apple Virtualization.framework (VZ) │
│ Native Apple Silicon virtualization │
├──────────────────────────────────────────────────────────────┤
│ macOS + Apple Silicon │
└──────────────────────────────────────────────────────────────┘
| Layer | Responsibility | What it does not decide |
|---|---|---|
| Hermes Box | Configuration, lock verification, lifecycle, backups, updates, health, ownership | How macOS executes a virtual CPU |
| Lima | Linux VM and disk primitives, SSH connectivity, host/guest transport, port forwarding | Which Codex, Claude, Hermes, or Executor version is allowed |
| Apple VZ | Hardware-accelerated virtualization on Apple Silicon | Box lifecycle or application policy |
| Ubuntu guest | Runs the tools, services, tmux session, and guest transaction engine | Host ownership or backup retention policy |
Is Lima a replacement for smolvm?
Yes, narrowly: v2 replaces smolvm with Lima as the host-side Linux machine runtime. It is also a broader architectural reset. v2 does not import, adopt, upgrade, or restore v1 smolvm machines or snapshots.
| Concern | v1 / smolvm model | v2 / Lima model |
|---|---|---|
| VM runtime | smolvm | Lima 2.1.3 over Apple VZ |
| Operating system | Mutable machine/image lineage | Checksum-pinned Ubuntu 26.04 cloud image |
| Durability | Rootfs snapshots and packaging concepts | One explicit, independently persistent ext4 data disk |
| Platform upgrades | Preserve or migrate an existing image | Delete and recreate the disposable root with rebuild |
| Desired versions | Provisioning and machine state | Reviewed, input-only hermes-box.lock |
| SSH | Project-managed machine connectivity | Lima-managed SSH transport |
| Compatibility | Existing v1 boxes | No v1 compatibility or adoption by design |
Lima is attractive here because it exposes the boring primitives v2 needs: create/delete a VM, create/attach a persistent disk, connect over SSH, copy inputs, inspect state, and forward a loopback port. Hermes Box wraps those primitives in stricter ownership and recovery rules.
The complete system
Apple Silicon Mac
│
├── Repository-owned desired state
│ ├── hermes-box.yaml human choices: name, CPU, memory, disks, port
│ └── hermes-box.lock immutable URLs, versions, checksums, digests
│
├── hermes-box Go CLI
│ ├── config + lock validation
│ ├── exact resource ownership
│ ├── content-addressed artifact cache
│ ├── encrypted backups
│ ├── operation locks + host logs
│ └── macOS Keychain-backed age identity
│
├── Lima 2.1.3
│ ├── <name> VM disposable Ubuntu root
│ ├── <name>-data disk persistent ext4 state
│ ├── Lima-managed SSH
│ └── Executor loopback forwarding
│
└── Ubuntu 26.04 ARM64 guest
├── Disposable root disk
│ ├── systemd + sshd + tmux
│ ├── hermes-box-guest transaction helper
│ ├── /opt/hermes-box/tooling/{node,uv}
│ ├── /opt/hermes-box/releases/{claude,codex,hermes}
│ ├── hermes.service
│ ├── executor.service → Podman → pinned Executor image
│ └── /var/lib/hermes-box applied state + recovery journals
│
└── Persistent /data disk
├── home/agent/.claude
├── home/agent/.codex
├── home/agent/.hermes
├── home/agent/workspace
└── executor
The root disk is cattle; the data disk is the box. Reattach the same data disk and apply the same reviewed lock to a fresh Ubuntu root, and the same working environment returns without restoring a mutated operating system.
Inside the guest
- Claude Code and Codex are interactive commands with versioned releases on the disposable root and durable auth/session state under
/data/home/agent. - Hermes Agent runs as
agentunder systemd; its release is reconstructable, while its config, auth, sessions, memory, and skills are durable. - Executor runs as a digest-pinned ARM64 container under system Podman. Its database and secrets live in
/data/executor; Podman's image store is disposable. - Node and uv are independently pinned guest tooling with their own update and rollback paths.
- tmux supplies the persistent interactive workspace: green status bar, clickable windows, Ghostty metadata, true color, clipboard passthrough, and extended keys.
What happens during create
- Load intent. Read
hermes-box.yamland the reviewedhermes-box.lock. - Fail-fast preflight. Verify Apple Silicon, the exact Lima version, free loopback port, ownership, checksums, and every required artifact before creating resources.
- Prepare recovery identity. Create the box's age identity in macOS Keychain.
- Create durable storage. Ask Lima for the exact
<name>-datadisk. - Create a fresh root. Ask Lima to boot the checksum-verified Ubuntu 26.04 ARM64 image through Apple VZ and attach the data disk.
- Bootstrap the guest. Cloud-init creates
agent, mounts/data, installs the minimal OS surface, systemd units, tmux assets, and static guest helper. - Materialize exact software. The host verifies locked artifacts, uploads them, and invokes one guest transaction.
- Stage before activation. The guest installs and smoke-tests Node, uv, Claude, Codex, Hermes, and Executor without making an unverified candidate current.
- Activate and verify. Atomic symlinks select native releases; systemd starts Hermes and Executor; health, versions, port binding, and MCP surfaces are checked.
- Create the first backup. Success is not returned until an encrypted recovery bundle verifies.
- Hand off human authentication. The CLI prints the ordered Claude, Codex, Hermes, Executor, and backup-key steps.
hermes-box create
│
├─ verify everything
├─ Lima: disk create
├─ Lima: VM create + attach disk
├─ cloud-init: minimal guest bootstrap
├─ host: upload verified artifacts
├─ guest helper: stage → smoke → activate → health
├─ encrypted backup
└─ human login/setup handoff
Daily operation
hermes-box ssh
→ Hermes Box asks Lima for the managed SSH connection
→ login as agent in /workspace
→ automatically run tm
→ create or attach tmux session "main"
start is intentionally boring: it starts only the releases already applied to the root and reports drift. It never installs software. stop stops services, syncs the data disk, and stops the VM. status and doctor are read-only.
| Intent | Command | Behavior |
|---|---|---|
| Enter the workspace | hermes-box ssh | Attach the persistent main tmux session |
| Run automation | hermes-box exec -- … | Run without tmux and preserve argument boundaries |
| Inspect | hermes-box status | Health, setup, desired/applied/running versions, storage, ports, backup |
| Diagnose | hermes-box doctor | Bounded read-only host and guest checks |
| Use Executor | hermes-box open executor | Verify and open the loopback-only portal |
Application update vs platform rebuild
| Change | Command | What happens |
|---|---|---|
| Claude, Codex, Hermes, Executor, Node, uv | update COMPONENT | Stage, verify, snapshot scoped durable state, activate, health-check, retain one previous release |
| Undo one component | rollback COMPONENT | Restore its valid encrypted transaction snapshot and activate the previous release |
| Ubuntu, provisioner, Lima compatibility | rebuild | Verify recovery inputs, back up, replace the root VM, reattach /data, apply the lock, verify |
Version selection never happens inside the running box. An update begins as a normal Git change to hermes-box.lock, passes review and qualification, then an operator applies it. If the lock contains a platform change, the correct operation is a rebuild, not an in-place Ubuntu upgrade.
Application change: lock PR → merge → update codex
Platform change: lock PR → merge → rebuild
rebuild:
verify current + desired artifacts
→ encrypted backup
→ stop and delete disposable VM root
→ create fresh Ubuntu root through Lima
→ reattach existing <name>-data
→ apply reviewed lock
→ health checks
→ automatic prior-root reconstruction if desired state fails
State, backup, and recovery
| Location | Examples | Treatment |
|---|---|---|
| Disposable root | Ubuntu, systemd units, Podman image store, application binaries, applied metadata | Recreate from the lock |
/data | Workspace, agent auth/sessions, Hermes memory/config, Executor database/secrets | Persist across stop/start and rebuild |
| Host artifact cache | Verified immutable install inputs | Reconstructable optimization |
| Encrypted backup | /data, applied lock, exact reconstruction artifacts | Portable recovery unit |
| macOS Keychain | Age backup identity | Export separately for disaster recovery |
A backup deliberately includes credentials and private work, so it is encrypted by default. It also embeds the exact reconstruction artifacts, avoiding a future dependency on an old npm tarball, GitHub release, container registry entry, Ubuntu package, Python archive, or wheel still being available.
The security boundary
The VM protects the host. Claude, Codex, Hermes, Executor, and the passwordless-sudo agent user are not security-isolated from one another inside the guest.
v2 provides
- No host-directory mounts, SSH-agent forwarding, Docker socket, or host credential-store forwarding.
- Lima-managed SSH instead of a project-owned long-lived SSH key.
- Executor exposed only on host loopback.
- Checksum-, SRI-, or digest-verified software inputs.
- Encrypted backups and reviewed immutable pins.
- Exact VM/disk ownership; destructive operations never discover targets by broad prefix.
v2 does not claim
- Containment between tools inside the guest.
- Offline or firewall-backed strict networking.
- Protection from guest-root data exfiltration through allowed egress.
- An out-of-band root repair console; recovery replaces the root instead.
Lima itself supports host mounts and several forwarding/networking modes. Hermes Box deliberately uses a narrower subset and disables host mounts because the product's boundary is stricter than Lima's general-purpose defaults.
Complete command surface
| Command | Outcome |
|---|---|
create | Create, apply, verify, back up, and print the setup handoff |
start / stop | Start applied versions or cleanly stop and sync the box |
ssh / exec | Interactive tmux workspace or noninteractive remote execution |
status [--check] | Health, state, drift, setup, backup, and optional upstream discovery |
logs | Bounded Hermes, Executor, or recovery service logs |
open executor | Open the verified loopback-only Executor portal |
setup executor | Safely connect Executor's reviewed MCP surface to Hermes |
update / rollback | Transactional application mutation with one retained prior release |
backup / restore | Encrypted self-contained recovery and absent-destination restore |
rebuild | Replace the root while retaining the persistent data disk |
doctor | Read-only diagnostics and exact repair guidance |
key export | Export the backup decryption identity with mode 0600 |
destroy [--force] | Final-backup-gated removal of the exact owned VM and disk |
completion / version / help | Shell integration and reference information |
Current qualification status
The implementation is present, but normal create is intentionally unavailable until the generated candidate lock passes the native ARM64 artifact build, isolated Lima lifecycle matrix, immutable publication, exact-URL re-download, and final reviewed root-lock promotion.
The missing repository-root hermes-box.lock is therefore a safety state, not an installation bug. Lima is also an operator-installed, version-qualified prerequisite; Hermes Box verifies it but never upgrades it.
Lima 2.1.3 plus the qualified and promoted lock. Once both exist, the user-facing path is intentionally short: clone, hermes-box create, then hermes-box ssh.