Build guards — the fix-forward catalog

The discipline that keeps bugs from recurring — every fix lands upstream, gets a regression test, and a catalog entry. The full G1–G19 index.

Build guards — the fix-forward catalog

The build guards are HostSSH's institutional memory of every bug that mattered. The rule is simple and absolute: when we hit a real defect, we don't patch the symptom on one box — we fix it at the most upstream layer, add a test so it can't silently return, and record it here.

The authoritative catalog (symptom → root cause → forward fix → guard/test, with full detail) lives at planning/BUILD-GUARDS.md. This page explains the discipline and indexes the guards for developers. The user-facing symptom→fix version is docs/user/troubleshooting.md.


The discipline

For every bug we hit:

  1. Fix at the most upstream layer that makes it automatic for every app — the HostPack build, the agent deploy/run defaults, the control-plane deploy pipeline, or a shared template — not per-app or per-box.
  2. Add a regression test so it cannot silently return.
  3. Add a catalog row (symptom → root cause → forward fix → guard/test).
  4. Enforce it in the checks gate (/system + the per-app verify) so a regressed build can't reach production.

Patch as we go: the moment a fix is generalizable, it moves upstream the same session we find it. A fix that lives only on one server is a bug waiting to reappear.

This is why, for example, HOSTNAME=0.0.0.0 is a platform default and not something every app owner rediscovers, and why a failed deploy gate rolls back instead of leaving an app down.


The catalog index (G1–G19)

#TitleClassStatus
G1Next.js standalone binds IPv6-only → 502deploy defaultfixed
G2Silently-dead container never restartsdeploy defaultfixed
G3Env dropped by a full redeployenv custodyfixed
G4Partial-bake on redeploy (stored Dockerfile)deploy modelfixed (from-source only)
G5Agent grace clock stalls (license refresh rejected)licensingfixed
G6Flaky session-recorder frame test (race)test hygienefixed
G7Build cache wiped on every deploydeploy performancefixed 2026-06-30
G8Failed gate leaves the app DOWNdeploy honestyfixed 2026-06-30
G9Agentless node reads as a dead boxUI honestyfixed 2026-06-30
G10Managed-proxy deploy succeeds but the cert never issues (pre-DNS ACME backoff)TLS/edgeopen (operational; forward-fix proposed)
G11Strict build-time env-check blocks a from-source build (env in context, not just runtime)build-configopen (playbook + forward-fix proposed)
G12HTTP-01 is not the production TLS path (no zero-downtime cutover, no wildcards)TLS/edgefixed 2026-07-01 (DNS-01)
G13WP migration rewrite: cascade corruption, shared-DB mass-drop, unanchored hostsdata safetyfixed 2026-07-02
G14A node served workloads with no license; secrets + backups single-copyresiliencefixed 2026-07-02
G15Containers ran unhardened (no no-new-privileges; setuid escalation)container securityfixed 2026-07-03
G16A rebuilt node did not self-harden (firewall was a manual step)reproducibilityfixed 2026-07-03
G17Auth had no brute-force protection (unbounded password + TOTP guessing)auth securityfixed 2026-07-03
G18Hardening was hardcoded; firewall required the ufw packageconfig/portabilityfixed 2026-07-03
G19Panel firewall config didn't survive a rebuild (no boot-reapply)persistencefixed 2026-07-03

Each row is fully written up in planning/BUILD-GUARDS.md. The three still-open items (G10, G11, and the G1/G2 "overclaim" — the code that makes them automatic) are tracked as Gate A code work; see planning/MIGRATION-MASTER-PLAN.md and planning/TODO.md.

Register note (accuracy): the Gap Register flags that the code making G1/G2 automatic — the HOSTNAME injection and the default container healthcheck/restart — is specified but not yet landed. Until it lands, treat G1/G2 as "fix designed, guard test pending." Don't cite a guard as done in a PR unless the regression test exists and passes.


Where a new guard lives in the code

LayerPackage / dirGuard examples
Agent deploy defaultsagent/internal/deploy/, agent/internal/runtime/G1, G2, G7, G8
Agent builderagent/internal/builder/G4, G11
Agent proxy / TLSagent/internal/proxy/G10, G12
Agent hardeningagent/internal/harden/, firewall engineG15, G16, G18, G19
Control-plane platformcontrol-plane/web/lib/platform/G3 (env custody), G8 (rollback state)
Control-plane authcontrol-plane/web/lib/auth/, ratelimitG17
WP pluginwordpress-plugin/G13
Fleet/telemetry UIcontrol-plane/web/lib/fleet/G9

When you add a guard, put the fix in the upstream layer above, colocate a *_test.go / *.test.ts regression test, and append the catalog row in planning/BUILD-GUARDS.md.