The vision
curl -fsSL https://tx9.davis7.sh/install | bash
tx9 create # → "large-cat" + getting-started checklist
tx9 list
tx9 enter large-cat # (alias: ssh)
tx9 backup large-cat --path ~/Downloads # (alias: export)
tx9 import large-cat-20260704.tx9
tx9 upgrade # self-update CLI
tx9 upgrade large-cat # move box onto current image
No repo checkout, no ./docker/boxd, no compose plugin. The binary embeds all build assets (provision script, guest scripts, Dockerfile); the first create on a machine builds the box image locally (~10 min, cached after). The Docker bits live in the boxes; the CLI is just a clean, fast Go tool for managing them.
The 12 decisions
| Topic | Decision |
|---|---|
| Image source | Embedded assets, local build on first create. No registry (can be added later). |
| State home | ~/.tx9/ (token cache, locks, config). Docker labels are the source of truth; backups default to ~/Downloads. |
| Naming | Friendly generated names (large-cat) by default; tx9 create my_box for explicit. |
| Create UX | Ends with a printed numbered checklist: enter, auth claude/codex, hermes setup, dashboard URL. |
| Aliases | enter/ssh; backup/export. |
| Passwords | --password → TX9_PASSWORD → hidden prompt. --no-encrypt escape hatch. |
| Archive | .tx9 extension; tar.gz inside, GPG-compatible when encrypted; embeds box-name metadata; import fails on collision, --name overrides. |
| Scope | Local-only v1. Cross-machine = backup → transfer → import. |
| Docker interface | Engine API via the Go SDK — no compose dependency (it bit us: siva didn't have the plugin), owned progress/error UX. |
| Repo | This repo; Go code alongside provision/ and guest/, which it embeds at build time. Open-sourced when ready. |
| Distribution | GitHub Releases + tx9.davis7.sh/install redirect; hosted site later. |
| Versioning | tx9 upgrade self-updates; images tagged tx9-box:<cli-version>; list shows per-box drift; prune cleans old images. Bash prototypes deleted. |
Command surface
| Command | Behavior |
|---|---|
create [name] | Generate name, build image if missing, create network + volumes + agent/executor pair, mint token, wire MCP, doctor, print checklist |
list | All boxes from daemon labels: state, image-version drift vs CLI, dashboard URL |
enter <box> | Agent-container shell, tmux attach; starts if stopped (alias ssh) |
start / stop <box> | Both containers together; volumes persist |
backup <box> | --path (default ~/Downloads), password precedence, --no-encrypt; quiesce → archive → validate → verify → .tx9 (alias export) |
import <file.tx9> | Validate before creating anything; name from metadata, --name override, collision fails; arrives quiesced + gateway-disabled + fresh token |
open <box> | Authenticated dashboard URL (?_token=) |
doctor <box> | In-box hb doctor + host port probe |
upgrade [box] | No args: CLI self-update. With box: recreate containers on current image + readiness gate |
delete <box> | Containers + volumes + token, typed-name confirmation |
prune | Unused image versions + stale state |
Contracts carried from the verified prototype
The compose bash draft was dry-run- and QA-verified before deletion; these survive the port as fixed requirements:
- Topology: free-rein agent container (no published ports, volume = the box) + executor-only container (one 0.0.0.0 token-gated port for dashboard + MCP) + per-box private network. Cross-box DNS isolation verified.
- Token: 256-bit per box, injected as
BOXD_EXECUTOR_TOKENwhich outranks any restored on-disk env (real bug found in dry runs); wiring re-checks token and URL freshness. - Archive safety: member-by-member validation (traversal, links, cycles, duplicates, special files) before anything is created on import; stage-then-promote restores; quiesce + gateway-disabled arrival.
- Single-writer gateway: restores never auto-enable Hermes;
hb gateway-enable --confirm-single-writer …stays the only path. - Gotchas: disable IPv6 in containers (executor binds localhost → ::1 otherwise); boot-window retry before executor health checks.
Saved for later (explicitly out of v1)
- Remote/multi-host —
tx9 --host nexus list,tx9 move large-cat nexusas one command. Worth it in the future; v1 keeps the Docker interface behind a small Go interface so a remote daemon slots in cleanly. - Hosted docs site at tx9.davis7.sh beyond the install redirect.
- Registry-published images if local build time annoys.
- Healthchecks, token rotation, slim executor image, executor egress policy, executor-state backup (currently scratch by contract).
Where things stand
- Deleted
- ./box (smolvm), ./docker/boxd + compose.yaml, smolvm tests/fixtures, smolvm-era systemd units — commit
9a030e2 - Kept
- guest/ (hb, hb-workload, hermes-state), provision/, docker build assets — the box internals tx9 embeds
- Spec
docs/tx9-cli-design.mdin the repo (all decisions + Go implementation notes)- CI
make checkgreen on the surviving surface- Next session
- Scaffold the Go CLI: cli/ module, embed pipeline,
create+listfirst
This is a shape-finding experiment. The runtime architecture underneath (agent/executor split) is verified and stable; the CLI surface above is the current best guess and expected to evolve with use.