Files
fleet-dotfiles-template/docs/setup-new-fleet.md
T
Anthony Cardinale ebccdda936 Initial public release
A chezmoi-based fleet-dotfiles template for macOS workstations:

- Two-way auto-sync via launchd watcher + 5-min puller
- Mesh SSH via modify_authorized_keys driven by .chezmoidata/fleet.yaml
- age-encrypted secrets file
- Bundled Claude Code agentic team (11 agents) + /lite + /lite-sub commands
- Verify-before-claiming Stop hook
- Generic statusline + project-boundary validate-path hook
- Reference launchd plist for cross-fleet task-durations aggregation
  (companion repo: gitea.tojo.team/cardinale/task-durations)
- AGENTS.md walks an agent through the entire setup Q&A interactively
- docs/ covers architecture, security model, fleet onboarding
2026-05-02 17:26:32 -04:00

5.1 KiB

Setup — first machine of a new fleet

If you'd rather have an agent (Claude Code, Codex, etc.) walk you through this interactively, point the agent at this repo and tell it to follow AGENTS.md. The instructions below are the same flow, written for a human to follow directly.

Prerequisites

brew install chezmoi age duckdb

You need git access to whichever forge you'll host the fleet repo on (gitea, GitHub, forgejo, gitlab — anything chezmoi can clone over HTTPS or SSH).

1. Fork this template

Fork fleet-dotfiles-template to your forge:

  • Gitea: https://gitea.tojo.team/cardinale/fleet-dotfiles-template → click Fork.
  • GitHub: if mirrored there, fork it.
  • Other forges: clone, push to a new repo on your forge.

Name the fork however you want (my-fleet-dotfiles, <your-handle>-dotfiles, etc.). The rest of this doc assumes you've named it fleet-dotfiles on your forge.

2. Generate an age keypair (one for the whole fleet)

This keypair encrypts the secrets file. The private key never touches the forge. Every machine in the fleet gets a copy of it via secure side-channel.

mkdir -p ~/.config/chezmoi
age-keygen -o ~/.config/chezmoi/key.txt
chmod 600 ~/.config/chezmoi/key.txt

age-keygen prints the public key to stderr and writes it as the first commented line of key.txt prefixed with # public key: . Capture it — you'll paste it into the chezmoi config in the next step.

3. Initialize chezmoi from your fork

chezmoi init https://<forge>/<you>/fleet-dotfiles.git

This clones the fork to ~/.local/share/chezmoi/ and runs the template prompts. When it asks for the age recipient, paste the public key you captured.

If you missed the prompt, run chezmoi edit-config and replace REPLACE_ME_WITH_YOUR_AGE_PUBLIC_KEY in the rendered config.

4. Replace placeholders with your real config

The example files live in examples/. Move and edit them into the chezmoi source layout:

chezmoi cd

# Fleet roster
mv examples/fleet.yaml.example .chezmoidata/fleet.yaml
$EDITOR .chezmoidata/fleet.yaml
# (replace placeholder hostnames, users, pubkeys; for now you only need this machine's pubkey)

# Git identity
mv examples/gitconfig.example dot_gitconfig
$EDITOR dot_gitconfig
# (replace <YOUR_EMAIL> and <YOUR_NAME>)

# SSH config (templated)
mv examples/ssh-config.tmpl.example private_dot_ssh/config.tmpl
$EDITOR private_dot_ssh/config.tmpl
# (replace placeholder hostnames + usernames with your real fleet)

# Shell config (templated)
mv examples/zshrc.tmpl.example dot_zshrc.tmpl
$EDITOR dot_zshrc.tmpl
# (add your own PATH, aliases, completions; keep the secrets.env source line)

# Claude Code top-level instructions
mv examples/CLAUDE.md.example dot_claude/CLAUDE.md
$EDITOR dot_claude/CLAUDE.md
# (add personal sections if you want; the template only carries fleet-relevant ones)

5. Generate this machine's identity SSH key

If ~/.ssh/id_ed25519 doesn't exist:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""

Then add this machine's pubkey to .chezmoidata/fleet.yaml:

PUBKEY=$(cat ~/.ssh/id_ed25519.pub)
echo "Add this to .chezmoidata/fleet.yaml under your machine's entry:"
echo "  pubkey: \"$PUBKEY\""

6. Set up encrypted secrets

mkdir -p ~/.config/fleet-dotfiles
cp $(chezmoi source-path)/examples/secrets.env.example ~/.config/fleet-dotfiles/secrets.env
chmod 600 ~/.config/fleet-dotfiles/secrets.env
$EDITOR ~/.config/fleet-dotfiles/secrets.env
# (fill in real values for tokens you actually use; delete the rest)

# Encrypt + add to chezmoi source:
chezmoi add --encrypt ~/.config/fleet-dotfiles/secrets.env

Verify:

ls $(chezmoi source-path)/dot_config/private_fleet-dotfiles/
# Expect: encrypted_private_secrets.env.age

7. Apply

chezmoi diff       # review changes
chezmoi apply      # materialize them

Verify the launchd jobs registered:

launchctl list | grep -E "chezmoi|taskdurations"
# Expect 3 lines: claude-watcher, claude-puller, taskdurations.pull-fleet

If any are missing, run:

for plist in ~/Library/LaunchAgents/com.{chezmoi,taskdurations}.*.plist; do
  launchctl unload "$plist" 2>/dev/null
  launchctl load "$plist"
done

8. Install task-durations (optional)

If you want fleet-wide time estimates:

git clone https://gitea.tojo.team/cardinale/task-durations.git ~/.claude/scripts/task-durations
chmod +x ~/.claude/scripts/task-durations/*.{py,sh}
python3 ~/.claude/scripts/task-durations/extract.py

The Stop hook in your ~/.claude/settings.json will keep the local corpus fresh; the com.taskdurations.pull-fleet launchd job will mesh-rsync peer parquets every 5 minutes.

9. Push to your forge

chezmoi cd
git status        # confirm no unencrypted secrets are staged
git add -A
git commit -m "Initial fleet dotfiles for $(hostname -s)"
git push -u origin main

You're done. Open the forge URL and confirm the encrypted secrets file is present (encrypted_private_secrets.env.age, NOT secrets.env).

To onboard the next machine, see add-machine.md.