Capstone: scope and plan
Capstone: scope and plan
Goal: Close the highest P0 gaps from the architecture review so the Capstone A skeleton is real: flake host role, module layout, HM or service users, and a live checklist that tracks proof—not vibes.
Capstone A (required): flake host · HM or service users · sops/agenix · reverse proxy · deploy-rs/colmena · nixosTest · CI + cache.
Later Capstone chapters implement secrets/proxy, harden deploy/CI, prove rebuild, optional freeze, DR wipe, rollback, and retrospective.
Why this chapter exists
Architecture review without commits is stationery. This chapter turns GAPS.md P0s into code on a branch, with rebuilds you can demonstrate.
Theory — Working agreements for Capstone
| Rule | Why |
|---|---|
| One change → build → commit | Bisectable; safer switch |
| Lab VM first | Protect daily driver |
| Checklist is law | CAPSTONE-A.md updated same session |
| No drive-by rewrites | Freeze before DR |
| Secrets never in git plaintext | Even “temporary” |
Suggested repo shape (adapt, don’t dogma)
flake.nix
flake.lock
hosts/<name>/configuration.nix
modules/{proxy,common,users,…}.nix
homes/<user>.nix # if HM
secrets/secrets.yaml # sops-encrypted
tests/*.nix
deploy.nix or colmena hive
.github/workflows/ci.yml
docs/{ARCHITECTURE,CAPSTONE-A,RUNBOOK,GAPS}.md
Capstone A checklist (live)
Copy into repo if missing; tick as you go:
# CAPSTONE-A.md
- [ ] Host role rebuilds from flake
- [ ] HM user OR service user story
- [ ] sops-nix OR agenix wired; service reads secret
- [ ] Reverse proxy edge with TLS or lab HTTP mode documented
- [ ] deploy-rs OR colmena path works from admin machine
- [ ] nixosTest/check catches a real regression
- [ ] CI builds toplevel (and checks)
- [ ] CI pushes to binary cache (preferred)
- [ ] ARCHITECTURE.md current
- [ ] RUNBOOK.md for deploy/rollback
- [ ] Disaster recovery: wipe + rebuild timed
- [ ] Rollback drill + suite greenTarget scope for this chapter
From GAPS.md, implement P0s that unblock structure, typically:
nixosConfigurations.<lab>evaluates and builds
- Host modules split (common + role)
- Users: interactive HM or system service user + data dir
- Firewall baseline + SSH
- Placeholder modules for proxy/secrets if not ready (interfaces only)—prefer real stubs that
mkEnableOptioncleanly
Defer full proxy TLS and CI cache to implement/harden chapters if needed—but leave option shapes ready.
Explicit non-goals (this chapter)
| Non-goal | Why |
|---|---|
| Multi-node mesh | Optional bar later |
| Perfect ACME | Document lab mode later |
| Full observability stack | P2+ |
| CA store experiments | Internals literacy only |
Worked example — Minimal host flake skeleton
# flake.nix (sketch)
{
description = "capstone lab";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# home-manager.url = "github:nix-community/home-manager/release-26.05";
# home-manager.inputs.nixpkgs.follows = "nixpkgs";
# sops-nix.url = "github:Mic92/sops-nix";
# sops-nix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, ... }@inputs: {
nixosConfigurations.lab = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs; };
modules = [
./hosts/lab/configuration.nix
./modules/common.nix
# inputs.sops-nix.nixosModules.sops
# inputs.home-manager.nixosModules.home-manager
];
};
};
}# modules/common.nix
{ lib, pkgs, ... }:
{
time.timeZone = "UTC";
networking.firewall.enable = true;
services.openssh.enable = true;
environment.systemPackages = with pkgs; [ vim git curl ];
}# hosts/lab/configuration.nix
{ config, pkgs, ... }:
{
imports = [ ./hardware.nix ];
networking.hostName = "lab";
# users / HM / roles
system.stateVersion = "26.05"; # your real value
}Theory — Capstone A success definition (this chapter)
| Done | Not done |
|---|---|
| Green host from flake | Perfect theming |
| Users + SSH story | Every optional service |
| Module hygiene applied | Multi-region fleet |
| Architecture doc updated | Final polish |
Scope control is part of the grade.
Theory — Branch discipline
git checkout -b capstone-a
# work
# merge only when smoke passesAvoid committing broken main mid-capstone if you use the same repo for the lab host.
Worked example — smoke script
#!/usr/bin/env bash
set -euo pipefail
hostname
systemctl is-active sshd
curl -sf -o /dev/null -w '%{http_code}\n' http://127.0.0.1:… || true
nix flake metadataKeep it boring; run after every significant rebuild.
Rebuild proof steps (skeleton → switch)
| Step | Command | Pass means |
|---|---|---|
| 1 | nix flake show |
Host attr visible |
| 2 | nix build .#nixosConfigurations.lab.config.system.build.toplevel -L |
Exit 0 |
| 3 | nixos-rebuild dry-activate --flake .#lab |
No activation surprises |
| 4 | sudo nixos-rebuild switch --flake .#lab (lab only) |
SSH still works |
| 5 | systemctl --failed |
Empty or known waivers |
Lab 0 — Branch & checklist
cd /path/to/capstone-flake
git checkout -b capstone-a
mkdir -p docs ~/lab/nixos/capstone/scope
# seed docs/CAPSTONE-A.md from architecture review if neededLab 1 — Build green host
nix flake show
nix build .#nixosConfigurations.lab.config.system.build.toplevel -L \
2>&1 | tee ~/lab/nixos/capstone/scope/build.logFix until green. Prefer build over switch until config is sane; then:
sudo nixos-rebuild switch --flake .#lab
# or deploy path if already wiredAcceptance: toplevel green; hostname/SSH story demoable.
Lab 2 — Users story
Track HM (workstation-ish)
- Enable HM as NixOS module
- Manage shell + one dotfile
- Prove with
home-manager generationsor files in place
Track service users (server-ish)
users.users.myapp = {
isSystemUser = true;
group = "myapp";
home = "/var/lib/myapp";
createHome = true;
};
users.groups.myapp = { };Document which track in docs/CAPSTONE-A.md.
Lab 3 — Module hygiene
Ensure at least one custom module uses:
- typed options
mkEnableOption
mkIf
- one assertion
Even if proxy is stubbed:
options.my.capstone.proxy.enable = lib.mkEnableOption "edge proxy";Lab 4 — Update architecture & gaps
# edit docs/ARCHITECTURE.md paths if moved
# mark GAPS.md items closed with PR/commit refsWrite ~/lab/nixos/capstone/scope/NOTES.md: what shipped, what slips to implement.
Lab 5 — Smoke
systemctl --failed
hostname
# ssh localhost or from clientLab 6 — Gap register freeze for next chapter
Write docs/capstone-gaps.md with only items that remain for implement/harden. Anything critical for a bootable admin host must be closed now.
Lab 7 — Teach-back outline (5 bullets)
- How to rebuild
- How to roll back
- Where secrets will live
- What state to back up
- What implement will add
Failure modes
| Symptom | Fix |
|---|---|
| Hardware config missing | Import hardware or disko |
| Infinite recursion after split | Option cycles; evaluator chapter |
| HM + system package duplication | Pick layers deliberately |
| Switching daily driver | Stop; use VM |
| Checklist not updated | Same commit as code |
Common gotchas
| Symptom | What to do |
|---|---|
| Hardware config missing in flake | Import hardware-configuration.nix or disko |
| Infinite recursion after module split | Dependency cycles |
| HM + system package duplication | Pick layers deliberately |
| Switching daily driver | Stop; use VM |
| Checklist not updated | Same commit as code |
Checkpoint
nix buildtoplevel green for lab host
- HM or service user story demoable
- Modules structured; ≥1 typed custom module
CAPSTONE-A.mdupdated honestly
GAPS.mdP0s reduced or re-dated
- Commits on capstone branch
Commit
git add .
git commit -m "capstone: host skeleton and user story"Write three personal gotchas before continuing.
Next
→ Capstone: implement — secrets E2E and reverse-proxy front door.