Files
nixos_flake_config/CLAUDE.md
2026-05-31 11:56:52 +02:00

4.8 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository purpose

Personal NixOS configuration managing multiple hosts via a single flake. Each host is a separate nixosConfiguration output in flake.nix. Home Manager is wired in as a NixOS module per host. Upstream is nixos-25.11.

Hosts

Defined in flake.nix under nixosConfigurations:

  • fuji — x86_64 laptop, Plasma6 + Sway, intel 13th gen, dual wireguard (one in a network namespace for ProtonVPN routed via dnscrypt-proxy)
  • nixy — x86_64 workstation (also builds the nixy_iso installer image)
  • mediabox — x86_64 media server (jellyfin/qbittorrent; uses the local modules/qbittorrent.nix)
  • blue — x86_64 (no sops-nix)
  • magpieaarch64, qemu guest, runs simple-nixos-mailserver; the wireguard hub other hosts dial into

When adding a new host, follow the pattern in flake.nix: include common/packages.nix, common/suspend.nix, the host's configuration.nix + hardware-configuration.nix, sops-nix.nixosModules.sops (if secrets needed), and a home-manager block pointing at home/<host>/home.nix.

Common commands

# Rebuild the current host (uses hostname to select config)
sudo nixos-rebuild switch --flake .#

# Rebuild a specific host
sudo nixos-rebuild switch --flake .#fuji

# Test without making the new generation the default boot entry
sudo nixos-rebuild test --flake .#fuji

# Build the nixy installer ISO
nix build .#nixosConfigurations.nixy_iso.config.system.build.isoImage

# Update a single flake input
nix flake update nixpkgs

# Format Nix files (formatter is alejandra)
nix fmt

# Enter the dev shell (sops, ssh-to-age, age available)
nix develop

# Cross-build magpie (aarch64) from x86_64 — requires binfmt or remote builder
nix build .#nixosConfigurations.magpie.config.system.build.toplevel

fuji has boot.binfmt.emulatedSystems for wasm32-wasi + x86_64-windows but not aarch64, so building magpie locally from fuji needs a remote builder or adding aarch64 to that list.

Directory layout

  • <host>/configuration.nix + <host>/hardware-configuration.nix — per-host NixOS config
  • <host>/secrets/*.yaml — sops-encrypted secrets, decrypted at activation via the host's SSH host key (/etc/ssh/ssh_host_ed25519_key → age)
  • common/ — modules imported by every host (packages.nix, suspend.nix) plus shared wireguard pubkeys and common/secrets/ for cross-host secrets like the wireguard preshared key
  • home/<host>/home.nix — entry point for that host's home-manager config
  • home/common/ — shared home-manager modules (zsh, sway, i3, i3status-rust, firefox, etc.) imported from per-host home.nix
  • modules/ — local NixOS modules not yet upstream-ready (currently qbittorrent.nix, nextcloud.nix)
  • packages/ — derivations for packages built locally (bubblewrap, viber)

Architectural notes worth knowing before editing

Inputs are passed as _module.args to every module. That means configuration.nix files receive nvim, zremap, swaysw, system, etc. as function arguments — they aren't imported explicitly. When you see an unfamiliar identifier in a host module's arg list, check flake.nix inputs.

sops-nix wiring. Secrets decrypt using the host's SSH ed25519 host key converted to age. Each host's sops.secrets.<name> references either ./secrets/<file>.yaml (host-local) or ../common/secrets/<file>.yaml (shared). config.sops.secrets.<name>.path is the runtime decrypted path — pass it to systemd units, never read the file at eval time.

Fuji's split-tunnel ProtonVPN. fuji/configuration.nix builds a wg network namespace, brings up proton_wg inside it, and runs dnscrypt-proxy_proton bound to that namespace. Anything that should egress over Proton must be launched with ip netns exec wg .... The main-host wg0 interface is unrelated and connects to magpie for the personal mesh (10.100.0.0/24).

Home-manager backup extension. backupFileExtension = "home_backup" is set on most hosts — if a switch fails on file conflicts, look for *.home_backup files in $HOME.

Hardening already in place on fuji (mirror to other hosts when relevant): nftables firewall, scudo allocator, AppArmor, sysctl hardening (kptr_restrict, dmesg_restrict, rp_filter, redirect blocking), sudo.execWheelOnly, firewall.logRefusedConnections, doas, firejail, no coredumps, KillUserProcesses, ro nix store mount, systemd-boot editor disabled.

Editing secrets

nix develop          # gets sops + age + ssh-to-age
sops <host>/secrets/<file>.yaml

.sops.yaml (if present at repo root, otherwise inferred) defines which age keys may decrypt which paths. When adding a new host, derive its age pubkey from the SSH host key with ssh-to-age and add it to the .sops.yaml creation rules before re-encrypting.