Skip to content

hopboxd configuration

hopboxd is the hopbox daemon: ssh box@host spawns a box (a Firecracker microVM or a Docker container) and bridges your session into it, and it serves the AI-control MCP plane. Run hopboxd --help for the authoritative flag list.

Config file

Instead of a wall of flags, point hopboxd at a YAML file:

sh
hopboxd --config /etc/hopbox/hopboxd.yaml

Each key is the flag name without the --; its value becomes that flag's default, and an explicit --flag on the command line still overrides it. Unknown keys are rejected (so typos surface). See deploy/hopboxd.example.yaml in the repo.

yaml
ssh-addr: ":22"
compute: microvm
idle-reap: 3h
verify-mode: github
github-client-id: "Ov23li…"
quota-boxes: 3
quota-boxes-verified: 20

Front door (SSH)

The SSH listener where the username is a box spec and the client key is the identity. See the username grammar.

FlagDefaultDescription
--ssh-addr:2222Front-door SSH listen address (username = box spec, key = identity).
--host-key./hopboxd-ssh-host-keyFront-door SSH host key path (auto-created on first run).
--admin-keys(empty = no admins)An authorized_keys file; those keys may open the fleet-wide admin console via ssh sudo@host.
--default-imagealpineImage used when the username names none. With microVM, set this to a catalog image (e.g. ubuntu-22.04).
--default-cpus2CPU cap (vCPU) per box. 0 = unlimited. A named flavor in the spec overrides this.
--default-mem-mb2048Memory cap (MB) per box. 0 = unlimited. A named flavor in the spec overrides this.

Accounts & verification

An account is an SSH key fingerprint. ssh cli@host acc shows it; acc register runs identity verification (when enabled) to unlock durable features. See accounts.

FlagDefaultDescription
--verify-modeoffVerification for acc register: off | github | manual.
--github-client-id(empty)GitHub OAuth app client id (device flow; no secret needed) for github mode.
--quota-boxes0Max concurrent boxes per key (0 = unlimited) — the per-key floor against key-spam.
--quota-boxes-verified0Max concurrent boxes per verified account (0 = unlimited).
--verify-addr(empty)Serve the verification callback endpoint here (host:port), behind a public TLS proxy.
--verify-base-url(empty)Public base URL the QR / webhook targets, e.g. https://box.hopbox.dev.
--verify-secret(host key)HMAC secret for manual-mode completion tokens.

Lifecycle

Boxes auto-suspend when idle (memory snapshot to disk; resume on reconnect) and are idle-reaped if never reconnected to. Browser-terminal boxes are the exception — ephemeral, reaped a short --grace after the socket closes. See lifecycle.

FlagDefaultDescription
--idle-timeout2mSuspend a box after this long empty and quiet — no session attached, no interactive shell open in the box, and load below the threshold (memory snapshot to disk; resumes on reconnect, and the HTTP proxy resumes it on request). A box can override it with box-guest idle.
--idle-reap3hReap an auto-suspended box left inactive this long (never reconnected). 0 = never. A durable box (box-guest durable on), a keep-alive pin, or box-guest auto-suspend off exempts a box.
--grace2mReconnect window for a run box before it is reaped after its owner detaches. 0 = reap immediately.

On a clean stop (SIGTERM) the daemon also drains: every running, persistent box is suspended, concurrently, so the next start resumes it, and any attached SSH session is warned first. These are internal constants, not flags — 4 boxes snapshotted at once, 60s timeout per box, 3s warning grace before suspending. See shutdown drain and the systemd unit.

Compute backend

FlagDefaultDescription
--computedockerCompute backend: docker | microvm.
--agent-bin(empty)Docker: host path of the Linux hopbox-agent binary side-loaded into each box. (microVM bakes the agent into the rootfs.)
--guest-bin(empty)Docker: host path of the Linux box-guest binary side-loaded into each box. (microVM bakes it in.)

microVM (Firecracker)

Active when --compute microvm. A box is a Firecracker microVM booted from a CoW clone of a catalog image. See microVM & the image catalog.

