Commands
Everything is driven through ztd <verb>. ztd is a Go (Cobra) binary with its
runtime assets embedded, so an installed copy needs no source checkout — see
Install. The tables below spell it ./ztd,
which is the shim in a source checkout: it builds the binary and execs it, pointing
it at the live tree so script edits take effect without a rebuild. If you
installed ztd, drop the ./ — nothing else differs.
The CLI dispatches each verb down one of two lanes: most run Task inside the
toolbox container
(docker compose run --rm ztd task …), while check, setup, test, mount,
unmount, fetch, auth, skills, commands, run, attach, cattle, and
audit run on the host (they inspect or mutate host state, shell out to
virsh, do a host-side FUSE mount, drive the operator’s own git, or ssh into the
guest to sync credentials/skills, launch the agent, or export a run record). Prefix
any command with TARGET=proxmox to act on the remote backend instead of local
kvm.
The table below is the task-oriented overview; for the exhaustive per-command detail (every flag and usage line, auto-generated from the CLI), see the CLI reference.
| Command | Does |
|---|---|
./ztd | List the available commands |
./ztd version | Print the CLI version (also decides which toolbox image tag it pulls) |
./ztd check | Verify host requirements for your TARGET (host-side) |
./ztd setup | Prepare the host for the local backend — installs the persistent Docker→libvirt forwarding fix (host-side; --transient, --undo) |
./ztd test | Full clean-slate acceptance test — routes by TARGET: local (virsh + virtiofs/ownership/confinement), TARGET=proxmox, or TARGET=ec2 (each boots a real VM/instance and checks ephemeral-key SSH, cloud-init, Claude+auth, guest docker, mount/fetch/run/audit, clean teardown). Host-side. Its preflight also runs test:config. |
./ztd test:config | Fast, VM-free unit test of the .ztd/ztd.toml config loader (parsing + precedence). Runs in ~a second |
./ztd init | Scaffold this project’s .ztd/ztd.toml (every key commented out) plus a .ztd/.gitignore that keeps keys, secrets and state out of git. Idempotent — never clobbers an existing config. Terraform providers are downloaded automatically by up/plan; there is nothing to run once. |
./ztd validate | Check the Terraform config is valid (no VM) |
./ztd plan | Show what up would change |
./ztd up | Create / update the VM, print its IP |
./ztd status | Show whether a VM is running, on which target, and its IP. Add --watch for a live readiness dashboard — a red→green checklist (SSH reachable → cloud-init done → repo mounted → Docker → Node → Claude) that updates in place as the VM comes online (--timeout, default 10m). Since up returns on the DHCP lease while cloud-init keeps provisioning, this shows you when the box is actually ready. |
./ztd config | Print the resolved orchestration config — effective TARGET, NAME, skills/commands modes, and time/turn caps after env > .ztd/ztd.toml > default resolution |
./ztd ip | Print the current VM’s IP |
./ztd logs | Follow the guest provisioning (cloud-init) log |
./ztd auth | Sync your existing Claude Code login (~/.claude/.credentials.json) into the guest over the ephemeral key, so the in-VM agent is authenticated (host-side; both backends). Run after up. |
./ztd skills / ./ztd commands | Push Claude Code skills/commands into the guest per ZTD_SKILLS / ZTD_COMMANDS (none|user|project|both; default project). Add pull to bring VM-written ones back to .ztd/claude/<asset>-incoming/ for review (never auto-installed). Host-side; both backends. |
./ztd run "<task>" | Launch the agent (claude, subscription-billed) in a guest tmux session on a fresh ztd/<run-id> branch, and attach. --detach starts without attaching; task falls back to .ztd/task.md. Caps: ZTD_MAX_TIME (wall-clock, default 3600s) and ZTD_MAX_TURNS (agent iterations, default 0 = off) — whichever trips first ends the run. Never tears the VM down. |
./ztd attach | Reattach to a running ztd run session (Ctrl-b d to detach) |
./ztd cattle "<task>" | One-shot: up → (mount, remote) → auth → skills+commands → run --detach. Boots a VM and launches the agent autonomously in one command. Leaves fetch + down to you. Task falls back to .ztd/task.md. |
./ztd audit [run-id] | Export a run’s record (manifest, console, agent transcript, derived shell-command log) to .ztd/runs/<run-id>/ — run it before down. Defaults to the most recent run. Host-side; both backends. |
./ztd ssh | Open a shell in the VM (ephemeral key) |
./ztd tunnel -- <port> | Forward a VM port to host localhost |
TARGET=proxmox ./ztd mount | SSHFS-mount a remote guest’s work dir under .ztd/mnt/<name>, and on first mount sync your working tree up into it (host-side; no-op locally — virtiofs already live-shares the tree) |
TARGET=proxmox ./ztd unmount | Unmount that SSHFS view |
TARGET=proxmox ./ztd fetch | Add/refresh a ztd git remote on your local repo pointing at the remote guest’s checkout and git fetch its commits (host-side, over the ephemeral key; removed by down; no-op locally). The host then pushes to the forge. |
./ztd down | Destroy the VM |
./ztd reset | down then up from clean cloud-init |
./ztd list:orphans | List ZTD libvirt resources present in libvirt but not in Terraform state |
./ztd purge | Force-remove those orphans (e.g. a domain left by a failed up) |
Argument passing
Pass a port to tunnel positionally — the CLI supplies the Task -- separator
for you:
./ztd tunnel 8080 # forward guest:8080 → localhost:8080
./ztd tunnel -- 8080 # also works (explicit separator)Verbs that wrap a script with flags (e.g. run --detach, skills pull) forward
their arguments straight through to the underlying command.
Selecting a backend
./ztd up # local kvm (default)
TARGET=proxmox ./ztd up # proxmoxTARGET defaults to local. See Backends.
The ztd Go CLI
Phase 2 replaced the bash wrapper with a Go (Cobra) binary, rooted at the repo
so its runtime assets can be embedded in it. It offers every verb above over the
same Terraform/Task engine, with all the host-side orchestration (config
resolution, folder→name derivation, ephemeral-key lifecycle, secret sourcing,
up/down/reset) native to Go.
It ships as a standalone installable binary — a Homebrew cask on macOS, a tarball
on Linux (Install) — and carries the Terraform
root, the lima template, cloud-init and the toolbox compose file inside itself,
extracting them to a version-scoped cache on first use. That is why a project
directory needs nothing but its own .ztd/. The per-command detail in the
CLI reference is generated from the same Cobra
tree, so it cannot drift from the binary.