Nix configs for my machines
  • Nix 76.7%
  • Python 21.2%
  • Shell 2.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Bryan Thompson 5c71689f2c
Manage Studio Jellyfin through Homebrew cask
Jellyfin.app was hand-installed and has no built-in updater, so
onActivation.upgrade never touched it. Declaring the cask lets each
darwin-rebuild switch keep it current.
2026-10-11 21:20:46 -07:00
hardware-configs feat(gnarbox): restore NixOS host on fresh hardware 2026-09-29 08:15:27 -07:00
modules Manage Studio Jellyfin through Homebrew cask 2026-10-11 21:20:46 -07:00
overlays add zen browser to gnarbox 2026-04-04 12:05:19 -07:00
scripts Retire Hindsight service and update machinery 2026-09-29 10:54:16 -07:00
.envrc.example add git fetch to .envrcs 2026-02-13 08:12:11 -08:00
.gitignore Allow AGENTS.md to be tracked in this repo 2026-04-10 11:47:55 -07:00
AGENTS.md feat(gnarbox): restore NixOS host on fresh hardware 2026-09-29 08:15:27 -07:00
flake.lock Track herdr upstream default branch on Gnarbox 2026-10-02 16:42:45 -07:00
flake.nix Track herdr upstream default branch on Gnarbox 2026-10-02 16:42:45 -07:00
README.md Manage Studio Jellyfin through Homebrew cask 2026-10-11 21:20:46 -07:00

Nix Configuration

Declarative, reproducible system configuration for three macOS hosts using nix-darwin and Gnarbox using NixOS, plus cross-platform development environments for VA projects.

Architecture

This repository uses a dendritic (tree-like) modular architecture with flake-parts and import-tree. Configurations are organized by feature/capability rather than by host:

modules/
├── base/       # Core system: fonts, homebrew, nix-settings, zsh
├── dev/        # Development: cli-tools, editors, git
├── desktop/    # GUI: shared macOS apps plus NixOS gnome, gaming, and audio
├── services/   # Daemons/agents: Hermes, ollama, monitoring, SMB, syncthing (Obsidian Sync/backup modules retained, unused)
├── hosts/      # Compositions: a6mbp, mbp, studio (Darwin), gnarbox (NixOS)
└── dev-envs/   # VA project environments

Each host imports and composes feature modules. See modules/README.md for detailed structure.

Hosts

mbp (personal macOS)

Personal MacBook Pro and mobile fallback with syncthing, Tailscale, a Studio-backed Hermes client, and shared personal desktop apps. Obsidian Headless Sync was retired on 2026-09-16 when the personal second brain moved to Apple Notes; /Users/bryan/second-brain is a frozen rollback archive that nothing on MBP synchronizes, commits, or pushes. Location: modules/hosts/mbp.nix

a6mbp (work macOS)

Work MacBook Pro with syncthing and work tools (AWS, Docker, DDEV, Slack, Zoom). Location: modules/hosts/a6mbp.nix

studio (home server and personal desktop macOS)

Bryan's daily desktop and home server, running the primary Hermes gateway and per-user Tailscale-only remote backends, plus ollama, open-webui, monitoring (Prometheus + Grafana), SMB mount, syncthing, and iCloud backup. It shares Bryan's personal desktop applications with the MBP while retaining the Studio-only service stack. Prometheus and blackbox-exporter run as user-owned system LaunchDaemons so macOS Local Network Privacy cannot strand their LAN probes when Nix store identities change. They wait for the Nix volume before exec, while activation keeps monitoring state directories owned by the service user. Nix owns Hermes launchd supervision, permissions, ports, and Tailscale exposure; Bryan and Traci use isolated managed runtimes that update nightly and weekly, respectively. Actual updates announce their start and verified completion in Bryan's primary Matrix channel, while no-op checks stay silent. Bryan's primary backend is available at https://bryans-mac-studio.tail5ba690.ts.net through Tailscale Serve. Ollama remains bound to loopback and is forwarded tailnet-only at http://100.121.238.48:11434. Traci's isolated headless backend runs under her macOS account and is available at https://bryans-mac-studio.tail5ba690.ts.net:9120 through Tailscale Serve.