FlagDefaultDescription
--fc-bin/usr/local/bin/firecrackerFirecracker binary.
--fc-kernel/opt/hopbox-microvm/vmlinuxvmlinux guest kernel.
--fc-images-dir/opt/hopbox-microvm/imagesBase-image catalog dir; image <name><dir>/<name>.ext4. Built with build/microvm/build-rootfs.sh / build-deboot.sh.
--fc-rundir/var/lib/hopbox/microvmPer-VM working dir (CoW disks, sockets).
--fc-bridge(empty → hopbox-vmnet)Host bridge for the microVM fleet. Set with --fc-subnet to run a second fleet beside another daemon.
--fc-subnet(empty → 10.0.0)/24 base — first three octets. The bridge gateway is .1; boxes reach the host (agent hub + metadata) there.

Owner network

By default every box shares one open segment on the microVM bridge — any box can reach any other. --owner-network isolates boxes per owner and gives them DNS names. See Owner network.

FlagDefaultDescription
--owner-networkfalseIsolate boxes by owner on the microVM bridge (nftables): a box reaches only its own owner's boxes and the gateway; cross-owner box↔box is dropped. Also serves owner-scoped <name>.hbox names on the gateway resolver. Fail-closed — the daemon refuses to start if the nft ruleset can't be programmed (nft missing). microVM backend only.

Storage

Persistent per-box home volumes (microVM): each named box gets an ext4 image at /home/dev that survives the box. See persistent home.

FlagDefaultDescription
--homes-dir(empty = off)Directory for persistent home volumes (ext4), keyed by owner + box name. Empty = ephemeral rootfs only.
--homes-gc-ttl48hReclaim orphan home volumes (no live box row) older than this, on startup and hourly. 0 = off. Homes of live boxes are never touched.
--home-size-mb2048Size of each home volume (MB).

Workspace

The cross-box shared /wrk — a JuiceFS filesystem over S3-compatible storage, one volume per owner, isolated by Postgres schema. Off unless --workspace-pg-dsn is set. Stand up the backend with deploy/workspace-setup.sh (it prints these) and note it needs a FUSE guest kernel.

