| 2026-07-15 |

DOCFIX-195: office1-netbox holds the VR1 IPAM apex role; apex write-back deferred; C2 redefined
...
Operator ruling 2026-07-15: office1-netbox (10.10.1.10) HOLDS THE IPAM APEX
ROLE during VR1 -- the authoritative NetBox every consuming system (OpenTofu,
MAAS, overlays, bundle) derives its values from. It is a working draft (edited
as the buildout proceeds), but it is the apex. netbox.baldurkeep.com takes NO
write during VR1; it is the eventual production apex, frozen as a read-only v1
reference draft. Merge-back is deferred to end-of-deployment (a maybe), NOT a
Stage 2 gate.
The repo encoded the opposite roles and defined close-out C2 (the last Stage 2
gate, and the branch->main merge trigger) as an operator-gated write UPSTREAM.
Under the ruling there is no apex write in Stage 2, so C2 is redefined:
office1-netbox holds the COMPLETE validated dataset -- carve + six-plane roles
+ both DCs + the D-120 compose-/24 child ranges + the two service IPs -- and it
is VERIFIED. C2 stays OPEN.
Finding surfaced while drafting (logged, not fixed): the D-120 objects are
ip-ranges/ip-addresses, object classes the sandbox loop does NOT cover
(prod-draft-dump.py ENDPOINTS and sandbox-fidelity-check.py KEY/EXPECTED_NEW
both stop at rirs/roles/regions/sites/aggregates/prefixes). So the hardened
fidelity check cannot verify the D-120 load; verifying it needs a tool
extension (+harness) or a recorded manual read-back. Confirmed no live system
config points at any NetBox URL -- documentation-only change.
Files: dc-dc-deployment-workflow.md (State + C2 row), dc-dc-netbox-buildout-scope.md
(sec 8.1 + 8.6), session-ledger.md (5 narrative spots + new deferred-item bullet;
machine-derived block re-seeded from the 2026-07-15 scan), design-decisions.md
(D-010 note + D-103 pointer), netbox/README.md (target banner), + changelog.
repo-lint 0 fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015WgVrZE2ryY9aXP7JiBuwj
|

D-120 ADOPTED + EXECUTED: re-IP Office1 services into the .2-.49 static band
...
office1-netbox .201->.10 and office1-tailscale .202->.11, on the wire, no
wipe. NetBox HTTP 200 at .10; tailscale still advertises 10.10.0.0/22.
Method (DC replication inherits it): MAAS refuses interface edits on a
Deployed machine and the only lift is Release->redeploy=WIPE, so the live
address is set on the guest (netplan + curtin cfg, reboot-durable) over
lxc exec (socket immune to the netplan apply), and MAAS-model drift is
accepted (reconciles at next teardown/redeploy). NetBox ALLOWED_HOSTS=*
so no restart; tailscale re-asserted automatically.
Swept SANDBOX_HOSTS .201->.10 in the three importers + test + the on-box
working copy; updated live-state docs; flipped D-120 to ADOPTED with an
Execution record; resolved the re-IP runbook's PENDING markers to the
measured method. Gauntlet ALL GREEN (58); repo-lint 0 fail / 1 legacy WARN.
Still owed: D-120 Step 6 (register the /24 child ranges + service IPs in
the sandbox, feed upstream) rides with C2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpGaiX6UvhSReDwN2ywstJ
|
| 2026-07-14 |

C4 DONE: document the NetBox sandbox loop; add the upstream-write guard it exposed
...
C4 (Stage 2 close-out) -- the sandbox loop is now documented in two places, no
rationale duplicated: docs/dc-dc-netbox-buildout-scope.md section 8 (design: why a
sandbox, the fidelity check + its 2026-07-14 hardening, the write-guard table, the
WAF trap, C2 preconditions) and runbooks/dc-dc-phase1-office1-standup.md Step 10b
(the dump->seed->apply->verify->gated-write sequence, two-host/two-token separation).
Drafted by a subagent, corrected against this session's own fixes before placement.
The guard C4 exposed: roles-aggregates-import.py and dc-dc-prefixes-import.py had NO
hostname gate -- until today the WAF 403 was the only thing stopping an accidental
--commit to the apex, and the UA fix removed that accidental safety. Both now gate a
non-sandbox --commit behind --yes-write-upstream (SANDBOX_HOSTS allowlist), matching
d115-office-carve.py -- so all three write-capable importers behave identically. This
is defence-in-depth with the whole-plan preflights (which prevent a half-write).
Tests: prefixes-import 86->91 (refused without the flag + writes nothing; allowed
with it; sandbox host needs no flag). roles-aggregates 20->24. GAUNTLET ALL GREEN (58).
Stage 2: C1/C3/C4 DONE. C2 (production write) is the only item left -- gated, with
preconditions now documented (re-verify sandbox under the hardened check, run from
office1-netbox, --yes-write-upstream, approve each mutation).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|