Studio manages the Codex terminal CLI and the Jellyfin media server app through its host-specific Homebrew casks, so darwin-rebuild switch keeps both current.

Third-party agent memory is retired. Agents use their harness-native memory and history plus the existing project vaults and personal Apple Notes. Hindsight's service, dedicated database, proxy and scheduled upkeep are no longer declared; its encrypted backups and inactive local data are preserved, not imported into another memory system. Dotfiles owns removal of the Hindsight, context-mode and claude-mem client integrations.

The private Studio service portal is available over Tailscale at http://100.121.238.48:8088. It uses the immutable Nixpkgs Dashy static build for links and a read-only local adapter for health state from the model-free Hermes service watchdog; it has no public ingress, arbitrary proxy, or mutable web configuration.

Studio also serves the Summit Point concept preview (services.summit-point-gallery) on loopback 127.0.0.1:4321. It is an unlisted, public-by-link design review preview for the separate private repository bryan/summit-point-site, exposed through one hostname, summitpoint.thompson.codes, on the existing remotely managed Cloudflare Tunnel. Caddy answers any other Host header with a 404. The served tree is the current symlink under ~/Library/Application Support/Thompson Codes/Summit Point Gallery/, staged by that repository's scripts/publish-review.sh as an immutable releases/<sha>/ build; rollback is scripts/publish-review.sh --rollback <sha>, an atomic symlink change that needs no rebuild and no activation. Responses are noindex/no-store. The preview carries no approved Summit Point content.

The personal second brain lives in Apple Notes (iCloud folder Second Brain) since 2026-09-16 (decision record in the frozen archive: ~/second-brain/Decisions/2026-09-16-decision-apple-notes-personal-second-brain.md). Studio no longer runs Obsidian Headless Sync or the nightly vault-git-backup snapshot for /Users/bryan/second-brain; the archive repository and its remote are preserved unchanged as rollback history. After update-system, launchctl print gui/$(id -u)/md.obsidian.headless-sync and …/com.snowboardtechie.vault-git-backup should both report the agents as absent. Location: modules/hosts/studio.nix

gnarbox (NixOS desktop)

Gnarbox is a GNOME gaming desktop with Tailscale, a Hermes Desktop/CLI client of Studio's primary backend, and Pi for local agent work using Studio's Ollama models. Its fresh 26.05 installation supplies the storage layout in hardware-configs/gnarbox.nix; do not reuse the pre-Omarchy disk UUIDs or LUKS configuration. SSH is key-only, sudo still requires a password, and GNOME uses its normal screen-lock defaults. Syncthing is disabled on Gnarbox; its prior local configuration is preserved for rollback, and the retired personal second-brain archive must not be synced. Claude Code is installed with Anthropic's user-managed native installer under ~/.local/bin/claude instead of the lagging Nix package; its own updater owns future versions. Location: modules/hosts/gnarbox.nix

Shared Configuration

Managed hosts share common packages through feature modules on both Darwin and NixOS. All three Macs also include the Cinny Matrix desktop client, available in /Applications/Nix Apps after rebuilding.

Feature modules define both Darwin and NixOS aspects. Gnarbox is the published NixOS system output; other Linux machines use only the development flakes and manage their operating systems separately.

Prerequisites

Apple Silicon Macs (mbp, a6mbp, studio): Install Determinate Nix Installer:

curl -fsSL https://install.determinate.systems/nix | sh -s -- install --determinate

All macOS hosts also need Homebrew installed first. nix-darwin's homebrew module manages your Brewfile; it does not install Homebrew itself. If brew isn't already on the machine:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Gnarbox: NixOS is already installed. Before any flake activation, compare hardware-configs/gnarbox.nix with the live /etc/nixos/hardware-configuration.nix, especially the root, EFI, and swap UUIDs. Preserve the fresh installation's system.stateVersion = "26.05".

Installation

Clone the repository:

