Self-Healing¶
.dotfiles detects, diagnoses, and repairs configuration drift, missing tools, broken symlinks, and environmental damage — often without user intervention.
The Self-Healing Loop¶
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ dot doctor │ ──► │ detect │ ──► │ report │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ dot heal │ ──► │ repair │ ──► │ verify │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼ (on failure)
┌─────────────┐ ┌─────────────┐
│dot rollback │ ──► │ revert │
└─────────────┘ └─────────────┘
dot doctor — Detection¶
dot doctor runs ~40 health checks grouped into categories:
| Category | Checks |
|---|---|
| Paths | ~/.local/bin, ~/.cargo/bin, Mise shim order, Homebrew prefix |
| Tools | Required: git, chezmoi. Optional: mise, nix, age, sops, pandoc |
| Chezmoi | Source dir exists, data file valid, no uncommitted drift |
| Shell | Default shell matches profile, startup time <500ms |
| Security | SSH keys present, Age key present, gitleaks baseline |
| Portability | Git user.email set, LC_ALL sane, TERM recognized |
Exit codes:
| Code | Meaning |
|---|---|
| 0 | All checks passed |
| 1 | Warnings (non-blocking) |
| 2 | Critical failures (tool missing, config broken) |
Flags:
--score/-s— numeric health score (0-100)--heal/-H— auto-fix detected issues (equivalent todot doctor && dot heal)--json/-j— machine-readable output for CI/monitoring--verbose/-v— show every check (default shows only failures)
dot heal — Repair¶
dot heal addresses three common failure modes:
1. Missing Tools¶
Reinstalls tools listed in .chezmoidata.toml that aren't on PATH:
dot heal
# [heal] jq not found, installing via mise
# [heal] age not found, installing via homebrew
# [heal] ✓ 2 tools installed
Priority order: mise → homebrew (macOS) → apt/dnf (Linux) → nix → manual install script.
2. Chezmoi Drift¶
Runs chezmoi apply --force to reconcile local files back to the source state. Skipped files (excluded via .chezmoiignore) are left alone.
3. Broken Symlinks & Missing Files¶
Detects dangling symlinks (target doesn't exist) and re-applies chezmoi to recreate them. Missing critical files (e.g. ~/.zshrc) trigger a targeted re-render.
Flags¶
--dry-run/-n— show what would be fixed, don't change anything--force/-f— skip confirmation prompts--tool <name>— heal only a specific tool
Exit Codes¶
- 0 — nothing to heal or all fixes succeeded
- 1 — some repairs failed, manual intervention required
dot chaos — Self-Test¶
dot chaos intentionally corrupts the local installation to verify dot heal can recover it. This is destructive — run only in ephemeral environments (containers, VMs, fresh installs).
Corruption scenarios:
| Scenario | What it breaks |
|---|---|
symlink |
Delete 3 random managed symlinks |
config |
Rewrite ~/.gitconfig with garbage |
tool |
mv a critical binary out of PATH |
permission |
chmod 000 on a dotfile |
all |
Run all scenarios sequentially |
Typical workflow:
docker run --rm -it ubuntu bash
# inside container:
bash -c "$(curl -fsSL https://.../install.sh)"
dot doctor # baseline
dot chaos all # break things
dot heal # fix
dot doctor # verify
This is part of CI: every PR runs dot chaos in a Docker container and validates dot heal restores a healthy state.
dot rollback — Revert¶
Before every dot apply, chezmoi writes a snapshot to ~/.local/state/dotfiles/snapshots/YYYY-MM-DD-HHMMSS/. dot rollback restores the most recent snapshot:
dot rollback # revert to the most recent snapshot
dot rollback status # list available snapshots
dot rollback restore 3 # restore snapshot #3
dot rollback clean # delete snapshots older than 30 days
Snapshots include:
- Every file chezmoi would have overwritten
- The previous
.chezmoidata.tomlandchezmoi.toml - A pointer to the Git SHA at apply time
They do not include:
- Generated caches (
~/.cache/) - Tool binaries (Mise-managed)
- External state (databases, remote repos)
dot bundle — Offline Recovery¶
dot bundle creates a self-contained archive that can restore the workstation without network access:
dotfiles-bundle-v0.2.501-20260416.tar.zst
├── source/ # Full git clone at current HEAD
├── tools/ # Pre-built chezmoi binary + mise
├── secrets/ # Age-encrypted snapshot of ~/.config/age/
├── manual/ # Offline manual (HTML + PDF)
├── attestation.json # Signed state
├── install-offline.sh # Bootstrap script
└── SHA256SUMS # Integrity checksums
Usage:
dot bundle # create bundle in ~/Downloads/
dot bundle --to /path/to/usb.img # write to external storage
dot bundle restore bundle.tar.zst # restore from bundle
Recovery use case: you're stranded on a new machine with no internet. Copy the bundle over (USB, phone tether, Bluetooth). Run bash install-offline.sh. You have a working dotfiles environment in <60 seconds, no network required.
Observability¶
Health Score¶
dot score returns a 0-100 score combining:
| Dimension | Weight |
|---|---|
| Tool availability | 30 |
| Chezmoi drift | 20 |
| Security gates (sigs, secrets, Age key) | 20 |
| Performance (shell startup, cache health) | 15 |
| Compliance (policy hash match) | 15 |
Scores:
- 90-100 — healthy
- 70-89 — minor issues
- 50-69 — needs attention
- <50 — run
dot healimmediately
Metrics¶
dot metrics shows recent observations: shell startup time, last-apply duration, heal events, chaos self-tests, CVE counts from SBOM scan. Metrics are stored locally in ~/.local/state/dotfiles/metrics.jsonl (append-only, one line per event).
Design Principles¶
- Idempotent — running
dot applyordot healtwice has the same effect as once - Reversible — every mutation creates a rollback point
- Observable — failures produce actionable error messages with exit codes
- Offline-capable — core flows (detect, heal, rollback) work without network
- Minimally invasive — repairs are scoped to the smallest unit that fixes the problem