SDLC & Minimal Policies#
A software development life-cycle and the smallest set of policies that make it real: for a solo practice whose production system is a family.
Read this as written
This is deliberately informal, yet formal enough. I write security and compliance frameworks for a living, and I know the difference between governance and governance-pretend. Everything below is a control I actually run, phrased at the altitude a one-person shop can sustain. If a line here ever stops matching what the repository does, the repository wins: fix the doc.
1. Purpose & Scope#
This document governs this flake — tsunaminoai/nix-flake-final,
the single Nix configuration that builds and operates every computer, server,
and gadget in the household and at the family sites (Lafayette and Mom &
Pop’s place in Bedford).
It is scoped to this repository because this is the one repo I maintain where a mistake doesn’t just cost me — it takes down the family’s Wi-Fi, DNS, documents, Home Assistant, and phones. That production obligation is what earns the extra formality.
In scope: changes to NixOS / nix-darwin / home-manager configuration, modules, packages, secrets, and the documentation in this repo, and their deployment to the live fleet.
Out of scope (governed only informally): my other repositories (see §2). They carry no family-availability obligation, so they run on the spirit of this SDLC without the ceremony.
2. The Repo Constellation#
This SDLC is written for this flake, but it’s informed by how I actually work and on the following:
| Group | Repositories | Availability obligation |
|---|---|---|
| This homelab | nix-flake-final |
High — the family depends on it daily |
| GRC / compliance work | corp, praxis, PolicyPress (private, sc2in), security-center, internal-grc-system, ai-compliance, scf-api |
Professional — governed by their own controls |
| Zig experiments & forks | atlas, clay-zig, tomlz, zig-time, zcloc, md4c, ggtag, haxo-rs, blackstripes, game-boy, tinyradio, lastchoice, and my build-system branches on DOOM, nmap, gnuradio, FluidX3D, CM1 |
None — pure R&D |
| Web / creative | falseblue.com, craton.net, sc2.in, liturgy-latex, intaglio, ukiyoe, anikifm |
Low — mostly static on Cloudflare |
Why only this one is ‘production’
Most of my repos are experiments, forks I’m porting to a Zig build, or content sites. This flake is the only one with live human dependents who are not me. Proportionality (below) is the whole point: match ceremony to blast radius, and this is the repo with the biggest blast radius at home.
3. Guiding Principles#
- Proportionality. Formality scales with blast radius. A change to my prompt theme is not a change to the household DNS resolver. Tier your changes (§7) and spend rigor where a failure hurts the family.
- Determinism & reproducibility. Everything is Nix. A host can be rebuilt from scratch and come back identical. This is the primary safety control — it makes every change reversible and every rollback boring.
- Everything-as-code, one source of truth. Configuration, secrets (sealed), documentation, compliance controls, and topology all live in this repo. If it isn’t in the flake, it isn’t real (and if it must live outside — cloudflared on voile, HAOS on the appliance — that exception is written down).
- Nothing lands unproven. No configuration reaches
masterwithout passing automated evaluation, and nothing family-critical reaches the fleet without building and, where applicable, passing the compliance VM test. - Family-first operations. Deploys happen inside sane change windows, not during dinner, homework, or a movie. The people are the SLA.
- Honest self-governance. I am the developer, the reviewer, the approver, and the operator. I don’t pretend that’s segregation of duties — I name it as a risk and lean on automation as the compensating control (§5).
4. The Life Cycle#
The flow is intentionally small: branch → prove → merge → tag → converge → operate.
4.1 Plan / Intake#
Work starts as a GitHub issue (or a note in project memory for smaller things). Classify the change’s tier (§7) before touching code — that decides how much of the rest of this section applies.
4.2 Develop#
- Cut a branch off
master; aspire to never commit directly tomaster. nix developgives the pinned toolchain; treefmt and pre-commit hooks (git-hooks.nix) format and lint on commit.just preflightlocally before pushing when the change is non-trivial.- Commits are conventional-ish and signed where the signing key is available (see the known travel/hardware-key caveats in project memory).
4.3 Verify (CI is the reviewer I don’t have)#
Two-speed gating, by design (see .github/workflows/):
| Gate | Workflow | Trigger | What it does |
|---|---|---|---|
| Fast eval | eval.yml |
every branch push | nix flake check --no-build — catches eval errors, dangling refs, bad option refs. Cheap, quick feedback. |
| Full build | checks.yml |
PR to master and the master push after merge |
Full nix flake check that builds: the compliance VM test, per-host nginx -t, all host toplevels. Also re-runs on the merge commit to catch two-green-PRs-merge-red. |
| CVE / SBOM | cve.yml |
manual (workflow_dispatch) |
vulnix scan + sbomnix SBOM; flags CVSS > 7.0 findings. Currently manual — see §6. |
| Docs | docs.yml |
master push touching docs/modules/lib |
Builds .#docs, publishes to docs.falseblue.com (Cloudflare Pages). |
4.4 Review & Merge#
- Open a PR into
master. Even solo, the PR is the record: the diff, the CI result, and my reasoning in one place. - Run the self-review checklist honestly.
- Merge only when the full build gate is green. Squash-merge to keep
masterlinear.
4.5 Release#
- A push to
masterthat changesflake.locktriggersrelease.yml: it cuts a CalVer tag (vYYYY.MM.DD[.n]) and fast-forwards thereleasebranch. releaseis the moving pointer the fleet follows — not everymastercommit ships automatically; the release branch is the deliberate ship line.
4.6 Deploy / Converge#
- Hosts follow
releaseviasystem.autoUpgrade; interactive/targeted pushes use deploy-rs. - Stage family-critical changes: prove on a low-stakes host (e.g.
mokou) first, then converge the rest. - Respect the change window. Announce anything that could interrupt Wi-Fi, DNS, or Home Assistant.
4.7 Operate & Monitor#
- Telemetry (VictoriaMetrics/Logs + Grafana + Alloy) and the public status pages watch availability.
- Runbooks (
runbooks/) and the emergency guide cover the known failure modes and SPOFs (e.g. the HA Green triple-SPOF).
4.8 Rollback#
- Every deploy is a Nix generation:
nixos-rebuild --rollback/ boot the previous generation is the first move. - Reverting the
releasepointer or the offending PR restores the fleet’s target. - Rollback is expected and cheap, not a failure event. Reach for it early.
5. Roles (honest about being one person)#
| Role | Who | How the conflict is managed |
|---|---|---|
| Developer | Ben | — |
| Reviewer / Approver | Ben | Automation is the second set of eyes. No merge without green CI; the self-review checklist is mandatory, not optional. |
| Operator | Ben | Staged rollout + rollback-by-default limit operator error. |
| Users / stakeholders | The family | Their experience is the acceptance test. “Take a photo and text Ben” is the highest level incident-report channel. |
Named limitation: no segregation of duties
A solo practice cannot separate author from approver. I don’t paper over it. The compensating controls are: deterministic builds, mandatory automated gates, staged rollout, trivially cheap rollback, and everything-as-code so any change is auditable after the fact.
6. Minimal Policies#
The smallest set worth writing down. Each is intent → control → evidence.
6.1 Change Management#
- Intent: no unproven change reaches the family’s systems.
- Control: branch → PR → green full-build gate → squash-merge →
releasetag. Direct pushes tomasterare (almost) never done. - Evidence: PR history, CI run logs (artifacts retained 30 days), CalVer tags.
6.2 Secrets Management#
- Intent: no plaintext secret in the repo or the Nix store.
- Control: sops-nix with age keys; policy in
.sops.yaml. Secrets decrypt to/run/secrets(never/var/run/*— that path taint kills networking; see project memory). - Evidence:
secrets.yamlis ciphertext;.sops.yamlrecipient list; per-host key rotation viajust sops-updatekeys.
6.3 Vulnerability Management#
- Intent: know what’s in the closure and whether it’s vulnerable.
- Control:
cve.ymlruns vulnix + sbomnix; findings with CVSS > 7.0 are surfaced. Accepted findings go inwhitelist.tomlwith a written justification — an exception must be reasoned, not silent. - Evidence:
vulnix.json,critical_cves.json,sbom.json,whitelist.toml. -
Known gap
cve.ymlis currently manual (workflow_dispatch); the push/PR triggers are commented out. Re-enabling scheduled scanning is a tracked improvement, not the current state. Don’t claim continuous scanning until it’s on.
6.4 Compliance-as-Code#
- Intent: security controls are enforced by the build, not by good intentions.
- Control: the SCF-driven compliance layer (
modules/compliance/) applies Secure Controls Framework controls as NixOS modules and attests them inside a NixOS VM test that runs in the full-build gate. Managed via thescf-add-control,scf-generate, andscf-checkworkflows. - Evidence: the
compliance.evidenceattestation trail and the rendered attestation report. See Compliance.
6.5 Access Control & Third-Party Inputs#
- Intent: least privilege for both people and machine-to-machine trust.
- Control: service exposure is Tailscale / Cloudflare-Access gated; the private
sc2ininputs (corp→praxis,PolicyPress) are fetched overgit+sshusing a dedicated machine-account key (SC2_CI_SSH_KEY), not a personal credential. - Evidence: CI SSH setup steps; registry wiring in
modules/flake/nix/.
6.6 Backup & Recovery#
- Intent: the family’s data and the ability to rebuild survive a host loss.
- Control: Borg/BorgWarehouse backups; Nix makes the systems reproducible so recovery is rebuild-plus-restore-data, not reinstall-from-memory.
- Evidence: backup schedules and repos documented in maintenance and the runbooks; retention noted in project memory.
6.7 Documentation#
- Intent: anyone in the family can act without me; future-me can understand past-me.
- Control: docs live in-repo and auto-publish to docs.falseblue.com; the family-facing guides are written for non-technical readers.
- Evidence: this docs site;
docs.yml.
6.8 Licensing & Non-Commercial Use#
- Intent: the license reflects that this is personal, non-commercial research.
- Control: Apache 2.0, no warranty, commercial users assume all liability
(see
README). - Evidence:
LICENSE,README.md.
7. Change Tiers#
Proportionality made concrete. Tier the change before you start.
| Tier | Examples | Required rigor |
|---|---|---|
| T1 — Family-critical | DNS, DHCP, Wi-Fi/RADIUS, Home Assistant, Paperless/documents, PKI/CA, the exit node, backups | Full gate + compliance VM green; stage on a non-critical host first; announce; deploy in a change window; rollback plan ready. |
| T2 — Household service | Jellyfin, Grafana, dashboards, scanners, non-critical containers | Full gate green; deploy off-window; watch telemetry after. |
| T3 — Personal / cosmetic | my desktop theme, shell, editor, dev tooling, docs wording | Fast eval green is enough; deploy anytime to my own machines. |
When unsure which tier, round up.
8. Self-Review Checklist#
Run before merging anything above T3:
- Change tier identified; rigor matches (§7).
- Fast eval gate green; for T1/T2, full build gate green (compliance VM included where applicable).
- No new plaintext secret; anything sensitive went through sops.
- Blast radius understood — what breaks for the family if this is wrong?
- Are you pushing to master (eg will it affect any non-tsunami users if a rebuild is triggered)?
- Rollback path is clear (which generation / which revert).
- SPOFs and known-fragile areas (HA Green, exit node, octopi Wi-Fi) considered.
- Docs / runbooks updated if behavior the family sees changed.
- Deploy timing respects the change window.
9. Records & Evidence#
Where the audit trail actually lives — no separate GRC tool needed:
- Git history & PRs — every change, its reasoning, and its review.
- CI artifacts — flake-check / eval logs (30 days), CVE/SBOM results (90 days).
- CalVer tags &
releasebranch — what shipped and when. - Compliance attestation —
compliance.evidence+ rendered report. - Project memory — the non-obvious operational context between sessions.
10. Review Cadence#
This is a living document. Review it:
- Quarterly, lightly — does it still describe what I actually do?
- On any material change to CI, the release flow, the secrets model, or the compliance layer.
- After any family-impacting incident — fold the lesson into a policy or a runbook so it can’t recur silently.
Last aligned with the repo: 2026-07-21.