aboutsummaryrefslogtreecommitdiffstats

Installation

Full setup guide, from a freshly unboxed machine to a working environment. For the short version, see README.md.

macOS (nix-darwin)

1. Xcode Command Line Tools

Needed for git and the system toolchain before Nix is in place.

xcode-select --install

Sign in to the App Store now as well — mas installs the App Store apps during the first switch and fails if no account is signed in.

2. Install Determinate Nix

curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --determinate

--determinate matters: modules/darwin/default.nix sets nix.enable = false, which hands Nix management to the Determinate installer instead of nix-darwin. Installing plain upstream Nix here leaves the daemon unmanaged.

Open a new shell afterwards so nix is on PATH.

3. Install Homebrew

nix-darwin declares Homebrew packages but does not install Homebrew itself. The first switch fails without it.

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

4. Clone the repository

The clone path is not arbitrary — the rebuild and update aliases in modules/home/zsh.nix point at ~/ghq/simenandre.no/dotfiles, so clone it there.

mkdir -p ~/ghq/simenandre.no
git clone https://simenandre.no/dotfiles.git ~/ghq/simenandre.no/dotfiles
cd ~/ghq/simenandre.no/dotfiles

HTTPS on purpose: SSH access depends on 1Password's SSH agent, which is not installed yet. After the first switch, git rewrites the HTTPS remote to SSH (url."git@simenandre.no:".insteadOf), so this remote keeps working.

5. Grant Full Disk Access to the terminal

modules/darwin/macos.nix writes preferences for three sandboxed apps — Safari, Help Viewer, and TextEdit. Their preference files live under ~/Library/Containers/, which macOS protects with TCC, so defaults is denied unless the terminal running the switch has Full Disk Access. The activation script runs under set -e, so a single denial aborts the whole switch.

System Settings → Privacy & Security → Full Disk Access → + → add /System/Applications/Utilities/Terminal.app (Ghostty is not installed yet on a fresh machine) → quit and reopen the terminal, so the new grant applies to a fresh process.

6. First switch (bootstrap nix-darwin)

Pick the configuration that matches the machine — each one pins its own username, so switching with the wrong attribute fails during activation:

Attribute Machine User
makkie MacBook Pro (sets its hostname to makkie) simenandre
default Older MacBook Pro cobraz
sudo nix run nix-darwin#darwin-rebuild -- switch --flake .#makkie

This is the long one: it installs every Homebrew cask, the Mac App Store apps, all nixpkgs packages, and applies the macOS defaults. Expect Homebrew to prompt for your password, and expect to approve a few system dialogs.

Subsequent switches use the installed binary:

sudo darwin-rebuild switch --flake .#makkie

The rebuild and update aliases already carry the right attribute for the machine they were built for, so after the first switch you can just run rebuild.

Adding another Mac later means one entry in flake.nix:

"newhost" = mkDarwin {
  username = "simenandre";
  flakeAttr = "newhost";
  hostName = "newhost";
};

Restart the terminal (or log out and back in) so the new zsh config, PATH, and Ghostty configuration take effect.

7. 1Password and SSH

Git commits are signed through 1Password's SSH agent (/Applications/1Password.app/Contents/MacOS/op-ssh-sign), so this has to be set up before committing anything.

  1. Open 1Password, sign in, and unlock it.
  2. Settings → Developer → enable Use the SSH agent.
  3. Settings → Developer → enable Integrate with 1Password CLI (for op).
  4. Verify the signing key matches the one pinned in modules/home/git.nix:

shell ssh-add -l

  1. Confirm GitHub over SSH works:

shell ssh -T git@github.com

8. Sign in to the rest

Each of these is a one-time manual login:

gh auth login          # GitHub CLI
op signin              # 1Password CLI
gcloud auth login      # Google Cloud
gcloud auth application-default login

GUI apps that need a login or permission grant: Tailscale (App Store), Slack, Discord, Signal, Obsidian, Proton Mail, ProtonVPN, Spotify, Raycast (grant Accessibility), Google Drive.

9. Container runtime

Docker CLIs come from Homebrew, but the VM has to be started once:

colima start

DOCKER_HOST is already pointed at ~/.colima/default/docker.sock by modules/home/zsh.nix.

10. Rust toolchain

rustup is installed, but ships without a toolchain:

rustup default stable

11. Tools not managed by Nix

These are installed with go install and live in ~/go/bin. Shell completions for them are generated on every switch, and the generator quietly skips any that are missing:

go install github.com/sokkelorg/cli/cmd/so@latest
go install github.com/simenandre/bobbin/cmd/bobbin@latest
go install github.com/simenandre/robin-cli/cmd/robin@latest
go install github.com/dinacomputer/api/cmd/dinactl@latest
go install github.com/sokkelorg/identity/cmd/identityctl@latest

12. Loose ends

mkdir -p ~/Screenshots     # macos.nix points screencapture here; it is not created for you

~/.gitignore_global is referenced by core.excludesfile but is not managed by this repo. Create it if you want it:

.DS_Store
.twm.yaml
.twm.yml

**/.claude/settings.local.json

Some macOS defaults (Dock, Finder, trackpad) only take effect after a logout or restart.

NixOS

sudo nixos-generate-config --show-hardware-config > hosts/nixbox/hardware-configuration.nix
sudo nixos-rebuild switch --flake .#nixbox

The nixbox host expects the user simenandre. Git commit signing is disabled outside macOS, so no 1Password setup is required.

Linux (standalone home-manager)

# x86_64
home-manager switch --flake .#simenandre

# aarch64
home-manager switch --flake .#simenandre@aarch64-linux

Day-to-day

Aliases defined in modules/home/zsh.nix (macOS):

Alias Does
rebuild darwin-rebuild switch against this flake
update nix flake update, then rebuild
nix-gc Garbage-collect store paths older than 14 days

scripts/cleanup.sh is a separate macOS cleanup utility (caches, logs, trash).

Troubleshooting

error: Homebrew is not installed — step 3 was skipped, or brew is not on PATH for the shell running the switch. Run eval "$(/opt/homebrew/bin/brew shellenv)".

Mac App Store apps fail to install — sign in to the App Store, then re-run the switch. mas cannot authenticate on its own.

defaults[…] Could not write domain …/Library/Containers/…; exiting — the terminal lacks Full Disk Access (step 5), so a sandboxed app's preferences can't be written, and set -e aborts the switch. Grant it, then reopen the terminal before retrying — an already-running process keeps the old denial.

If it still fails after that, the app's container does not exist yet:

ls -d ~/Library/Containers/com.apple.Safari

defaults cannot create a container the app has never made. Launch the app once (Safari, Help Viewer, or TextEdit — whichever the error names), quit it, and re-run. The three container-backed domains are all in modules/darwin/macos.nix under CustomUserPreferences; deleting the offending block is the other way out if you don't care about those settings.

home-manager refuses to overwrite an existing file — files it would clobber are backed up with a .bak suffix (backupFileExtension = "bak"). If a switch still fails on a collision, a stale .bak from an earlier run is usually in the way; delete it and re-run.

A cask fails to install because the app already exists — remove the hand-installed copy from /Applications and re-run the switch.

warning: failed to generate _<tool> during activation — that tool is not installed (see step 10). Harmless; only its completions are missing.

A nixpkgs package suddenly fails to build — check flake.lock. Pinned overrides live in modules/darwin/default.nix (for example WhatsApp's version and hash).