Architecture
Two principles shape ZTD: one interface across every backend, and all orchestration tooling in Docker.
One interface, many backends
ztd CLI (Go / Cobra) ← the single binary; assets embedded
│
├── host lane ─────────────────────────► vz (macOS)
│ (bash, on the host) lima / Virtualization.framework
│
└── container lane
│ Taskfile → Terraform root ← runs inside the toolbox container
│ selects ONE backend module by TARGET
┌─────────────┼───────────────────────┐
kvm-local proxmox ec2
(libvirt) (bpg/proxmox) (aws provider)
└─────────────┴───────────────────────┘
│ all four consume the SAME
cloud-init (Docker + Node + agent + creds)The CLI routes each verb down one of two lanes. Most dispatch into the toolbox
container, which runs Task and Terraform; the host lane runs bash directly on the
host for the verbs that inspect or mutate host state (check, setup, the agent
workflow) and for the whole vz backend, which has no usable Terraform provider
and drives Apple’s hypervisor from the host.
Terraform’s provider swap is hidden inside per-backend modules, selected by a
single target variable (count = var.target == … ? 1 : 0). You never juggle
two tools or two configs — the same ./ztd up works everywhere, and the same
cloud-init provisions every guest. This was a deliberate choice over, say,
Vagrant-for-local plus Terraform-for-remote.
Tooling in Docker
Everything except the hypervisor runs in a toolbox container (Terraform,
providers, Task — and eventually the ztd CLI image). The host stays clean:
| Backend | Host needs |
|---|---|
kvm (local Linux) | Docker + libvirt/qemu |
vz (local macOS) | Docker + Virtualization.framework |
proxmox / ec2 (remote) | Docker only |
The toolbox reaches the local hypervisor over the libvirt socket using host
networking. A fully self-contained variant — libvirtd + qemu inside a
privileged container, using only the host’s /dev/kvm — is possible later; v1
keeps the hypervisor host-side for reliability.
Dual Docker
There are two independent Docker daemons, and that’s the point:
HOST ── Docker (toolbox) + libvirt/qemu (hypervisor)
└─ VM ── its OWN dockerd (the agent's sandboxed dev systems)The guest’s daemon is where the agent builds and runs things. Because the host socket is never shared in, the two never touch — so a compromised guest can’t reach host containers. See The zero-trust model.