Day 32 — Stage III gate

Updated

July 30, 2026

Day 32 — Stage III gate

Stage III · ~4h
Goal: Prove the Stage III exit criteria: a workstation/server skeleton where host + Home Manager + project/devShell workflow + flake checks live in one coherent flake layout—and a new machine of the same role is “clone + hardware + rebuild.”

Important

Gates are not content cram days. They are proof. If something is missing, fix it here before Stage IV secrets and services multiply the mess.

Why this day exists

Stage III introduced structure without which Stage IV becomes dangerous:

Stage III skill Stage IV needs it because…
hosts/modules/homes Secrets modules attach cleanly
Lock discipline New inputs (sops-nix) must pin sanely
HM as NixOS module User + system activate together
direnv/devShell Service dev doesn’t bloat system
checks Catch eval breakage before deploy
light overlays Occasional pin without chaos

Today you demonstrate, not skim blog posts.


Theory 1 — Official exit criteria (from syllabus)

New machine of same role = clone flake + hardware config + rebuild.

Operationalization:

  1. Flake is the source of truth (no critical imperative snowflakes)
  2. Layout is navigable by someone else (or future you)
  3. User environment is HM-managed for shell/git basics
  4. Project tools are not only global profiles
  5. nix flake check (your subset) passes
  6. Rebuild from cold boot still works; rollback still works

Theory 2 — Evidence pack (what “done” looks like)

Prepare a short evidence directory (journal or docs/stage-iii-evidence.md):

# Stage III evidence

## Tree
(paste tree -L 3)

## Commands
- nix flake show
- nix flake check
- nixos-rebuild switch --flake .#lab
- direnv / nix develop proof
- home-manager user programs proof

## Pins
- nixpkgs rev: …
- home-manager rev: …

## Known debt
- 

Theory 3 — Role skeleton checklist

Host (hosts/lab)

  • Hostname set
  • Hardware imported
  • Common modules imported
  • SSH keys-only access still true
  • Firewall stance known (even if simple)
  • system.stateVersion set

Modules

  • Common policy split (users/ssh/nix/…)
  • At least one custom module still present from Stage II
  • Overlay wiring intentional

Homes

  • HM NixOS module wired
  • User programs: shell + git
  • At least one managed file or XDG config
  • Optional user service still present

Flake outputs

  • nixosConfigurations.<host>
  • devShells or project-flake workflow documented
  • checks nonempty and passing
  • formatter optional but nice
  • Style doc (flake-parts vs hand-rolled)

Theory 4 — Clone drill (mental or real)

Ideal proof on a second VM:

git clone <your-flake-url> lab && cd lab
# generate or copy hardware-configuration for THIS machine
sudo nixos-rebuild switch --flake .#lab

If you lack a second VM today: walk the steps dry and list what would differ (disk UUIDs, hostname, network).


Theory 5 — Debt register

Unfinished work is allowed only if listed:

Debt Stage to pay
Secrets still plaintext IV (33–35) urgently
No format check Fix today if <1h
Theming rabbit hole Never required
Multi-host Later stages

Do not enter Stage IV with “I will structure the flake later.”


Lab 1 — Tree and show

cd /path/to/your-flake
find . -type f \( -name '*.nix' -o -name '*.md' -o -name '.envrc' \) | sort
nix flake metadata
nix flake show

Paste into evidence doc.


Lab 2 — Full rebuild proof

sudo nixos-rebuild switch --flake .#lab

Verify:

hostname
systemctl is-active sshd
git config --global --get user.name || true
command -v direnv

Lab 3 — Checks pass

nix flake check -L

Fix failures before continuing. If toplevel check is too heavy, ensure documented lighter checks still pass.


Lab 4 — DevShell / direnv proof

cd ~/lab/90daysofx/02-nixos/day28/demo-app  # or your project
# show tools available via direnv or nix develop
nix develop -c true

Lab 5 — Rollback still works (confidence)

# list generations
sudo nix-env -p /nix/var/nix/profiles/system --list-generations | tail

You do not need to break the system today if already proven in Stage II—but confirm you still know the boot menu / switch --rollback path. Write the commands in evidence.


Lab 6 — Peer-style review of your own flake

Answer in writing:

  1. Where do I add a second host?
  2. Where do I add a system service module?
  3. Where do I add a user package?
  4. How do I update only nixpkgs?
  5. What is still imperative on this machine?

If any answer is “I don’t know,” fix docs or layout now.


Lab 7 — Gate commit and tag (optional)

git add .
git commit -m "day32: stage III gate — host+HM+shell+checks"
git tag -a stage-iii-gate -m "Stage III exit"

Tags help Stage VIII capstone archaeology.



Theory 6 — Stage III → IV interface contract

Stage IV will attach:

Concern Expected hook in your flake
sops-nix / agenix module import + secrets files outside store plaintext
reverse proxy modules/services/ style
data service state dirs + secrets paths
firewall matrix docs/ports.md living doc

If your layout has nowhere obvious to put those, redesign today.


Theory 7 — Pin coherence check

nix flake metadata
# home-manager should follow nixpkgs when you set follows

Mismatched HM release vs nixpkgs (e.g. HM 25.11 on nixpkgs 26.05) is a classic eval footgun. Align release-26.05 tracks.

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";
};

Worked example — Minimal checks still count

checks.${system}.hostname = pkgs.runCommand "check-hostname" { } ''
  # example pattern only — prefer real eval checks you already have
  echo ok > $out
'';

Better: evaluate that nixosConfigurations.lab exists and maybe that config.networking.hostName matches expectation (as you did in Day 30). Gate requires passing checks, not toy checks that ignore the host.


Lab 8 — HM vs system package boundary audit

List five tools and where they live:

Tool systemPackages HM home.packages devShell
git ? ? ?

Prefer: system or HM for daily drivers; devShell for language toolchains.


Lab 9 — flake.lock age honesty

git log -1 --format=%ci -- flake.lock

If the lock is months stale on a public-facing host, schedule an update session—not a blind full update on this gate day unless you have time to smoke-test.

Common gotchas

Symptom / mistake What to do
Gate day used to learn HM from zero Return to Day 25; don’t fake the gate
Checks failing “but switch works” Fix checks—CI will not care about switch on your laptop
Evidence only in head Write the evidence file
Second host impossible today Dry-run clone steps still required
Secret debt ignored Escalate to Day 33 first thing next stage
Layout relapsed into monolith Re-split before Stage IV

Checkpoint — Stage III exit

  • Layout: hosts / modules / homes
  • Lock policy exists; lock committed
  • HM as NixOS module; shell + git managed
  • Files/XDG or programs configs real
  • direnv/devShell workflow works
  • Flake style chosen and documented
  • nix flake check passes
  • Light overlay optional but understood
  • Evidence doc written
  • Clone + hardware + rebuild story clear

Commit

git add .
git commit -m "day32: stage III gate"

Write three personal gotchas before continuing.


Tomorrow

Day 33 — Secret problem: why strings in Nix are dangerous, how the store leaks “secrets,” and what patterns Stage IV will replace.