Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260714-c4-sandbox-loop-and-upstream-guard.md

2026-07-14 -- C4 (sandbox loop documented) + the upstream-write guard it exposed

C4 -- the NetBox sandbox loop is now documented (Stage 2 close-out)

Two placements, no duplication of rationale:

  • docs/dc-dc-netbox-buildout-scope.md section 8 -- the design: why a sandbox, why a fidelity check (the 17-of-90 silent scope drop), the 2026-07-14 hardening (do not trust a pre-hardening "faithful" verdict), the five-tool write-guard table, the WAF trap, and the C2 preconditions.
  • runbooks/dc-dc-phase1-office1-standup.md Step 10b -- the operational sequence (dump -> seed -> apply delta -> fidelity-check -> operator-gated upstream write), with the two-host / two-token separation called out as the safety property. Step 10's stale "the literals do not exist yet" note was corrected (D-115/117/118 assigned them).

Drafted by a subagent, then corrected against this session's own changes before placement (the draft predated the two fixes below and would otherwise have documented a now-false state).

The guard C4 exposed: the two pynetbox importers had NO upstream gate

Documenting the write-guard architecture surfaced that roles-aggregates-import.py and dc-dc-prefixes-import.py had no hostname gate at all -- unlike sandbox-seed.py (structural refusal) and d115-office-carve.py (SANDBOX_HOSTS + --yes-write-upstream). Until earlier today the WAF's 403 on the default User-Agent was the ONLY thing stopping an accidental --commit against the production apex -- and the WAF fix (changelog-20260714-netbox-importers-waf-useragent.md) removed that accidental safety. So an explicit gate was required, and is now added:

  • both importers gain --yes-write-upstream; a --commit whose NETBOX_URL host is not in SANDBOX_HOSTS = {localhost, 127.0.0.1, 10.10.1.201} REFUSES without it. Same pattern as d115-office-carve.py, so all three write-capable importers now behave identically.

This is defence-in-depth with the whole-plan preflights (which prevent a half-write); the guard prevents the write from starting against the wrong target.

Tests

  • tests/dc-dc-prefixes-import/ 86 -> 91: --commit to a non-sandbox host is REFUSED without --yes-write-upstream (and writes nothing); WITH the flag it proceeds; a sandbox host needs no flag. The write-path fake tests now target the known sandbox host so the guard passes transparently.
  • tests/roles-aggregates-import/ 20 -> 24: asserts the flag, the SANDBOX_HOSTS gate, the refusal message, and that the guard actually checks the flag.

GAUNTLET ALL GREEN (58); repo-lint 0 fail.

Stage 2 close-out after this change

C1 DONE, C3 DONE, C4 DONE. C2 (feed the carve upstream) is the ONLY remaining item -- a production write, still gated, with its preconditions now documented (section 8.6): re-verify the sandbox under the hardened check, run the pynetbox importers from office1-netbox, pass --yes-write-upstream, approve each mutation.

Revert

git revert <this commit>

Docs + one argparse flag and guard block per importer + their tests. No NetBox was written.