{"id":47735242,"url":"https://github.com/brettdavies/dotfiles","last_synced_at":"2026-05-16T07:22:20.524Z","repository":{"id":323046147,"uuid":"1091271911","full_name":"brettdavies/dotfiles","owner":"brettdavies","description":"Cross-platform dotfiles for macOS and headless Ubuntu servers — managed with GNU Stow, secured with git-crypt","archived":false,"fork":false,"pushed_at":"2026-04-15T23:13:51.000Z","size":782,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T23:15:04.080Z","etag":null,"topics":["cross-platform","dotfiles","git-crypt","gnu-stow","macos","shell-configuration","stow","ubuntu","zsh"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brettdavies.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-06T19:42:04.000Z","updated_at":"2026-04-15T23:11:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brettdavies/dotfiles","commit_stats":null,"previous_names":["brettdavies/dotfiles","brettdavies/dotfiles-lib"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/brettdavies/dotfiles","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fdotfiles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fdotfiles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fdotfiles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fdotfiles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brettdavies","download_url":"https://codeload.github.com/brettdavies/dotfiles/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fdotfiles/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31865081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cross-platform","dotfiles","git-crypt","gnu-stow","macos","shell-configuration","stow","ubuntu","zsh"],"created_at":"2026-04-02T22:26:31.251Z","updated_at":"2026-05-16T07:22:20.510Z","avatar_url":"https://github.com/brettdavies.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dotfiles\n\nCross-platform dotfiles for macOS and headless Ubuntu servers — managed with\n[GNU Stow](https://www.gnu.org/software/stow/), secured with [git-crypt](https://github.com/AGWA/git-crypt).\n\n## Quick Start\n\n```bash\n# 1. Install Homebrew (skip if already installed)\n#    macOS\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\neval \"$(/opt/homebrew/bin/brew shellenv)\"\n#    Linux\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\neval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n\n# 2. Install core tools\nbrew install stow git-crypt\n\n# 3. Clone and unlock\ngit clone git@github.com:brettdavies/dotfiles.git ~/dotfiles\ncd ~/dotfiles\ngit-crypt unlock ~/.config/git-crypt/key\n\n# 4. Deploy\nscripts/stow-deploy --all              # macOS: shared + desktop packages\nscripts/stow-deploy --headless --all   # Linux: shared packages only\n```\n\n\u003e **Stow \u003e= 2.4.0 required.** Ubuntu 24.04 apt only has 2.3.1, which has a\n\u003e [bug with nested `dot-` directories][stow-bug]. Use Homebrew/Linuxbrew.\n\nFor detailed platform-specific setup (oh-my-zsh, Ghostty, Cursor extensions, iCloud sync), see\n[BOOTSTRAP.md](BOOTSTRAP.md).\n\n[stow-bug]: https://github.com/aspiers/stow/issues/33\n\n## Repository Layout\n\n```text\ndotfiles/\n├── stow/                  Stow packages (symlinked into $HOME)\n├── config/\n│   ├── shell/             Shell fragments auto-sourced by .profile\n│   ├── git/               Per-platform git config templates\n│   ├── apparmor.d/        System-level AppArmor profiles (deployed via apparmor-deploy.sh)\n│   └── systemd/system/    System-level units (deployed via nas-deploy.sh)\n├── scripts/\n│   ├── stow-deploy        Stow wrapper with conflict resolution\n│   ├── nas-deploy.sh      System-level NAS mount/automount deploy\n│   ├── apparmor-deploy.sh System-level AppArmor profile deploy (Playwright/Chromium)\n│   └── sync/              iCloud sync scripts\n├── .githooks/             Repo-local git hooks (core.hooksPath)\n├── .github/\n│   ├── workflows/         CI: release.yml, shellcheck.yml\n│   └── rulesets/          Branch protection rules\n├── tests/                 bats-core test suites\n└── docs/\n    ├── solutions/         Solved problems and patterns\n    ├── plans/             Implementation plans\n    └── brainstorms/       Design explorations\n```\n\n### Stow Packages\n\nEach directory under `stow/` is a package. Files prefixed with `dot-` become dotfiles (`.` prefix) when symlinked via\n`stow --dotfiles`.\n\n| Package              | What it manages                                                                                     |\n| -------------------- | --------------------------------------------------------------------------------------------------- |\n| `bash`               | `.bashrc`, `.bash_profile`, `.bash_aliases`                                                         |\n| `brew`               | `Brewfile`, `Brewfile.optional`                                                                     |\n| `bun`                | `.bunfig.toml`                                                                                      |\n| `caam`               | `.caam/` (Claude account rotation config + vault, git-crypt encrypted)                              |\n| `claude`             | `.claude/` (settings, hooks, statusline, templates), `.markdownlint-cli2.yaml`                      |\n| `codex`              | `.codex/config.toml`                                                                                |\n| `cursor`             | `.cursor/rules/`, `extensions.txt`                                                                  |\n| `gh`                 | `.config/gh/` (GitHub CLI config), `.local/bin/gh` (merge guard wrapper)                            |\n| `ghostty`            | `.config/ghostty/config`                                                                            |\n| `git`                | `.gitconfig`, `.config/git/` (ignore, allowed\\_signers)                                             |\n| `github`             | `.config/github/` (PR template and other repo-workflow assets)                                      |\n| `gogcli`             | `.config/gogcli/config.json` — Google Workspace CLI config                                          |\n| `launchagent`        | `~/Library/LaunchAgents/` (macOS only)                                                              |\n| `lazygit`            | `.config/lazygit/config.yml` — clipboard over SSH via OSC 52                                        |\n| `local`              | `.local/bin/` (env, op-ssh-sign-wrapper, tmux-new-session)                                          |\n| `micro`              | `.config/micro/settings.json` — editor settings                                                     |\n| `obsidian`           | `.config/obsidian/`, systemd service, CLI wrapper (Linux only)                                      |\n| `openclaw`           | systemd user units for memory extract/distill and morning briefing (Linux, opt-in)                  |\n| `opencode`           | `.config/opencode/config.json`                                                                      |\n| `opendataloader-pdf` | Socket-activated hybrid PDF server with idle-exit, systemd user units (Linux only)                  |\n| `pip`                | `.config/pip/pip.conf`                                                                              |\n| `qmd`                | `.local/bin/qmd` wrapper + systemd user units (qmd-serve daemon, embed + update timers, Linux only) |\n| `rclone`             | `.config/rclone/`, Box bisync systemd service + timer (Linux only)                                  |\n| `rust`               | `rustup-update.service` + `.timer` (nightly rustup self-update, Linux, opt-in)                      |\n| `secrets`            | `.secrets` (git-crypt encrypted)                                                                    |\n| `shell`              | `.profile`                                                                                          |\n| `ssh`                | `.ssh/config` (git-crypt encrypted)                                                                 |\n| `tmux`               | `.config/tmux/tmux.conf`                                                                            |\n| `tmuxinator`         | `.config/tmuxinator/*.yml` — declarative session configs (16 projects, see below)                   |\n| `yazi`               | `.config/yazi/` — file manager config, keymaps, theme, packages                                     |\n| `zsh`                | `.zshrc`, `.zshenv`, `.zprofile`, `.p10k.zsh`                                                       |\n\n### Tmuxinator Sessions\n\nEvery `.config/tmuxinator/*.yml` config defines the same 3-pane working layout: yazi on the left (1/3 width, full\nheight), a bare shell top-right (2/3 × 2/3), and lazygit bottom-right (2/3 × 1/3). All three panes start in the\nproject's root.\n\nStart or attach to a configured session with `tmuxinator start \u003cname\u003e` — it creates the session on the first call and\nattaches on every subsequent call, so the same command works whether or not the session is already running:\n\n```bash\ntmuxinator start anc                          # local terminal\nmux start anc                                 # zsh shell alias (same thing)\nssh \u003cdev-host\u003e -t tmuxinator start anc        # over SSH (preferred connection idiom)\n```\n\nRaw `tmux attach -t \u003cname\u003e` only works after the session has already been started, which makes it the wrong default for\nSSH.\n\nTo create a new session from scratch (config + symlink + first start in one shot), use `tmux-new-session \u003cname\u003e\n\u003crepo-path\u003e` — it writes a new tmuxinator config into `stow/tmuxinator/dot-config/tmuxinator/`, re-stows the package,\nthen runs `tmuxinator start`.\n\n### System-Level Units (`config/systemd/system/`)\n\nSystem-level systemd units are **not** managed by stow (which targets `$HOME`). They live in `config/systemd/system/`\nand are deployed via `scripts/nas-deploy.sh`, which copies them to `/etc/systemd/system/` and activates them.\n\n| Unit                | Purpose                                         |\n| ------------------- | ----------------------------------------------- |\n| `mnt-nas.mount`     | SMB mount for the NAS (`//\u003cnas-host\u003e/openclaw`) |\n| `mnt-nas.automount` | On-demand automount, solves WiFi boot race      |\n\n**Deploy:** `sudo scripts/nas-deploy.sh` (requires `/root/.smbcredentials-\u003cnas-host\u003e` from 1Password).\n\n### AppArmor Profiles (`config/apparmor.d/`)\n\nSystem-level AppArmor profiles live in `config/apparmor.d/` and are deployed via `scripts/apparmor-deploy.sh`, which\ncopies every file to `/etc/apparmor.d/` and reloads it with `apparmor_parser -r`. Profiles persist across reboots\nbecause AppArmor loads `/etc/apparmor.d/` at boot.\n\n| Profile      | Purpose                                                                                   |\n| ------------ | ----------------------------------------------------------------------------------------- |\n| `playwright` | Grants `userns` to Playwright's bundled Chromium so the browse tool works on Ubuntu 24.04 |\n\n**Deploy:** `sudo scripts/apparmor-deploy.sh` (Linux only; requires the `apparmor` package).\n\n### Shell Environment (`config/shell/`)\n\n`.profile` sources every `*.sh` file in `config/shell/` automatically — drop a file in and it's picked up, no manifest\nneeded.\n\n| File                | Purpose                                               |\n| ------------------- | ----------------------------------------------------- |\n| `caam.sh`           | Claude account rotation wrapper + daemon              |\n| `caches.sh`         | XDG cache directory locations                         |\n| `claude-code.sh`    | Claude Code environment variables                     |\n| `github.sh`         | GitHub CLI aliases                                    |\n| `gogcli.sh`         | Google Workspace CLI keyring password injection       |\n| `litellm.sh`        | LiteLLM proxy configuration                           |\n| `lm-studio.sh`      | LM Studio PATH setup                                  |\n| `local-paths.sh`    | Custom local PATH additions                           |\n| `models.sh`         | AI/ML model storage locations                         |\n| `platform-linux.sh` | Linux-specific platform checks and config             |\n| `python.sh`         | Python tooling config                                 |\n| `qmd.sh`            | `QMD_SERVER` export (qmd-serve daemon URL)            |\n| `supply-chain.sh`   | Supply-chain safety (package age gates)               |\n| `telemetry.sh`      | Telemetry opt-out environment variables               |\n| `tmuxinator.sh`     | `mux` and `mux-all` tmuxinator wrappers               |\n| `shell-functions`   | Interactive shell utilities (sourced by bashrc/zshrc) |\n\n## Secrets Management\n\nSensitive files are encrypted with git-crypt:\n\n- `stow/secrets/dot-secrets` — API keys and tokens\n- `stow/ssh/dot-ssh/config` — SSH host configurations\n- `stow/git/dot-config/git/allowed_signers` — SSH allowed signers\n\nGit hooks auto-unlock on checkout and merge. Back up `~/.config/git-crypt/key` in a password manager — if lost,\nencrypted files cannot be recovered.\n\n## Git Hooks\n\nActivated via `core.hooksPath` (set automatically by `stow-deploy`):\n\n| Hook            | Purpose                                             |\n| --------------- | --------------------------------------------------- |\n| `pre-commit`    | Blocks commits on `main`, verifies `commit.gpgsign` |\n| `post-checkout` | Auto-unlocks git-crypt, chains Git LFS              |\n| `post-merge`    | Auto-unlocks git-crypt, chains Git LFS              |\n| `pre-push`      | Chains Git LFS pre-push                             |\n\n## CI and Testing\n\n| Workflow         | Trigger              | Purpose                                      |\n| ---------------- | -------------------- | -------------------------------------------- |\n| `release.yml`    | Squash merge to main | CalVer tag, changelog via git-cliff, release |\n| `shellcheck.yml` | Push / PR            | Lints shell scripts                          |\n\nShell scripts are tested with [bats-core](https://github.com/bats-core/bats-core) (`bats tests/`). Suites cover\nstow-deploy, git hooks, shell config, symlinks, and CLI wrappers.\n\n## Performance\n\nShell startup budgets are enforced — non-interactive shells must start under 200ms, interactive shells under 500ms.\n  \nSee `docs/solutions/performance-issues/` for optimization details.\n\n## Cross-Platform Notes\n\n- `$OSTYPE` checks gate macOS-specific features; `$HOME` used everywhere\n- Homebrew paths: `/opt/homebrew` (macOS) vs `/home/linuxbrew/.linuxbrew` (Linux)\n- Git signing: 1Password on macOS, `ssh-keygen` fallback on Linux (via `op-ssh-sign-wrapper`)\n- SSH config uses `Match exec` for platform-conditional 1Password agent paths\n- All GitHub/Gist URLs rewritten to SSH via `url.insteadOf` in `.gitconfig`\n- SSH key: `~/.ssh/brett_ed25519` on all machines\n- oh-my-zsh plugins: brew symlinks on macOS, git clones on Linux\n\n## Release Automation\n\nEvery squash merge to `main` triggers a GitHub Action that computes a CalVer version (`YYYY.MM.DD`), tags the commit,\nand creates a GitHub Release. Release notes are extracted from the topmost section of the committed `CHANGELOG.md`. See\n[RELEASES.md](RELEASES.md) for the end-to-end flow (feature branch → dev → `release/*` cherry-pick branch → main).\n\n### CI_RELEASE_TOKEN Secret\n\nThe workflow pushes back to protected `main`, which requires a fine-grained PAT stored as the `CI_RELEASE_TOKEN` repo\nsecret.\n\n**Create / rotate the token:**\n\n```bash\nop read \"op://secrets-dev/dotfiles_RELEASE_TOKEN/credential\" \\\n  | gh secret set CI_RELEASE_TOKEN\n```\n\n**Creating a new PAT** (if the 1Password entry doesn't exist):\n\n1. Go to \u003chttps://github.com/settings/personal-access-tokens/new\u003e\n2. **Repository access:** `brettdavies/dotfiles` only\n3. **Permissions:** Contents: Read and write\n4. Save to 1Password at `secrets-dev/dotfiles_RELEASE_TOKEN`\n5. Run the `gh secret set` command above\n\n## Documentation\n\nPast solutions and design decisions live in `docs/solutions/`:\n\n- **Deployment** — cross-platform stow deployment, shell config fixes, headless git signing, conflict resolution\n- **Configuration** — branch divergence reconciliation, workflow enforcement\n- **Performance** — shell startup optimization, zsh interactive startup tuning\n\n## License\n\nPersonal dotfiles — use at your own risk.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettdavies%2Fdotfiles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrettdavies%2Fdotfiles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettdavies%2Fdotfiles/lists"}