git clone https://git.snowboardtechie.com/bryan/nix-configs.git ~/code/nix-configs
cd ~/code/nix-configs

Build and activate:

macOS — Apple Silicon (mbp, a6mbp, studio):

First-time bootstrap of nix-darwin (Determinate has flakes enabled already):

sudo nix run nix-darwin/master#darwin-rebuild -- switch --flake '.#mbp'  # or '.#a6mbp', '.#studio'

Subsequent rebuilds:

darwin-rebuild switch --flake '.#mbp'

Gnarbox (on the NixOS machine): Clone this repository to ~/code/nix-configs, then evaluate and build before switching. Keep /etc/nixos and the prior boot generation available for rollback.

cd ~/code/nix-configs
nix --extra-experimental-features 'nix-command flakes' flake check --no-build
nix --extra-experimental-features 'nix-command flakes' build --no-link '.#nixosConfigurations.gnarbox.config.system.build.toplevel'
sudo env NIX_CONFIG='experimental-features = nix-command flakes' nixos-rebuild switch --flake '.#gnarbox'

After switching, authenticate the existing tailnet interactively with sudo tailscale up (do not put auth keys in Git or command history). Verify tailscale status and SSH access before rebooting. Hermes is client-only; connect the Desktop app to Studio's authenticated remote backend at https://bryans-mac-studio.tail5ba690.ts.net using its normal login flow, not a second local gateway. Confirm remote authentication in the app rather than inferring it from a successful Nix build.

Next, clone the dotfiles to ~/code/dotfiles and follow its Gnarbox Stow preflight (stow -n -v before apply) and setup script. Git identity and any signing credentials are machine-local and must not go into this flake. Pi and Zed use Studio Ollama over the tailnet at http://100.121.238.48:11434; Gnarbox does not run another Ollama server or install OpenCode. Verify /api/tags and a real completion from Gnarbox after Tailscale authentication before claiming that AI connections work.

Usage

Obsidian Headless (retired 2026-09-16; procedure kept for rollback)

The obsidian-headless and vault-git-backup modules remain in modules/services/ but no host enables them: the personal vault they served is frozen and Apple Notes is authoritative. Re-enabling them on a host is the rollback path and requires the steps below again. Do not use the stale Syncthing copy at /Users/bryan/notes/second-brain.

Historical procedure (one host at a time, Studio first, then MBP):

  1. In desktop Obsidian, open /Users/bryan/second-brain, disable the core Sync plugin, and quit Obsidian. Disable automatic commit, pull, and push actions in the Obsidian Git community plugin as well. Desktop Obsidian may be reopened after Headless Sync is healthy, but its Sync plugin must remain off.
  2. Verify .obsidian/core-plugins.json contains "sync": false. Do not continue while it is true.
  3. Apply the host configuration only after step 2: darwin-rebuild switch --flake '~/code/nix-configs#studio' on Studio or darwin-rebuild switch --flake '~/code/nix-configs#mbp' on MBP. The managed job waits without a crash loop while interactive setup is incomplete.
  4. Log in without command-line credentials: ob login.
  5. List existing remote vaults: ob sync-list-remote. Select the existing remote vault; do not run ob sync-create-remote.
  6. Connect the existing local vault, allowing any E2E password to be prompted interactively: ob sync-setup --vault "<existing-vault-name-or-id>" --path /Users/bryan/second-brain --device-name studio on Studio, or use --device-name mbp on MBP. Do not use --password, reset history, or create a replacement remote.
  7. Keep core-plugin activation state device-specific: ob sync-config --path /Users/bryan/second-brain --configs "app,appearance,appearance-data,hotkey,core-plugin-data". Omitting core-plugin prevents the phone's enabled Sync state from overwriting the disabled Mac state, and prevents the Mac state from disabling Sync on the phone.
  8. Verify configuration with ob sync-status --path /Users/bryan/second-brain; its Configs line must list core-plugin-data but must not list a standalone core-plugin entry.
  9. Start the configured managed process immediately: launchctl kickstart -k gui/$(id -u)/md.obsidian.headless-sync.
  10. Inspect /tmp/obsidian-headless.log and /tmp/obsidian-headless.error.log, then rerun ob sync-status --path /Users/bryan/second-brain and verify .obsidian/core-plugins.json still contains "sync": false before reopening desktop Obsidian.

