Concepts
Fleet Architecture
A "fleet" is two or more workstations sharing a single .dotfiles source repository. The fleet model handles per-host customization, drift detection, and cross-host trust propagation.
Single Source, Many Hosts
All hosts clone the same repository. Platform and per-host differences are resolved at chezmoi apply time using three sources of data:
defaults/.chezmoidata.toml— defaults for all hostsdefaults/.chezmoidata/hardware.toml— the[hardware]defaults and the[machines.*]presets (macbook-t2,surface-pro,geekom-a9)~/.config/chezmoi/chezmoi.toml— per-host overrides, generated bychezmoi init
The machine key in chezmoi.toml selects which preset applies:
# ~/.config/chezmoi/chezmoi.toml on a MacBook
[data]
machine = "macbook-t2"
theme = "tahoe-dark"
A template starts from .hardware and swaps in the preset when machine names one. This is how defaults/dot_config/niri/config.kdl.tmpl does it:
scale
Hardware Presets
defaults/.chezmoidata/hardware.toml ships three presets: macbook-t2, surface-pro and geekom-a9. Each preset, like the [hardware] defaults, sets:
| Field | Purpose |
|---|---|
profile | laptop or desktop |
scale, hidpi | Display scale (1.0 by default, 2.0 on macbook-t2, 1.5 on surface-pro) and HiDPI |
touchscreen, touchpad | Input devices present |
touchpad_accel, touchpad_accel_profile | Touchpad acceleration |
max_refresh | Highest refresh rate; above 60 enables variable refresh in Niri |
gpu | integrated or discrete |
keyboard_layout | XKB layout, us by default |
output_name, output_mode | The Niri output and its mode |
Add a new preset by appending a [machines.<name>] table to hardware.toml: no code changes needed. Feature flags are not part of a preset; set them per host under [data.features].
Drift Detection
dot doctor drift show compares this machine against the source and reports four classes of drift:
| Drift Type | Meaning |
|---|---|
| Managed drift | A deployed file differs from the rendered source (the chezmoi status lines) |
| Untracked source | A file in the chezmoi source tree is not tracked by Git |
| Orphan deployed | A file that chezmoi used to manage is still deployed after its source was removed |
| Stale source | A source file is older than its deployed target: someone edited the deployed file, and the next apply would revert it |
It exits 0 when every section is clean, 1 when it finds drift and 2 when chezmoi or Git is missing.
Drift is normal during development. dot apply overwrites managed drift (dot doctor fix runs chezmoi apply --force for the same purpose); dot diff previews the change without applying.
Fleet Attestation
Each host writes its own attestation record. Point every host at one shared directory to collect them:
# writes /mnt/fleet/home/<hostname>/workstation-attestation.json
# plus a timestamped copy next to it
DOTFILES_FLEET_STORE and DOTFILES_FLEET_ID set the same values from the environment. There is no command that compares the records across hosts: review them side by side, or run the verifier on each (see Trust Model). The records are unsigned.
Fleet Deployment Workflow
Recommended rollout procedure for fleet-wide changes:
- Develop on one host — make the change, preview it with
dot diff, thendot apply - Open a PR — CI validates shellcheck, tests, security scanners
- Merge to main — signed commit required
- Per-host pull — on each fleet host:
dot config update(pull the source and apply the dotfiles), ordot fleet apply --cmd "dot config update"from one host over SSH - Verify alignment — on each host,
dot fleet status(version and drift) anddot attest show --fleet-store <dir>
For urgent rollback: every host can independently run dot doctor rollback rollback to restore its most recent backup of the core dotfiles (kept in ~/.local/share/dotfiles/backups/; see Self-Healing).
Cross-Host Trust
Each host has its own SSH ED25519 signing key. To establish mutual trust:
- On each host:
cat ~/.ssh/id_ed25519.pub— capture the public key - Add every host's public key to
~/.config/git/allowed_signers(the filegpg.ssh.allowedSignersFilepoints at) on every other host - Keep
allowed_signersin the source repository if you want it shared (it's public-key material, safe to share) - Any host can then verify commits signed on any other host
This creates a web-of-trust among your own fleet. No central CA, no external service — just SSH keys.
Machine-Scoped Secrets
Some secrets must decrypt only on specific hosts (e.g. a work VPN config that doesn't belong on a personal laptop). The SOPS approach:
# defaults/dot_config/vpn.sops.yaml
vpn_key: ENC[AES256_GCM,data:...]
sops:
age:
- recipient: age1... # work laptop only
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
...
Only the host with the matching Age private key can decrypt. Other hosts see the encrypted file but cannot read the content.
Fleet Operational Commands
| Command | Purpose |
|---|---|
dot fleet status | This node's ID, namespace, version, OS, shell, drift state and last apply (--json for machine output) |
dot fleet drift show | This node's drift, recorded in the drift history |
dot fleet apply | SSH to every host in ~/.config/dotfiles/fleet.toml and run a command there (--host, --cmd, --dry-run, --jobs, --verify-hosts) |
dot fleet push <user@host> | Stream chezmoi archive from this host into the remote $HOME over SSH (without a user@host, it behaves like dot fleet apply) |
dot fleet namespace list | The active namespace and the [namespaces.*] defined in .chezmoidata.toml |
dot fleet log show | Recent fleet events: the fleet_<event> records of ~/.local/state/dot/fleet/log.jsonl (log.v1) |
dot fleet policy show / dot fleet policy set <advisory|strict> | The RBAC enforcement mode in agent-profiles.json |
fleet.toml lists one [hosts.<name>] table per host with an ssh target and an optional profile. By default dot fleet apply runs dot config update && dot doctor on each host (pull the source, apply it, then check the host), four hosts at a time.
Limitations
dot fleet statusanddot fleet drift showreport on the host they run on; onlydot fleet applyanddot fleet pushreach other hosts, over SSH (see Roadmap)- Secret rotation requires re-encrypting for all recipients; there is no
dotcommand for it, so re-encrypt withsops updatekeysor by re-adding the file withchezmoi add --encrypt - There is no central policy store — policy is versioned in the Git repository itself