NixOS flake covering my lil fleet.
  • JavaScript 48%
  • Nix 23.7%
  • CSS 14.7%
  • HTML 8.4%
  • Just 5.2%
Find a file
Danny Roberts f63962dc2c feat(starship): colour each host's prompt name from the palette
One line in the shared Home Manager config rather than an edit per host: the
palette is keyed by machine, so `palette.hosts.${hostname}.ramp.mark` gives
green its spring green and blue its blue from the same expression, and red will
get its Transistor red when it lands.

mark is the role the brand book assigns to a prompt hostname. starship only
shows the hostname over SSH by default, which is exactly when knowing the box
matters -- left alone rather than widened.

Verified by reading the generated starship.toml for blue: `style = "bold
#0093ed"`. green's setting evaluates to bold #05ff74; its file cannot be built
here, being aarch64.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 19:15:02 +01:00
design feat(design): read the palette from Nix 2026-09-07 19:08:54 +01:00
home/danzi feat(starship): colour each host's prompt name from the palette 2026-09-07 19:15:02 +01:00
hosts docs(amber): add install runbook 2026-09-07 12:03:50 +01:00
modules/common fix: pin danzi's GID to 1000 as well as the UID 2026-09-07 11:17:53 +01:00
.gitignore feat: initialise multi-host nixos flake with blue and amber 2026-09-05 23:14:18 +01:00
CLAUDE.md fix(amber): shared ESP with Windows, and switch to GRUB with os-prober 2026-09-07 11:46:38 +01:00
flake.lock feat(green): add Raspberry Pi 5 host via nixos-raspberrypi 2026-09-07 00:20:08 +01:00
flake.nix feat(design): read the palette from Nix 2026-09-07 19:08:54 +01:00
GLOSSARY.md docs(glossary): define ESP 2026-09-07 11:41:32 +01:00
justfile refactor(design): brand book at the index, configurator beside it 2026-09-07 19:04:22 +01:00
README.md feat(design): boot from gamut-colours.json, and add a brand book 2026-09-07 18:56:45 +01:00

gamut

A single NixOS flake covering every machine I run.

Gamut: the complete range of colours a device can reproduce. The hosts are named for colours, so the repo is named for the set of them.

Host Machine Arch Role
red laptop x86_64 main workstation, dual-boots Arch
amber desktop x86_64 heavy/GPU work, NVIDIA, dual-boots Windows, intermittently online
green Raspberry Pi 5 aarch64 NAS, NVMe SSD, always on
blue VPS x86_64 always-on public host

Channel: nixpkgs 26.05 (stable) everywhere. Dotfiles via Home Manager.

Usage

Common tasks are just recipes -- run just on its own to list them.

just nix-switch blue      # apply this config to blue, permanently
just nix-test blue        # apply it only until the next reboot
just rollback blue        # undo the last rebuild
just generations blue     # what you could roll back to
just gc blue              # prune generations older than 30 days
just hs-join              # join THIS machine to the tailnet
just hs-join amber        # join amber to the tailnet, over SSH

The underlying commands, if you want them directly:

# Rebuild the machine you're sitting at
sudo nixos-rebuild switch --flake .#amber

# Try a change without making it the default boot entry
sudo nixos-rebuild test --flake .#amber

# Deploy to another machine over SSH/Tailscale.
# NB: 26.05 uses --sudo; --use-remote-sudo was the pre-25.05 spelling.
nixos-rebuild switch --flake .#blue --target-host danzi@blue --sudo --ask-sudo-password

# Update pinned inputs
nix flake update

If a rebuild breaks the machine, pick the previous generation from the boot menu. Nothing is destroyed by a bad config.

Layout

flake.nix          inputs (pinned) + nixosConfigurations
CLAUDE.md          conventions for working in this repo
GLOSSARY.md        NixOS terminology
hosts/<name>/      per-machine config, and that machine's INSTALL.md
modules/common/    applies to every host
modules/desktop/   niri, greetd, audio, fonts       (Phase 3)
modules/dev/       toolchains                       (Phase 3)
modules/server/    NAS services                     (Phase 4)
home/danzi/        Home Manager -- dotfiles, user packages
design/            colour tools and brand book; plain web pages, outside the flake

Per-host install runbooks live beside the host they describe: hosts/blue/INSTALL.md, hosts/red/INSTALL.md.

Roadmap

