This repo manages my machine setup with:
yadmfor tracked dotfiles in$HOMEbrewfor machine-global packages and apps1Passwordfor secrets, SSH, and identity material
A machine should be reproducible, composable, and scoped to a domain like personal or work.
Today, dotfiles is heavily configured around macOS. In the future, linux distributions and other OSes may see more functionality contributed.
There are three layers here:
- Tracked home files
- this repo is laid out like
$HOME yadmtracks the shared dotfiles directly- examples:
.gitconfig,.zshrc,.config/nvim,.pi/agent/settings.json
- this repo is laid out like
- Bootstrap/ops scripts
- tracked under
~/script - used for setup, package management, identity helpers, and audits
- tracked under
- Local-only machine state
- identity files, secrets, and machine-specific overrides
- intentionally not committed
That means ~/script is part of the tracked home layout and is expected to be available on every machine after yadm clone.
On a fresh macOS machine:
brew install yadm
yadm clone git@github.com:bry-guy/dotfiles.git
yadm bootstrap personal-macosOr for a work-scoped machine:
yadm bootstrap work-macosyadm bootstrap is a thin wrapper around ~/script/setup, so the explicit form below also works:
~/script/setup personal-macosRunning ~/script/setup [brew-profile] currently does the following:
- installs Homebrew if needed
- installs baseline Brew manifests
- installs 1Password dependencies where appropriate
- persists the selected Brew profile to
~/.config/dotfiles/brew-profile - applies the selected Brew profile if one was provided
- on macOS, enables the tracked
launchdagent for automatic terminal theme sync whendark-notifyis installed - bootstraps SSH known_hosts for common remotes
Running ~/script/setup without a profile still performs the baseline bootstrap only.
After that, Homebrew package state is managed through the manifest/profile system below.
Homebrew state is tracked via small reusable manifests under:
~/script/brew/manifests/
Machine profiles are tracked under:
~/script/brew/profiles/
The old top-level legacy files like ~/.brewfile.* are no longer used and should not exist.
Profiles represent the intended machine role and decide which Brew manifest groups get applied beyond the baseline bootstrap.
personal-macos- intended for a personal macOS workstation
- includes personal infrastructure, personal AI tooling, and personal GUI apps
work-macos- intended for a work-scoped macOS workstation
- includes work infrastructure, work AI tooling, and work-scoped GUI apps
- currently does not include
apps.personal
If no profile is selected, ~/script/setup still performs the baseline bootstrap, but it does not persist ~/.config/dotfiles/brew-profile and does not apply a role-specific package set.
auth.1passwordbase.corebase.desktop-macosdev.commoninfra.commoninfra.personalai.commonai.personalvirtual.colimaapps.commonapps.personal
auth.1passwordbase.corebase.desktop-macosdev.commoninfra.commoninfra.workai.commonai.workvirtual.colimaapps.commonapps.work
auth.1passwordbase.corebase.desktop-macosdev.commoninfra.commoninfra.personalinfra.workai.commonai.personalai.workvirtual.colimaapps.commonapps.personalapps.work
ai.common currently includes shared AI/dev tooling such as ccusage, claude-code, codex, and pi-coding-agent.
Apply a full profile:
~/script/brew-apply-profile personal-macosThe tracked theme sync pieces are:
~/script/theme-sync— appliesdark/lightthemes across Neovim, Pi, Posting, Harlequin, tmux, and Claude Code, and sends a best-effort Ghostty reload signal on macOS; shared Harlequin profiles/themes live in~/.harlequin.toml, while project-local.harlequin.tomlfiles should only setdefault_profilebecause Harlequin shallow-merges top-level config tables; Posting theme state is owned by~/.config/posting/config.yaml, while project-local overrides should useposting.env/POSTING_*and usually omitPOSTING_THEME~/script/sunfly-install <extra...>— updates the tracked local Sunfly config files fromgithub.com/bry-guy/sunfly; required extras are explicit (ghostty,pi,posting,tmux, orall)~/script/theme-watch— macOS watcher wrapper arounddark-notify~/Library/LaunchAgents/net.bryguy.theme-sync.plist— user launch agent for automatic macOS syncing~/.local/share/darkman/{dark-mode.d,light-mode.d}/50-theme-sync— Linux darkman hooks~/.local/share/posting/themes/{moonfly,sunfly}.yaml— tracked local Posting themes;sunfly-install postingrefreshessunfly.yamlfrom the public Sunfly repo~/.pi/agent/themes/{moonfly,sunfly}.json— tracked local Pi themes;sunfly-install pirefreshessunfly.jsonfrom the public Sunfly repo~/.config/tmux/theme.dark.confand~/.config/tmux/theme.light.conf— tracked tmux templates;sunfly-install tmuxrefreshes the light template from the public Sunfly repo andtheme-synccopies the active template to~/.config/tmux/theme.conf~/.config/ghostty/themes/Sunfly— tracked local Ghostty light theme;sunfly-install ghosttyrefreshes it from the public Sunfly repo~/.config/nvim/lua/plugins/sunfly.lua— tracked Neovim Sunfly loader glue; the current Sunfly Neovim implementation still uses Moonfly under the hood, sobluz71/vim-moonfly-colorsremains a required dependency~/docs/plans/light-theme-follow-up.md— follow-up bug plan for remaining light-mode polish and automation issues
Useful commands:
~/script/sunfly-install ghostty pi posting tmux
~/script/theme-sync auto
~/script/theme-sync-enable
~/script/theme-sync-disablebrew-apply-profile automatically taps any required taps declared by the manifests and will continue past a failing manifest, then report a skipped-manifest summary at the end.
Apply one or more manifests directly:
~/script/brew-apply-manifest base.core ai.common ai.personalShow the fully merged desired Brewfile for a profile:
~/script/brew-wanted personal-macosAudit installed Homebrew state against a profile:
~/script/brew-audit personal-macosIgnore a local-only package explicitly by adding a Brewfile-style entry to:
~/.config/dotfiles/brew-ignore
Example:
cat >> ~/.config/dotfiles/brew-ignore <<'EOF'
brew "foo"
cask "bar"
tap "homebrew/cask-fonts"
EOFbrew-audit treats ignored entries as intentional local exceptions. It also reports stale ignores so the file stays tidy.
To avoid passing a profile every time, set one of:
DOTFILES_BREW_PROFILE=personal-macos, or~/.config/dotfiles/brew-profilecontaining the selected profile name
Normally, ~/script/setup personal-macos or ~/script/setup work-macos writes this file automatically.
On this machine, the local profile file is used.
Interactive shells wrap brew and mark package state dirty after successful mutating commands such as:
brew installbrew uninstallbrew tapbrew upgrade
When state is dirty, the shell reminds me to run:
~/script/brew-auditThe intended workflow is:
- install something normally when needed
- run
~/script/brew-audit - either add it to the right tracked manifest or explicitly add it to
~/.config/dotfiles/brew-ignore - uninstall it if it was temporary or accidental
- machine-global CLI tools
- GUI apps
- workstation utilities
- machine-level infra / AI / virtualization tools
- project-local tooling
- versioned project runtimes
- repo-specific development toolchains
Global mise usage is intentionally minimized in favor of Brew for machine-global tools.
Machine identity is selected locally, not committed.
Apply machine-local identity files:
~/script/identity-apply personalOn a work-scoped machine, this also works and records the machine profile as work:
script/identity-apply workInspect the current generated identity files:
~/script/identity-current~/.config/dotfiles/identity-profile~/.gitconfig.identity.local~/.gitconfig.identity.work~/.ssh/config.identity~/.ssh/git-personal.pub~/.ssh/git-work.pub(when available)
Tracked git config includes:
~/.gitconfig.identity.localas the personal default~/.gitconfig.identity.workfor repos whose remote points atlumora.ghe.com
Tracked SSH config includes ~/.ssh/config.identity and uses the 1Password SSH agent.
github.comuses the personal SSH key and personal git emaillumora.ghe.comuses the work SSH key and work git email
That means the same tracked dotfiles can be used on personal and work machines without changing committed config. The machine-local files are generated from 1Password and kept out of the repo.
- git name:
Bryan Smith - git email:
bryan@bry-guy.net - 1Password account:
my.1password.com - 1Password vault:
Private - key item title:
git-personal - legacy fallback key item title:
brainbook.local
- git name:
Bryan Smith - git email:
bryan@lumoratech.com - 1Password account:
my.1password.com - 1Password vault:
Lumora - key item title:
git-work
Both personal and work defaults can be overridden with environment variables before running script/identity-apply.
Secrets should live in 1Password, not in a sourced ~/.secrets file.
Current shell behavior already loads some secrets from 1Password when available. For example, GEMINI_API_KEY is now loaded from:
op://bry-guy/GEMINI_API_KEY/credential
The direction is:
- no new secrets in
~/.secrets - prefer
op read,op run, or targeted shell loading - keep machine-global secrets in the appropriate 1Password vault