Capstone: scope and plan

Updated

July 30, 2026

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.

Important

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 green

Target scope for this chapter

From GAPS.md, implement P0s that unblock structure, typically:

  1. nixosConfigurations.<lab> evaluates and builds
  2. Host modules split (common + role)
  3. Users: interactive HM or system service user + data dir
  4. Firewall baseline + SSH
  5. Placeholder modules for proxy/secrets if not ready (interfaces only)—prefer real stubs that mkEnableOption cleanly

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 passes

Avoid 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 metadata

Keep 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 needed

Lab 1 — Build green host

nix flake show
nix build .#nixosConfigurations.lab.config.system.build.toplevel -L \
  2>&1 | tee ~/lab/nixos/capstone/scope/build.log

Fix until green. Prefer build over switch until config is sane; then:

sudo nixos-rebuild switch --flake .#lab
# or deploy path if already wired

Acceptance: 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 generations or 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 refs

Write ~/lab/nixos/capstone/scope/NOTES.md: what shipped, what slips to implement.


Lab 5 — Smoke

systemctl --failed
hostname
# ssh localhost or from client

Lab 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)

  1. How to rebuild
  2. How to roll back
  3. Where secrets will live
  4. What state to back up
  5. 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 build toplevel green for lab host
  • HM or service user story demoable
  • Modules structured; ≥1 typed custom module
  • CAPSTONE-A.md updated honestly
  • GAPS.md P0s 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.