Ordered by risk, lowest first -- though green jumped the queue, because the services it hosts are wanted before the desktop is.

  • Phase 0 -- repo skeleton, flake evaluates
  • Phase 1 -- blue (VPS). Installed 2026-09-06 with nixos-anywhere. Headscale 0.28 behind Caddy at https://blue.danzi.gay, Let's Encrypt certificate issued automatically. Took three attempts: missing virtio drivers, then a BIOS->UEFI firmware change on reimage. Both recorded in hosts/blue/INSTALL.md
  • Phase 2 -- green (Pi 5). Installed 2026-09-07 from a running Raspbian, using nixos-raspberrypi because the generic aarch64 image will not boot this board. Forgejo serving at https://git.danzi.gay, proxied from blue over the tailnet. See hosts/green/INSTALL.md
  • Phase 3 -- rest of green's services. NFS share at /srv/share (tailnet + LAN, v4 only) and a restic REST server on :8000, append-only, tailnet-only. No backup client yet -- that needs red on NixOS
  • Phase 4 -- amber (desktop) to a minimal booting box: console + SSH + Tailscale, NVIDIA still commented out. See hosts/amber/INSTALL.md. Note sdb1 is an ESP shared with Windows -- same trap as red
  • Phase 5 -- the real amber: NVIDIA, niri desktop, toolchains. Factor shared parts into modules/desktop and modules/dev
  • Phase 6 -- red (laptop), following hosts/red/INSTALL.md. Highest risk: shared 1 GiB ESP with Arch
  • Later -- Forgejo Actions runner on amber for CI; photo hosting (Immich needs 6 GB RAM, so not on a 2 GB Pi)

Considered and parked

Tangled (https://tangled.org) instead of, or alongside, Forgejo. Some of my peers already use it. Investigated 2026-09-07, left alone for now.

Tangled splits what Forgejo does in one piece: a self-hosted knot holds bare repositories, while identity, issues and pull requests live on AT Protocol and the web UI is tangled.org's appview. The appeal is that collaborators contribute under their own identity, so I never become their account provider, and their contribution history survives my hardware.

Why not now:

  • Requires an atproto identity. I use the fediverse via tech.lgbt, which is ActivityPub -- a separate network, no bridge. Being Bluesky-free means self-hosting a PDS as well as the knot, and possibly still relying on Bluesky's PLC directory unless did:web works end to end (unverified).
  • Not in nixpkgs. Would add a second third-party flake input (after nixos-raspberrypi), so green's rebuilds depend on two external repos.
  • A knot needs to be publicly reachable for both HTTP (port 5555, which blue's Caddy could proxy) and SSH git operations (port 2222). green is behind NAT and Caddy only proxies HTTP, so pushing needs a router forward or a TCP proxy on blue.
  • For a handful of peers, a Forgejo account each takes thirty seconds.

The middle path worth revisiting first: use tangled.org's hosted knot with danzi.gay as the handle, keep green canonical, and push to both remotes. That gets the collaboration with nothing new running here. The open question is whether tangled accepts a did:web handle or effectively requires a did:plc in Bluesky's directory -- that decides whether it is genuinely Bluesky-free.

Secrets (sops-nix) deferred until something actually needs one.

Known rough edges

  • just nix-switch green occasionally fails at activation with sudo: no password was provided, after a long build. Re-running works. Cause not established -- nixos-rebuild pipes the password rather than relying on sudo's timestamp, so a build taking a while should not matter.
  • Forgejo's SSH clone URLs are tailnet-only (SSH_DOMAIN = green.tail.internal). Caddy proxies HTTP, not SSH, so outside collaborators must clone over HTTPS.
  • blue's Caddy points at 100.64.0.2, green's headscale-assigned address, rather than a name. If green ever re-registers and changes address, that line in hosts/blue/caddy.nix needs updating.
  • MagicDNS resolves full names only. headscale runs with override_local_dns = false, so green.tail.internal works but the search domain is not pushed and bare green does not resolve for most tools. That was the right call for a laptop that roams; the cost is using FQDNs.
  • default = "saved" on amber's GRUB is unverified. GRUB needs savedefault in the entry and it is not established that NixOS emits it for os-prober entries. If Windows does not stay the default after being chosen, pin it by index or title from the generated /boot/grub/grub.cfg.

Things to remember when building the clients

  • NFS client support is not automatic. Mounting green:/srv/share needs the mount.nfs4 helper, not just a kernel that speaks NFS -- without it mount fails with the misleading "mount program didn't pass remote address". On Arch that was pacman -S nfs-utils; on red and amber it needs the equivalent in their NixOS config. Worth putting in modules/common once confirmed, since every client wants it.
  • The restic backup client is still unwritten. green is only the target. Once red is on NixOS: services.restic.backups.<name> pointing at http://green.tail.internal:8000/, backing up ~/Projects and ~/Rocketry (and ~/.ssh is worth considering). The repository password is a real secret, and is the thing that finally justifies setting up sops-nix.
  • restic forget/prune must run on green, not the client. appendOnly is deliberate and blocks the client from deleting anything, so retention is a job on green with the service briefly stopped.
  • Desktop work in progress: the quickshell-based desktop environment is being designed separately (colour palettes and theming first, outside quickshell). It lands here as Home Manager config under home/danzi/ once it settles.