FlagDefaultDescription
--workspace-pg-dsn(empty = off)Admin Postgres DSN for workspace metadata (e.g. postgres://hopbox:PASS@127.0.0.1/hopbox_ws?sslmode=disable). Enables the feature.
--workspace-s3-endpoint(empty)Box-reachable object-store endpoint (e.g. http://10.0.0.1:9000).
--workspace-s3-buckethopbox-wrkObject-store bucket holding workspace data.
--workspace-s3-key / --workspace-s3-secret(empty)Object-store credentials (baked into each volume's metadata at format).
--workspace-secret(host key)Server secret deriving per-owner role passwords; empty = derived from the SSH host key.
--workspace-automountfalseAuto-mount /wrk in every (non-browser) box on startup. Off = boxes mount explicitly — recommended on a public front door, so visitors don't each accrue a volume.

Secrets

FlagDefaultDescription
--secrets-key(host key)Server secret that encrypts stored secrets at rest (AES-256-GCM); empty = derived from the SSH host key. Keep it stable — rotating it makes existing secrets undecryptable.

Agent hub & metadata

FlagDefaultDescription
--agent-listen:7777Address the in-box agent dials back on (reverse tunnel).
--advertise(empty)Address the in-box agent is told to dial. Empty = derived from the backend gateway (host.docker.internal for docker, the bridge .1 for microVM) + the --agent-listen port.
--meta-addr:8090Box metadata API listen address. Boxes reach it by source IP — this powers box-guest.
--db./hopboxd.dbBox database path (SQLite). Tracks box ownership, state, and durable-home mapping across restarts.

This path (plus the derived -accounts.db / -shares.db) and --host-key above are exactly what hopbox-backup snapshots and ships off-host — see Backups & restore for the daily timer, what's in an archive, and how to restore.

AI-control plane

Serve the MCP plane and the canvas surfaces. The installer defaults --mcp-addr to unix:/run/hopboxd-mcp.sock.

FlagDefaultDescription
--mcp-addr(empty = off)Serve the AI-control MCP plane here (unix:/path or host:port).
--surface-addr(empty = off)Serve AI-rendered canvas surfaces over HTTP here (host:port).
--surface-url(empty)Public base URL for surface links (default: http://<surface-addr>).

HTTP API

Serve the HTTP API — exec, list, and lifecycle over REST, authed by hbx_ keys minted with ssh cli@host apikeys. Plain HTTP; front it with your own TLS proxy (same posture as --verify-addr).

FlagDefaultDescription
--api-addr(empty = off)Serve the HTTP API here (host:port). GET /healthz is unauthenticated; everything else needs a key.
--preview-domain(empty = off)Serve box ports at <box>-<port>.<domain> as well as the /v1/boxes/{box}/proxy/{port}/ path form. On a subdomain the app owns the origin root, so root-absolute asset URLs work — see preview subdomains. Needs a wildcard DNS record and a certificate per host (Caddy on_demand + ask http://<api-addr>/v1/tls-ask).
--wrk-mount-root/var/lib/hopbox/wrk-httpHost directory where the daemon mounts each owner's workspace to serve /v1/wrk/*. Only used when --api-addr and --workspace-pg-dsn are both set.

Diagnostics

FlagDefaultDescription
--checkfalseValidate the config file + flags and exit (no listeners, no engine). config OK + exit 0, or the errors + a non-zero exit. Used by hopbox-host update to gate a deploy before touching the running daemon.
--healthfalseProbe the running daemon's front door (SSH banner) and exit 0 if healthy, non-zero otherwise — the cheap liveness poll hopbox-host polls after a restart. It answers "is the daemon alive?", not "can it do its job?" — that's --health-full below. Stays a bool (there's no --health=full): the installed hopbox-host isn't part of the release a deploy swaps, so it always invokes plain --health.
--health-fullfalseDEEPLY probe the running daemon: spawn a throwaway box through its own front door with a freshly generated identity key, run a command in it and match a nonce in the output, tear it down, and check the image catalog + workspace object store. Prints a per-check breakdown and exits 0/1 — hopbox-host update's deploy commit gate. See the deep probe below.
--health-timeout90sOverall budget for --health-full, so a wedged backend fails the gate instead of hanging the deploy. A hard stop fires ~20s past the budget if a check is wedged somewhere the deadline can't reach.

The deep probe (--health-full)

--health only proves the front door is answering — true of essentially any daemon that got as far as binding its port. --health-full proves the daemon can do its job, over the same surfaces a user gets, in order:

CheckProvesSkipped when
front-doorThe SSH banner answers (same probe as --health).never
image-catalogssh images@host lists a catalog and it contains --default-image.the compute backend isn't microvm — only the microVM backend advertises a catalog; box-spawn covers image resolution on docker
box-spawnSpawns a throwaway box named anon-hlth-<rand>, runs a command in it, and matches a nonce in the output — proving a command actually ran, not just that a channel opened. The anon- prefix means the box gets no persistent home volume.never — this is the check the probe exists for
workspace-s3--workspace-s3-endpoint answers (MinIO's /minio/health/live first, then a plain GET).--workspace-pg-dsn is empty (/wrk is off). Fails — does not skip — if /wrk is on but the endpoint is empty.
cleanupRemoves every box the probe's ephemeral identity owns. Runs on every path, including a failed spawn or an exhausted budget, so a probe run never leaks a box.never; a cleanup failure fails the probe

Skipped checks do not fail the probe. Run it by hand against a live daemon:

sh
hopboxd --config /etc/hopbox/hopboxd.yaml --health-full
hopboxd deep health probe (compute=docker, budget=1m30s)
  front-door     ok        1ms  SSH banner on 127.0.0.1:2299
  image-catalog  skip           the docker backend advertises no catalog; box-spawn covers image resolution
  box-spawn      ok     1.219s  spawned anon-hlth-8aa29d8e, ran a command in it
  workspace-s3   skip           /wrk is not configured
  cleanup        ok        6ms  removed anon-hlth-8aa29d8e
healthy: 3 checks passed, 2 skipped

A failed check fails the whole probe (exit 1):

  box-spawn      FAIL    208ms  box anon-hlth-46d7b75d: hopbox: box failed to start: compute: docker: create: ...
unhealthy: 1 of 3 checks failed, 2 skipped

hopbox-host update runs this once, right before it commits the deploy — see Updating. --health-full and --health-timeout are operational hooks like --check/--health: they aren't config file keys.

See also

Instant isolated compute — for humans and AIs