D-118 ADOPTED (org ULA fd50:840e:74e2::/48); VR1 DC0 + DC1 IMPORTED; roles importer bug fixed
...
D-118: ORG_ULA_48 = fd50:840e:74e2::/48. RFC 4193 valid (fd -> L=1, 40-bit Global
ID 0x50840e74e2, /48 -> 256 x /56 for D-101's per-DC /56). The load-bearing check
is NOT the RFC conformance -- it is that it does NOT collide with Tailscale's
fd7a:115c:a1e0::/48: office1-tailscale runs --accept-routes, so an overlap would
have been a LIVE tailnet routing conflict, not a paper one.
Provenance stated honestly: the 'CSPRNG-generated' claim cannot be re-verified.
It does not change the ruling (RFC 4193's SHA1 construction is a SHOULD; the
requirement is a pseudo-random 40-bit Global ID). Regenerating would only swap in
a different random number at the cost of re-editing 7 places.
Corrects a FALSE claim: changelog-20260711 called this value 'ratified' when no
D-number ever assigned it. That phantom ratification is why it read as settled for
two days. Same failure mode as D-117: a document asserting authority it lacked.
IMPORTED into the sandbox: six-plane roles + 2 RIRs + 5 aggregates, then VR1 DC0
(18 prefixes) and VR1 DC1 (18). The D-117 fix is now LIVE-PROVEN, not just green
offline: --dc dc0 -> apex site VR1 DC0 (id=8), --dc dc1 -> VR1 DC1 (id=9). ULA
reads DC.NN in the 4th hextet per D-111 (fd50:840e:74e2:220:: .. :350::).
A REAL BUG, found by running roles-aggregates-import.py for the first time for
real: it checked existence by SLUG only, but NetBox also enforces UNIQUE NAMES.
The draft's legacy role 'Replication' (slug repl) blocks the six-plane role (slug
replication, name Replication) -- so it created 4 roles, 400'd on the 5th, and
DIED, leaving the apex HALF-POPULATED with no RIRs, no aggregates, no rollback.
It had never met a NetBox holding the real draft (only empty ones) and it SHIPPED
WITH NO HARNESS. That is why it survived.
- six-plane role renamed 'Replication Plane' (slug unchanged -- lib-net.sh
SPACES6 and the prefix importer look up 'replication')
- the script now PREFLIGHTS every name/slug and writes NOTHING unless the whole
plan is viable. An IPAM importer that can die partway corrupts what it
populates.
- tests/roles-aggregates-import/ CREATED: 16/16 PASS.
Final state PROVEN by sandbox-fidelity-check (now carrying the full planned
delta): the sandbox is the upstream draft + EXACTLY D-115 + D-117 + D-118 --
every shared object field-identical, nothing lost, nothing stray. 90 -> 134
prefixes.
repo-lint 0 fail. run-tests-all: ALL GREEN (56 harnesses).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ZBxzAbmqwLW7jEmMG23Xw
|

Prove the sandbox is a FAITHFUL replica, not just a count-matching one
...
netbox/sandbox-fidelity-check.py (+4 assertions in tests/sandbox-seed, 22/22 PASS)
Counts and idempotency CANNOT prove a faithful seed. The region-scope bug proved
it: 17 of 90 prefixes silently lost their scope, every count still matched, and
re-runs were still perfectly idempotent. It was caught by LUCK on a spot-check
that happened to print 'site None'. This is that catch, made deliberate.
Method needs no new plumbing: prod-draft-dump.py is READ-ONLY, so point it at the
sandbox and diff the two JSON dumps FIELD BY FIELD.
RESULT: every shared object is field-level IDENTICAL to upstream, and the delta is
EXACTLY the expected D-115 additions (+edge role, +vr1-off1 site, +8 prefixes).
Nothing missing, nothing unexpected. The sandbox is a PROVEN baseline -- which is
what the later feed-back diff to production depends on.
Also de-misdirects docs/dc-dc-netbox-buildout-scope.md, which still told the next
person to run '--dc dc2' with DC2_V4_SUPERNET -- both of which the new guards now
REJECT by name. Its G5 recommendation (rename the APEX to vr1-dc1/vr1-dc2) is
marked SUPERSEDED: D-117 ruled the opposite. ORG_ULA_48 is now listed there as
UNASSIGNED and BLOCKING, not as a settled literal.
Disclosure: D-117 Option B is PARTIALLY executed -- the import path and the scope
doc are done; the $DC shell selector and the runbook DC prose are NOT. Recorded
in the ledger so 'D-117 ADOPTED' is not misread as 'fully renamed'.
repo-lint 0 fail. run-tests-all: ALL GREEN (55 harnesses).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ZBxzAbmqwLW7jEmMG23Xw
|
| 2026-07-13 |

