Managing the dotfiles
Two repos, symlink-deployed across a fleet (2 Macs + Linux boxes):
~/git/dotfiles — public. Shell, editor, tool configs, bin/ scripts, .claude/ (CLAUDE.md,
commands, standalone skills). No secrets, ever.
~/git/dotfiles-private — private. ssh config, fnox config, bin/bw-unlock, work env. Still
only references to secrets, never values.
Each repo has a manifest (<repo-relative path> [macos|linux]) and a POSIX install.sh that
symlinks each entry into $HOME, backing up anything it replaces to ~/.dotfiles-backup/<ts>/.
Updating (the common case)
Files in $HOME are symlinks into the repo, so edit in place, then git commit. That's it —
the change is already live. You only run install.sh after adding a new manifest entry or on a
new machine. Commit + push, then git pull on the other hosts.
Adding a new config
- Move the file into the repo at its
$HOME-relative path (e.g. ~/.config/foo/bar →
.config/foo/bar).
- Add the path to
manifest — append macos or linux if it's OS-specific.
- Run
./install.sh (symlinks it; backs up the original).
- Commit.
$HOME-ize any absolute paths inside the file so it's portable.
OS-specific config
Prefer separate files chosen by the manifest tag (.config/zsh/macos.zsh vs linux.zsh,
.ssh/config.d/macos.conf) over in-file uname branches. The common file sources the OS file only
if present, so it's a no-op on the other OS. Guard tool integrations with command -v.
Secrets
Never commit a value. Tools get secrets lazily via fnox (docs/SECRETS.md): op (1Password,
work) + bw (Bitwarden, personal), with Bitwarden unlocked silently from 1Password via bw-unlock.
Headless machines (no 1Password app) use a service-account token in ~/.config/op/env (600,
untracked) — see SECRETS.md "Headless / unattended machines". When handling a secret value in the
shell, only ever pipe it to wc -c or shasum — never echo it, never 2>&1/-v/--full.
Onboarding a new machine
- Clone both repos into
~/git. (Linux/headless: register an ssh key with GitHub if needed.)
- Install the toolchain:
mise + fnox (mise use -g ubi:jdx/fnox), op, bw, jq, zsh.
./install.sh in each repo.
- Headless secrets: mint a per-box 1Password service account
(
op service-account create <host> --vault automation:read_items --raw), drop the token in
~/.config/op/env (route it only through a temp file / $(cat …), never echo), bw login --apikey, then verify the chain (fnox get <name> | wc -c).
chsh to zsh. First interactive zsh triggers the one-time z4h bootstrap.
magic-kingdom (Linux) and paradise-park (Mac, SSH-driven) are the worked exemplars.
Claude config (.claude/)
CLAUDE.md, commands/, and standalone skills under .claude/skills/ are versioned via
individual manifest entries and symlinked into ~/.claude/. This keeps host-specific
project-linked skills (a ~/.claude/skills/<x> that symlinks into a project repo) untouched. To
version a new standalone skill: drop it in .claude/skills/<name>/, add a manifest line, run
install.sh.
Auditing
Run dotfiles-audit before pushing and periodically (leak scan + deploy drift + perms). Full
process and the by-eye checks are in docs/AUDITING.md.
1---2name: managing-dotfiles3description: Use when adding, changing, deploying, onboarding, or auditing Jesse's dotfiles — the symlink-based config system in ~/git/dotfiles (public) and ~/git/dotfiles-private (private), deployed across his Mac + Linux fleet. Covers the update workflow, the manifest, OS-splitting, secrets, new-machine onboarding, and the audit process.4---56# Managing the dotfiles78Two repos, symlink-deployed across a fleet (2 Macs + Linux boxes):910- **`~/git/dotfiles`** — public. Shell, editor, tool configs, `bin/` scripts, `.claude/` (CLAUDE.md,11 commands, standalone skills). **No secrets, ever.**12- **`~/git/dotfiles-private`** — private. ssh config, `fnox` config, `bin/bw-unlock`, work env. Still13 **only references** to secrets, never values.1415Each repo has a `manifest` (`<repo-relative path> [macos|linux]`) and a POSIX `install.sh` that16symlinks each entry into `$HOME`, backing up anything it replaces to `~/.dotfiles-backup/<ts>/`.1718## Updating (the common case)1920Files in `$HOME` are symlinks into the repo, so **edit in place, then `git commit`.** That's it —21the change is already live. You only run `install.sh` after adding a **new** manifest entry or on a22new machine. Commit + push, then `git pull` on the other hosts.2324## Adding a new config25261. Move the file into the repo at its `$HOME`-relative path (e.g. `~/.config/foo/bar` →27 `.config/foo/bar`).282. Add the path to `manifest` — append ` macos` or ` linux` if it's OS-specific.293. Run `./install.sh` (symlinks it; backs up the original).304. Commit. `$HOME`-ize any absolute paths inside the file so it's portable.3132## OS-specific config3334Prefer **separate files** chosen by the manifest tag (`.config/zsh/macos.zsh` vs `linux.zsh`,35`.ssh/config.d/macos.conf`) over in-file `uname` branches. The common file sources the OS file only36if present, so it's a no-op on the other OS. Guard tool integrations with `command -v`.3738## Secrets3940Never commit a value. Tools get secrets lazily via `fnox` (`docs/SECRETS.md`): `op` (1Password,41work) + `bw` (Bitwarden, personal), with Bitwarden unlocked silently from 1Password via `bw-unlock`.42Headless machines (no 1Password app) use a **service-account token** in `~/.config/op/env` (600,43untracked) — see SECRETS.md "Headless / unattended machines". When handling a secret value in the44shell, only ever pipe it to `wc -c` or `shasum` — never echo it, never `2>&1`/`-v`/`--full`.4546## Onboarding a new machine47481. Clone both repos into `~/git`. (Linux/headless: register an ssh key with GitHub if needed.)492. Install the toolchain: `mise` + `fnox` (`mise use -g ubi:jdx/fnox`), `op`, `bw`, `jq`, `zsh`.503. `./install.sh` in each repo.514. Headless secrets: mint a per-box 1Password service account52 (`op service-account create <host> --vault automation:read_items --raw`), drop the token in53 `~/.config/op/env` (route it only through a temp file / `$(cat …)`, never echo), `bw login54 --apikey`, then verify the chain (`fnox get <name> | wc -c`).555. `chsh` to zsh. First interactive zsh triggers the one-time z4h bootstrap.5657`magic-kingdom` (Linux) and `paradise-park` (Mac, SSH-driven) are the worked exemplars.5859## Claude config (.claude/)6061`CLAUDE.md`, `commands/`, and **standalone** skills under `.claude/skills/` are versioned via62individual manifest entries and symlinked into `~/.claude/`. This keeps host-specific63**project-linked** skills (a `~/.claude/skills/<x>` that symlinks into a project repo) untouched. To64version a new standalone skill: drop it in `.claude/skills/<name>/`, add a manifest line, run65`install.sh`.6667## Auditing6869Run `dotfiles-audit` before pushing and periodically (leak scan + deploy drift + perms). Full70process and the by-eye checks are in `docs/AUDITING.md`.