meta: add CLAUDE.md
This commit is contained in:
80
CLAUDE.md
Normal file
80
CLAUDE.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# 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)
|
||||||
|
- `magpie` — **aarch64**, 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
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 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
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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.
|
||||||
Reference in New Issue
Block a user