Configuration

.ztd/ztd.toml is the ONE file you edit. Every dependency’s config — Terraform variables, the lima instance YAML, cloud-init, the compose env — is generated inside the ztd binary from ztd.toml + built-in defaults. There is no terraform.tfvars, no lima/cloud-init/compose file you ever touch directly. Secrets live separately in .ztd/secrets/*.env (gitignored) — never in ztd.toml, which is meant to be committed.

Scaffold it with:

ztd init

This writes .ztd/ztd.toml with every key commented out — an all-defaults project boots as-is; uncomment only what you want to change.

Precedence, for every key, is explicit env > .ztd/ztd.toml > built-in default (e.g. TARGET=proxmox ./ztd up, ZTD_VCPUS=4 ./ztd up, ZTD_MAX_TURNS=3 ./ztd run … all win over the file).

[vm] — box shape + backend selection

KeyEnv overrideDefaultMeaning
backendTARGETlocallocal (native: vz on macOS, libvirt kvm on Linux) | proxmox | ec2
nameNAMEderived ztd-<folder-slug>-devVM/domain name — the sanitized basename of the dir you ran ztd from
vcpusZTD_VCPUS2Virtual CPUs
memory_mbZTD_MEMORY_MB4096RAM in MiB
disk_gbZTD_DISK_GB24Disk size in GiB
usernameZTD_GUEST_USERdevWork user created in the guest
archZTD_ARCHthis host’s archGuest CPU architecture (amd64 | arm64)
image_urlZTD_IMAGE_URL"" (derived from arch)Override the Debian 12 generic qcow2 URL
repo_dirnameZTD_REPO_DIRNAMEbasename of the dir you ran ztd fromNames the guest work dir /home/<user>/<dirname>
repo_targetZTD_REPO_TARGET"" (derived /home/<user>/<repo_dirname>)Full path of the work dir in the guest; set only to override

repo_source (the host tree shared into the guest) is always the dir you ran ztd from — it is not a ztd.toml key.

[agent] — agent run knobs

KeyEnv overrideDefaultMeaning
skillsZTD_SKILLSprojectnone | user | project | bothztd skills push mode
commandsZTD_COMMANDSprojectSame modes — ztd commands push mode
max_timeZTD_MAX_TIME3600Wall-clock cap (seconds) for ztd run
max_turnsZTD_MAX_TURNS0 (off)Agent-iteration cap via a PostToolBatch hook

[proxmox] — only read when backend = "proxmox"

Credentials (endpoint, api_token) are NOT here — see Secrets below.

KeyEnv overrideDefaultMeaning
nodeTF_VAR_proxmox_nodepveTarget PVE node
datastoreTF_VAR_proxmox_datastorelocal-lvmDatastore for the VM disk
image_datastoreTF_VAR_proxmox_image_datastorelocalDatastore holding the imported cloud image
snippet_datastoreTF_VAR_proxmox_snippet_datastorelocalDatastore for the cloud-init snippet
bridgeTF_VAR_proxmox_bridgevmbr0Network bridge
ssh_usernameTF_VAR_proxmox_ssh_usernamerootLinux user on the node for snippet upload over SSH (NOT the API user)
insecureTF_VAR_proxmox_insecurefalseSkip TLS verification (self-signed PVE cert)
vm_idTF_VAR_proxmox_vm_id0 (auto-picked)Force a specific VMID; 0 = ztd picks one (see the proxmox how-to)

[aws] — only read when backend = "ec2"

Credentials (AWS_*) are NOT here — see Secrets below.

KeyEnv overrideDefaultMeaning
regionTF_VAR_aws_regionus-west-2Region (also the provider region)
instance_typeTF_VAR_aws_instance_typet3.microInstance type
amiTF_VAR_aws_ami""Override AMI; empty = latest official Debian 12 for region+arch
subnet_idTF_VAR_aws_subnet_id""Empty = default VPC’s default subnet
security_group_idTF_VAR_aws_security_group_id""Empty = ztd creates one (tag-scoped, SSH-only) in the default VPC
ssh_ingress_cidrTF_VAR_aws_ssh_ingress_cidr0.0.0.0/0SSH ingress CIDR for the created SG — lock this to your <ip>/32

Secrets (.ztd/secrets/)

Gitignored, host-side only, never read from ztd.toml:

  • .ztd/secrets/proxmox.envTF_VAR_proxmox_endpoint, TF_VAR_proxmox_api_token. Copy examples/proxmox.env.example to start.
  • .ztd/secrets/aws.envAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, etc. Copy examples/aws.env.example to start.

State and other project directories under .ztd/

  • ztd.toml — the one config file (committed).
  • state/terraform.tfstate — Terraform state for the local/proxmox/ec2 backends (gitignored; local to this project checkout).
  • keys/ — the per-VM ephemeral SSH keypair (gitignored).
  • secrets/ — operator-supplied host-side credential files (gitignored).
  • claude/{skills,commands}/ — project skills/commands pushed to the guest (tracked); *-incoming/ staging dirs for pulled-back review (gitignored).
  • mnt/ — sshfs mountpoint for a remote guest’s repo (gitignored).
  • runs/<run_id>/ — audit records exported by ztd audit (manifest, console, transcript, command log; gitignored). ztd run writes the launch manifest here.