Where things live¶
dotty splits state across three places with three different privacy levels: your
dotfiles repository (shareable), public config under $XDG_CONFIG_HOME
(safe to commit, safe to show), and private data under $XDG_DATA_HOME
(secrets and personal state, never in a repo). Knowing which is which is most of
the mental model.
The repository¶
dotty init scaffolds (or adopts) a dotfiles repository — by default
<repos-dir>/dotfiles, recognised later by its .dotty-version marker:
dotfiles/
├── .dotty-version # marks this repo as dotty-managed
├── Brewfile # composed from brewfile.d for the profile
├── brewfile.d/ # per-component Brewfile fragments
├── profiles/ # one directory per profile
│ ├── personal/
│ │ ├── profile.json # metadata + the profile's stored init answers
│ │ ├── Brewfile # the profile's package set
│ │ ├── env.zsh # profile-varying files (also git.gitconfig,
│ │ │ # worktrees.gitconfig)
│ │ └── home/ # profile-varying $HOME entries
│ └── work/
└── home/ # everything below maps into $HOME
├── .config/
│ └── zsh/ git/ ghostty/ tmux/ nvim/ …
└── .ssh/config # when security keys are enabled
dotty dotfiles link symlinks the home tree
into $HOME using symlink-farm folded links;
dotty dotfiles status shows what is linked,
missing, or conflicting.
Profiles travel with the repo — they describe machine classes (personal, work), not individual machines. See Profiles.
Public config — $XDG_CONFIG_HOME/dotty¶
| Path | Purpose |
|---|---|
~/.config/dotty/<profile>/ |
Symlinks into the repo's profiles/<name> directories |
~/.config/dotty/active-profile |
Symlink to the active profile — the only machine-local piece of profile state |
Nothing here is secret: it is symlinks into your dotfiles repo plus one more.
Retargeting active-profile
(dotty profile activate) atomically swaps
every per-profile rendered file at once.
Private data — $XDG_DATA_HOME/dotty¶
Created 0700, contents 0600. This is personal, per-machine state that must
never land in a repository:
| Path | Purpose |
|---|---|
~/.local/share/dotty/ |
Signing-key stubs (the non-secret halves of YubiKey-resident SSH keys) |
~/.local/share/dotty/backups/<timestamp>/ |
Timestamped backups taken when linking with --on-conflict=backup — recover with dotty dotfiles restore |
~/.local/share/dotty/dotty-ssh-askpass |
The askpass applet symlink that routes OpenSSH PIN prompts to pinentry-mac |
~/.local/share/dotty/private/<profile>/ |
Decrypted plaintext of the encrypted private repository, linked into $HOME through the private/active-profile symlink |
Security-key serial→alias mappings also live on the private side — key serials identify your hardware and stay out of the public repo.
PII — ~/.config/private/git/config¶
Your git user.name / user.email (and the commit.gpgSign default) are
written once by dotty init to ~/.config/private/git/config, which the
shared git config includes. It is never overwritten on re-runs and never part of
the dotfiles repo — so the repo can be public without leaking who you are.
The scalable form of this is the
encrypted private repository: a second repo, marked by
.dotty-private, whose per-profile trees hold git identities, ssh host blocks,
and the rest of your PII as age ciphertext keyed to your YubiKeys. When a
profile records one (privateRepo in its answers), dotty private link
provides ~/.config/private/git/config per profile and init skips its own
write.
Keychain¶
Secrets managed by dotty env don't live on disk at
all — they are items in the macOS login Keychain.