Linear: ENG-464
Summary #
A NixOS config that turns the VM spindle-exec-1 into a spindle executor that dials out to the mill.
executor/flake.nix: nixpkgs nixos-26.05 (fa13731, the same as the bootstrap) and Tangledv1.20.0(locked at2096d06d). OutputnixosConfigurations.executor.services.tangled.spindle:- role
executor, connecting towss://spindle.demi.dev/mill; - the token is read from
/var/lib/secrets/spindle-executor.token(LoadCredential), and the S3 credentials from/var/lib/secrets/spindle-executor.env; - nixery engine: workflow timeout 15m, 4 workflows at once, 6144 MiB per job;
- seats 4, labels
linuxandx86_64; - artifact store
s3. The bucket name is a placeholder until ENG-465.
- role
- Docker logs rotate at 10 MiB × 3. Each week a timer prunes unused Docker data older than 7 days and collects Nix garbage older than 14 days.
- nftables blocks CI jobs on
docker0andbr-*from reaching RFC 1918, link-local and ULA addresses. DNS to1.1.1.1and the internet stay reachable. - SSH is key-only, uses the lead key, and is only reachable from RFC 1918 sources.
hardware-configuration.nixkeeps the bootstrap's boot setup: GRUB on/dev/vda, the ext4 root labellednixoswith growPartition, the qemu guest agent and thettyS0console.
Verify #
$ mise run check
hadolint: no files yet
all checks passed!
/nix/store/cw7cjgqzmbgja8lndhsbvnza1mwzm3cq-nixos-system-spindle-exec-1-26.05.20261031.fa13731.drv
check_exit=0
$ nix eval (selected fields)
{"docker":true,"mill":"wss://spindle.demi.dev/mill","nft":true,"role":"executor","ssh22open":false,"timeout":"15m"}
The secret-pattern grep over the diff has 0 hits. The lane is local-only until the S3 smoke issue, and there is no CI yet.
Deviations #
- DNS is pinned to
1.1.1.1instead of the LAN resolver, so the firewall needs no hole into the LAN. Follow-up: ENG-474. - SSH is allowed from all RFC 1918 ranges, because the exact LAN subnet wasn't specified.
- ICMPv6 neighbor discovery is allowed on Docker bridges; without it, public IPv6 egress breaks.
Review #
Two rounds of pneumo-reviewer, both done on the local branch before this PR could open:
- round 1: REQUEST_CHANGES, for an out-of-scope README section;
- round 2: APPROVE.
Both are posted as comments. The deploy checks are tracked in ENG-468.
🤖 Generated with Claude Code