Before Studio's first scheduled Git snapshot, review exactly which untracked, non-ignored files it would include:

git -C /Users/bryan/second-brain ls-files --others --exclude-standard

The nightly job logs to /tmp/vault-git-backup.log and /tmp/vault-git-backup.error.log. Any nonzero result requires manual investigation; do not respond by pulling, rebasing, resetting, or force-pushing automatically. MBP's .git directory remains untouched, but no agent or plugin on MBP may commit, pull, or push this vault.

The corresponding agent policy lives in the second-brain repository's AGENTS.md, which now marks that vault as a frozen archive.

Inkling-Small release watchdog

The tracked watchdog in scripts/check-inkling-small-release.py checks the official Thinking Machines Hugging Face namespace daily for a public, ungated Inkling-Small repository with actual weight files. It stays silent while the release is pending. Once weights appear, its Hermes cron job reviews the official model card, formats and sizes, Apple Silicon runtime support, quantized availability, licensing, and fit on Studio's M2 Ultra with 128 GB unified memory before sending one notification. The release remains pending until that review explicitly acknowledges it, so an interrupted review retries on the next daily run. The job never downloads weights, installs software, or changes the running Ollama/Open WebUI stack.

The recreatable job definition is scripts/inkling-small-release-watch.job.json. Runtime state is private under ~/.hermes/state/inkling-small-release-watch.json and is not committed.

Colibrì GLM-5.2 readiness watchdog

The Hermes watchdog checks grouped-quality Metal support, a complete compatible checkpoint, reproducible Ultra-class Mac Studio evidence, tool-calling stabilization, and blocking regressions before recommending a controlled GLM-5.2 proof of concept. The recreatable, read-only job definition is scripts/colibri-glm52-readiness-watch.job.json. Runtime notification state remains private under ~/.hermes/state/ and is not committed.

GitHub incident recovery watchdog

The tracked monitor in scripts/check-github-status.py polls GitHub's official Statuspage summary every five minutes and emits a timestamp-free snapshot. Hermes suppresses unchanged ticks and uses the OpenAI Codex subscription only when the official status changes. This incident-specific finite watch stays silent for its initial baseline and source-health noise, notifies Bryan in Matrix when service meaningfully improves or worsens, and automatically stops after 36 checks (about three hours). The recreatable job definition is scripts/github-status-watch.job.json.

Apply Changes

darwin-rebuild switch --flake '~/code/nix-configs#mbp'

Update Dependencies

nix flake update
# then rebuild using commands above

Development Environments

Cross-platform development environments for VA projects:

Linux machines use these nix develop outputs without consuming a system configuration from this repository.

  • vets-website: Node 22.22.0, Yarn 1.x, Cypress → vets-website
  • vets-api: Ruby 3.3.6, PostgreSQL, Redis, Kafka → vets-api
  • next-build: Node 24, Yarn 3.x, Playwright → next-build
  • component-library: Node 22, Yarn 4.x, Puppeteer → component-library
  • content-build: Node 14.15.0, Yarn 1.x, Cypress → content-build
  • simpler-grants: Node 20, Python 3.11, pnpm (corepack) → simpler-grants-protocol (Poetry must be installed separately via brew install poetry or pipx install poetry)

Activate manually:

nix develop '~/code/nix-configs#vets-website'

Development environment definitions are located in modules/dev-envs/.

OpenCode

The OpenCode CLI is installed via Homebrew (opencode) on managed macOS systems. The OpenCode desktop app (opencode-desktop cask) is installed on mbp.

Authentication:

Authenticate your AI providers:

opencode auth login

Usage:

Start the CLI:

opencode

Documentation:

Resources

3 gits, one repo

This repository syncs to multiple remotes. The primary repository is at git.snowboardtechie.com, with backups on Codeberg and GitHub.

License

This configuration is free to use and modify for your own purposes.