D-115 ADOPTED: v4 stays role-based; Office = /22 per office; DC2 moves INSIDE the Cloud /16
...
Operator ruled all three questions:
1. v4 stays ROLE-BASED, fill the missing roles (v6 keeps its region hierarchy; v4 is pooled by
function -- v6 is abundant, v4 is scarce). No DC renumber; D-101's inherit-DC0-unchanged
economy is preserved.
2. /22 per office. VR1 Office1 = 10.10.0.0/22 -- which BLESSES the deployed LAN (10.10.0.0/24) and
LXD compose net (10.10.1.0/24) exactly. ZERO deployment renumber.
3. Register AND deploy dual-stack on Office1 now.
THE CARVE. New v4 roles: Office (10.10.0.0/16, /22 per office) and Edge (172.30.0.0/16, mirroring
v6's Edge Networks fe::/48) so the simulated ISP uplinks stop living inside the Corp OOB block --
office1-wan's 172.30.1.0/24 becomes legitimate. DC2 moves from 10.13.0.0/19 to 10.12.64.0/19,
INSIDE the Cloud /16: free (DC1 holds 10.12.{4,8,12,16,32,36}), still routable to DC1 over the
dark fiber. docs/dc-dc-netbox-buildout-scope.md is corrected -- its 10.13.0.0/19 recommendation sat
outside every allocated block and would have had DC2 squatting too.
v6 is determined by the existing pattern, no design freedom: Office1 = 2602:f3e2:f01 :/56 with
a /64 office subnet, plus a "VR1 Off1" site. (VR0 Off0 = e01 :/56 -> /64; Eugene's Charnelton =
101 :/56. VR1's office /48 already existed and we were simply not using it.)
MEASURED CONSTRAINT, attached before anyone builds on a false premise: IPv6 DOES NOT EGRESS THE LAB.
vcloud has global v6 and a v6 default route, and the v6 gateway IS reachable (RA and L2 work) -- but
v6 INTERNET is unreachable (2606:4700:4700::1111 and 2001:4860:4860::8888 both 100% loss; v4 from the
same host is fine). So deploying dual-stack on Office1 proves v6 ADDRESSING/RA/services-binding-v6 --
most of D-101's family matrix, worth doing -- but it CANNOT prove v6 internet reachability. Public
GUA reachability (v6 public API endpoints, tenant GUA egress) is UNPROVABLE IN VR1 until the lab's
upstream carries v6. That is outside this cloud and this repo. Do not schedule a VR1 test that
depends on it, and do not read a green internal-v6 result as "v6 works end to end".
The NetBox WRITE is deliberately NOT done unilaterally: netbox.baldurkeep.com is production, the
standing architecture is read=source / write=feedback-only, and the Office1 sandbox NetBox (composed,
Ready, not yet deployed) is where this carve gets simulated first.
repo-lint 0 fail -- and its L5 collision guard did its job, catching that my amendment header read
as a second DEFINITION of D-115. Renamed to the AMENDMENT form the guard exempts rather than
weakening the rule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
| 2026-07-11 |

D-111: align VR1 v6 subcarve to deployed NN mnemonic; fix ULA-gen doc bug
...
Basing VR1 NetBox on the real netbox.baldurkeep.com modeling (operator
directive). A read-only live export confirmed VR0-DC0/Willamette use an NN
net-byte mnemonic with /60-per-plane + /64-active; dc-dc-prefixes-import.py
did not match it (arbitrary contiguous indices, /64-only).
DOCFIX-182 / D-111: carve_v6 replaces carve_ula/carve_gua -- provider :10
(+API-VIP :11) GUA, metal /60 shared by admin :20 / internal :21, data :30,
storage :40, repl :50, all /60+/64 ULA; the ULA /56 is indexed to the GUA site
nibble so the 4th hextet reads DC.NN in both families. Direct-math _sub_at (no
subnet enumeration -- preserves the DOCFIX-181 no-hang property). Harness
40 -> 53 PASS. D-111 ADOPTED in design-decisions.md; scope-doc sub-decision #1
ratified.
DOCFIX-183: fix the invalid-IPv6 ULA-generation sed in the netem/ULA proposal
doc (4+6 -> 2+4+4 hextets, caught by actually running it); ratified VR1 ULA is
fd50:840e:74e2::/48.
repo-lint 0 fail; tests/dc-dc-prefixes-import 53/53.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|