diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 12b8c1b..ba6d7b8 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -171,29 +171,44 @@ it confirmed relations/bindings/subordinates HOLD under role-sep (uniform 6-NIC, SEC-011) and no channel/pin change is needed. - **OPEN before deploy (from the expansion review):** RESOLVE-BEFORE-DEPLOY -- - (a) **vault VIP** (decorative-HA regression: vault scaled 3 + hacluster but NO vip - anywhere; D-036 says vault carries VIPs; needs a dc1-band VIP overlay + the vault-HA - VERIFY-LIVE); (b) **address family: RULED 2026-07-25** (D-101 CONFIRMED via a dated ruling note under + (a) **vault VIP** (decorative-HA regression: vault scaled 3 + `vault-hacluster + cluster_count:3` but NO vip -> `vault:secrets` consumers hit a unit address). **D-020** + (not D-036) lists vault among the apps carrying provider+metal VIPs. Needs a per-DC VIP + in the symmetric overlay shape (ruling 3; metal-admin+metal-internal only, no provider + leg; octet .61 band-legal per D-134-amended .50-.99) + the vault-HA VERIFY-LIVE. OPS + (D-020 conformance repair), not a new D; (b) **address family: RULED 2026-07-25** (D-101 CONFIRMED via a dated ruling note under ## D-101; GA-R5) -- dual-stack where IPv4 is required, IPv6-only where IPv6-only is sound, for BOTH DC0 and DC1 this deployment; the v4-only phasing option is CLOSED. Consequence: the vr1-dc1-vips / dc-dc-ipv6-family-matrix per-key REPLACE collision must be reconciled to ONE dual-family `vip` per app per DC before deploy -- no longer - deferrable. Octavia family remains an OPEN sub-ruling (see D-136 open question 2). FOLLOW-UP scripts/gates -- preflight.sh runs the checker - BARE (must pass the dc1 overlays + --dc vr1-dc1); pre-flight-checks.sh VR0-frozen - (no $DC selector, host_sysid-by-hostname); cloud-assert.sh false-passes decorative HA - on the 12 scaled API services; `juju bootstrap` needs a controller-tag constraint; - phase-4 Step 4 de-stale. VERIFY-LIVE gates: vault 1.8 HA-on-MySQL@3 (D-121), hacluster + deferrable. Octavia family remains an OPEN sub-ruling (see D-136 open question 2). + (c) **per-DC artifact shape: RULED 2026-07-25** (ruling 3) -- `bundle.yaml` becomes + VIP-free (topology only); every per-DC value arrives via overlay, dc0 included; dc0's + VIPs extract from base into `overlays/vr1-dc0-vips.yaml` (2 commits: neutral MOVE proven + via `provider-bundle-check --overlay --dc vr1-dc0`, then the dual-stack ADD; MIGRATE the + inline VIP-line operational comments). FOLLOW-UP scripts/gates (2026-07-25 pack-review + blockers fold in here) -- preflight.sh runs the checker BARE (must pass the dc1 overlays + + --dc vr1-dc1); **BLOCKER-1** the VIP-free bundle makes pre-flight-checks.sh CHECK 1 + see 0 VIPs -> hard-fail (point it at the MERGED bundle, not the VIP_COUNT_EXPECT 11->12 + bump); **BLOCKER-2** provider-bundle-check.py rejects vault's non-triple/no-provider/.61 + VIP once DC-aware (allow it + widen the octet band to .50-.99; bump VIP_OCTET_MAX 60); + pre-flight-checks.sh VR0-frozen (no $DC selector, host_sysid-by-hostname); + cloud-assert.sh false-passes decorative HA on the 12 scaled API services; `juju + bootstrap` needs a controller-tag constraint; phase-4 Step 4 de-stale. VERIFY-LIVE gates: vault 1.8 HA-on-MySQL@3 (D-121), hacluster cluster_count:3 semantics, Ceph pools size=3/min_size=2/failure-domain=host, machines-block overlay merge (`--dry-run`); **keystone policyd-override (RULED 2026-07-25): `juju status` shows `PO:` on EVERY keystone unit after scale-up, never `PO (broken):`** -- a broken override is atomic (whole policy discarded, silently reverting every tenant domain-manager to a plain user; protects D-051/D-064). - - **NetBox->deploy coupling PROPOSED -> Chat (design seat).** A per-DC renderer/pipeline - that GENERATES the overlays + tfvars from NetBox IPAM (eliminating the hand-render - fragility + the VIP-overlay collision by construction, making the deploy gates - DC-aware) is being scoped as a new PROPOSED D-number (next-free per ledger-scan) in - Chat -- the escalation of the committee's Fork-2 deferred renderer. Prompt handed to - the operator 2026-07-24. + - **NetBox->deploy coupling: PROPOSED as D-136 (Chat, 2026-07-25).** A per-DC renderer + generating overlays + tfvars from the NetBox record, recommending option (C) -- build + the values-file BACK half now for both DCs, add the NetBox FRONT half for Roosevelt. + Does NOT gate the dc1 deploy; mechanism awaiting operator ruling. Prerequisite sequenced + FIRST: extend `netbox/sandbox-fidelity-check.py` (blind to D-124/D-134 today). The v6 + subcarve is NOT an open gate (D-111 ADOPTED; values imported both DCs). The target + OPERATING MODEL (NetBox draft -> approve-on-rendered-diff -> apply -> bounded + self-healing) is PINNED for a planning session + (`docs/audit/operating-model-plan-20260725.md`), not ratified. - **FULL DECISION RECON 2026-07-25 (4-lens committee; record `docs/audit/decision-recon-20260725.md`).** office1-netbox holds the entire IP PREFIX layer (all 12 planes + transit + uplink + v6 GUA/ULA), DRIFT-FREE across diff --git a/docs/audit/decision-recon-20260725.md b/docs/audit/decision-recon-20260725.md index 600077b..a4bc1f5 100644 --- a/docs/audit/decision-recon-20260725.md +++ b/docs/audit/decision-recon-20260725.md @@ -113,12 +113,12 @@ | Prio | Recon | Surfaces | When | Blocks | |---|---|---|---|---| -| P0 | VIP-overlay collision + vault-VIP completeness | vr1-dc1-vips vs ipv6-family-matrix (both own `vip:`); rendered vault (no vip) vs D-036 | before dc1 deploy | dc1 deploy | +| P0 | VIP-overlay collision + vault-VIP completeness | vr1-dc1-vips vs ipv6-family-matrix (both own `vip:`); rendered vault (no vip) vs D-020 | before dc1 deploy | dc1 deploy | | P0 | rendered bundle/overlays <-> apex | bundle + overlays (VIP band, tags, ovn MACs) vs the apex dump | before deploy (`--dc vr1-dc1` + `--dry-run`) | deploy correctness | | P1 | **extend `netbox/sandbox-fidelity-check.py`** (STALE) | the tool: covers only D-115/117/120, diffs the frozen 90-prefix draft not the 139-prefix apex; blind to D-124/D-134 | before the coupling design | trustworthy apex recons | | P1 | lib-net/lib-hosts literals <-> apex | 28 IP + 26 IP/MAC literals vs current apex | at coupling design; on any apex mutation | coupling adoption | | P1 | MAAS live <-> NetBox | D-134 bands + the 12 VLANs / 6 spaces (MAAS-only) vs apex | end-of-deploy + coupling | apex completeness / merge-back | -| P2 | post-deploy cloud <-> decisions (BOM) | cloud-assert --capture vs D-121 HA / D-036 vault / D-108 / Ceph size/CRUSH / cluster_count | after dc1 deploys | Stage-5 close | +| P2 | post-deploy cloud <-> decisions (BOM) | cloud-assert --capture vs D-121 HA / D-020 vault / D-108 / Ceph size/CRUSH / cluster_count | after dc1 deploys | Stage-5 close | | P2 | IPv6 / dual-stack | ipv6-family-matrix v6 tokens vs apex v6 | once v6 rendered (depends on P0 phasing) | dual-stack | | P2 | office1-netbox -> netbox.baldurkeep.com merge-back | 4.6.4 apex -> 4.5.8 frozen v1 reference | end-of-deployment (DOCFIX-195) | IPAM-of-record loop | | P2 | SEC-ledger <-> reality | security-ledger 15 open rows vs rotations/scope | G14 reconcile now; bulk at v1 close | security close-out | diff --git a/docs/audit/stage5-expansion-review-20260724.md b/docs/audit/stage5-expansion-review-20260724.md index 2c11628..fe09c62 100644 --- a/docs/audit/stage5-expansion-review-20260724.md +++ b/docs/audit/stage5-expansion-review-20260724.md @@ -71,7 +71,7 @@ - **Vault VIP (decorative-HA regression).** `dc-ha-scaleup.yaml` scales vault -> 3 + `vault-hacluster cluster_count: 3` + re-adds `[vault:ha, vault-hacluster:ha]`, but NO `vip:` is set on vault anywhere (base or overlays) -- it was dropped with BUNDLEFIX-002 - when vault was de-HA'd. D-036 lists vault among the apps carrying provider+metal VIPs. + when vault was de-HA'd. D-020 [corrected DOCFIX-201: D-020, not D-036] lists vault among the apps carrying provider+metal VIPs. Without a VIP, kv consumers (barbican-vault -> `vault:secrets`) get one unit's `vault_url`, not a stable HA endpoint = decorative HA. Fix = add a dc1-band vault VIP in an overlay (needs a band octet) + the vault-HA VERIFY-LIVE (section C). designate is diff --git a/docs/changelog-20260724-stage5-committee-fork1.md b/docs/changelog-20260724-stage5-committee-fork1.md index 8a94c5b..8b26750 100644 --- a/docs/changelog-20260724-stage5-committee-fork1.md +++ b/docs/changelog-20260724-stage5-committee-fork1.md @@ -214,6 +214,46 @@ left in the delete-after-use pack file. **Revert:** `git revert ` -- removes the gate line. +## Item 14 -- D-136 PROPOSED (NetBox-coupled render pipeline) + Ruling 3 recorded (GA-R5) + +**What:** appended D-136 (PROPOSED, [ARCH]) to `docs/design-decisions.md` register tail; +CURRENT-STATE Stage-4 bullet updated SAME COMMIT (C1) -- bullet (c) artifact-shape, the +coupling bullet -> "PROPOSED as D-136 / option (C)", the FOLLOW-UP gate blockers. Ruling 3 +is recorded INLINE in D-136's "Artifact shape (RULED 2026-07-25)" paragraph with the +verbatim question + utterance. +**GA-R5 (Ruling 3).** Question: symmetric (extract dc0 VIPs to an overlay, base VIP-free) +vs asymmetric (dc0 v6 into base, two renderer modes)? Operator utterance, verbatim: "Your +question about 1 or 2, go with 1. Base bundle and everything else is applied via overlay." +Effect: symmetric per-DC overlays; bundle.yaml VIP-free; gates the dc0 VIP extraction. +**Pack-review committee corrections folded in (2026-07-25):** verbatim inlined into durable +homes (D-136 paragraph + here), not the delete-after-use pack file (BLOCKER-1); added the +`VIP_OCTET_MAX=60` sibling constant + the 2nd stale `VIP_COUNT_EXPECT` site; mis-citation +count corrected to SEVEN. **Chat correction (2026-07-25) folded in:** open question 5 +rewritten -- Jenkins IS the runner (gap VOID); real requirements are webhooks + status-back +(unverified vs GitBucket); placement OPEN (voffice1/VR1, per-DC agents Roosevelt, D-132/D-105 +caveats); carry-forward `handoff-20260703-open-items.md:159`. +**Why:** escalates the Fork-2 renderer deferral; scopes the sub-prefix gap fix. PROPOSED -- +operator rules the mechanism. +**Revert:** `git revert ` -- deletes D-136 + restores the CURRENT-STATE bullets. + +## Item 15 -- DOCFIX-201: the dual-VIP decision is D-020, not D-036 (SEVEN sites) + +**What:** corrected SEVEN VIP-context citations of `D-036` where `D-020` is meant +(`## D-020: Dual provider + metal API VIPs`; `## D-036` is the magnum-capi-helm decision): +`docs/design-decisions.md` (3: the D-121 "problem this fixes" `per D-036`, the `D-009, +D-036` HA-scaleup table row, the References `D-036 (dual VIPs)`), `docs/CURRENT-STATE.md` +(the vault-VIP bullet), `docs/audit/decision-recon-20260725.md` (2 backlog rows), and +`docs/audit/stage5-expansion-review-20260724.md` (in-place with the bracketed marker +`[corrected DOCFIX-201: D-020, not D-036]`, operator-chosen). +**Why:** the mis-citation had spread to SIX sites (2 created by the recon itself); the 7th +(the `D-009, D-036` table row) was caught by the pack-review committee -- the pack listed +only 6 and its own verify count would have failed at 9. Reading the correct D-020 also +surfaced that D-020's ratified set includes vault + excludes ceph-radosgw while the deployed +set does the opposite (fed into D-136). +**Verify:** `git grep -n "D-036" -- ':!docs/archive' ':(exclude)_handoff-not-committed/*'` +-> 8 hits, all driver/chart/CAPO, zero VIP-context. +**Revert:** `git revert ` -- text-only across four files, no behavioural effect. + ## Next (not done this session; gated) Commit+push this batch (GA-R5: rulings land before dependent work). Then, each diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 2bb5997..524bad0 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -3446,7 +3446,7 @@ modeling at testcloud scale"): the hacluster + VIP relations were included at `num_units: 1` as a **decorative** pattern -- "a single unit can't form a real HA quorum" -- explicitly so that "Roosevelt scale-up is mechanical: change `num_units: 1` -> `num_units: 3` and rerun." The bundle -already carries all 12 hacluster subordinates and, per **D-036**, both provider+metal VIPs on every +already carries all 12 hacluster subordinates and, per **D-020**, both provider+metal VIPs on every clustered API app. VR1 (the regional rehearsal) is where that decorative HA is made real -- the missing units get added. @@ -3460,7 +3460,7 @@ | App | Now | -> | Mechanism | Governing | |---|---|---|---|---| -| keystone, glance, neutron-api, nova-cloud-controller, placement, cinder, openstack-dashboard, octavia, barbican, magnum, designate | 1 | 3 | hacluster + dual VIP (already wired) | D-009, D-036 | +| keystone, glance, neutron-api, nova-cloud-controller, placement, cinder, openstack-dashboard, octavia, barbican, magnum, designate | 1 | 3 | hacluster + dual VIP (already wired) | D-009, D-020 | | ceph-radosgw | 1 | 3 | hacluster + VIP (wired; NOT in D-009's named 12 but HA-eligible) | -- | | rabbitmq-server | 1 | 3 | native clustering / quorum queues (no hacluster) | -- | | vault | 1 | 3 | see the backend sub-question below | D-006 (revised by C1) | @@ -3562,7 +3562,7 @@ 3. **Per-node sizing:** RULED (control 16/64/150, compute 12/48/100, storage 8/24/550; Status). **Related:** D-009 (decorative HA pattern this executes), D-006 + C1 (vault backend history), -D-036 (dual VIPs, already present), D-062 (mysql-at-3), D-108 (rbd-mirror stays 1), D-040 +D-020 (dual VIPs, already present), D-062 (mysql-at-3), D-108 (rbd-mirror stays 1), D-040 (reserved memory), D-104 (single-unit JUJU controller -- unaffected; this is the OpenStack control plane, not the Juju bootstrap node), SEC-003 (unseal). Does NOT touch DC2/`vr1-dc1` scope. @@ -4723,3 +4723,272 @@ **A1 test:** Roosevelt greps this for the mirror service shape, the utility-address convention, the staged class order, and the measure-then-rule posture on charmhub/snaps. + +## D-136: NetBox-coupled per-DC render pipeline for bundle overlays and tfvars [ARCH] + +**Status:** PROPOSED 2026-07-25 (Chat design seat, repo HEAD bc9a2df; operator has not +ruled on the mechanism). Escalates the renderer deferral in +`docs/audit/committee-20260724-track2-bundle-render.md` (Fork 2, "hand-render dc1 now, +extract the tool when dc0 forces the second render") to a NetBox-coupled generator. +Supersedes nothing. + +**Problem.** Addressing is authoritative in NetBox (office1-netbox, the VR1 working +apex, DOCFIX-195) but reaches the deploy as HAND-RENDERED static literals: NetBox -> +imported by `netbox/*.py` -> literals in `scripts/lib-net.sh` / `lib-hosts.sh` and +tfvars -> hand-rendered into `bundle.yaml` and `overlays/*.yaml`. There is no live pull +and no generator. Measured fragility: + +- BUNDLEFIX-052 (ceph-rbd-mirror binding) and a YAML flow-comma slip, one session. +- The app-to-VIP set exists in FOUR live places and no two agree: D-020's ratified list + (11 apps, includes vault, excludes ceph-radosgw); `bundle.yaml` and + `overlays/vr1-dc1-vips.yaml` (11, vault dropped, ceph-radosgw added); + `overlays/dc-dc-ipv6-family-matrix.yaml` (10, plus octavia deliberately commented at + lines 140-152). +- Two overlays own `vip:` for the same 10 applications under per-key REPLACE and + last-wins, so the deploy input depends on overlay ORDER, not on a reconciled value. +- vault carries no `vip:` anywhere in the tracked tree while `overlays/dc-ha-scaleup.yaml` + scales it to 3 units and declares `vault-hacluster` at `cluster_count: 3`. HA intent + the artifact does not express, and an unrecorded departure from D-020. + +Under the 2026-07-25 family ruling (see the ruling record for this date) the per-app +VIP becomes a six-address dual-family string, for BOTH DCs. That roughly doubles the +hand-maintained literal count at the precise point where the hand-render has already +failed twice. + +**What the pipeline PULLS** (verified present in +`netbox/draft/vr1-office1-current-20260725.json`, captured 2026-07-25): + +- Six-plane v4 `/22`s and v6 `/60`+`/64` per DC, selectable by `scope_site` + (`vr1-dc0`, `vr1-dc1`); 92 of 139 prefixes carry `scope_site`, and the `role` slugs + match the plane names exactly. +- The D-111 API-VIP `/64` per DC (`2602:f3e2:f02:11::/64` dc0, `2602:f3e2:f03:11::/64` + dc1) and the shared metal `/60` at `:20`, admin `/64` `:20`, internal `/64` `:21`. +- Aggregates (`2602:f3e2::/36` ARIN, `fd50:840e:74e2::/48` org ULA, RFC1918, + `23.157.124.0/24`), the D-120 bands as ip-ranges, two rack-controller static IPs. + +**What it CANNOT pull today** (scoping constraint, not a defect): VIPs -- the record +holds four ip-addresses and none is a VIP. MAC pins and node inventory -- there is no +`dcim/devices` and no `dcim/interfaces` in the record at all. VLANs/VIDs -- no +`ipam/vlans`; the VLAN-group buildout is the open G4 item at +`docs/dc-dc-netbox-buildout-scope.md` section 4e. + +**Where the gap actually is (per `docs/audit/decision-recon-20260725.md`, 4-lens, +2026-07-25).** The PREFIX layer is COMPLETE and DRIFT-FREE -- all 12 plane `/22`s, both +transit `/30`s, both uplinks, both GUA `/48`s, the ARIN `/36`, the full v6 subcarves and +the org ULA are present and match `lib-net.sh` value-for-value. The gap is entirely +SUB-PREFIX, and it is what this decision exists to close: + +- **The D-134 bands** (as amended 2026-07-23, the CONTIGUOUS table: `.1-.3` infra, + `.4-.49` reserved utility, `.50-.99` VIP, `.100-.200` nodes with control `.100-.119` / + compute `.120-.149` / storage `.150-.200`, `.201-.254` dynamic) exist ONLY as decision + prose -- in neither NetBox nor `lib-net.sh`. `lib-net.sh` carries only the dc0 VIP + octet band `.50-.60`, UNSET for dc1. +- **The 33 VIP addresses** (11 services x provider/admin/internal, `.50-.60`) exist ONLY + as hand-maintained literals in `overlays/vr1-dc1-vips.yaml`. They are internally + consistent with the bundle octet-for-octet, but they are not a projection of the apex. + +So a generator has nothing to pull for either. **Division of labour, corrected:** the +coupling MODELS the bands (as ip-ranges) and the VIP addresses (as scoped ip-addresses) +IN the apex, then GENERATES the overlays from them, so the overlay becomes a projection +of NetBox rather than a parallel hand-maintained truth. The per-DC values file is +therefore TRANSITIONAL -- it carries bands and VIPs by hand only until they land in the +apex, after which it shrinks to genuinely non-IPAM convention. + +**Options.** + +- **(A) Build the generator now and gate the dc1 deploy on it.** The forced dual-stack + reconciliation IS its first job, so hand-rendering then rebuilding pays twice. + Against: an unproven tool on the critical path of an otherwise-ready deploy; its + first output has nothing to diff against; MAC pins and VLANs are absent from the + record, so the tool cannot be complete on this timeline. + +- **(B) Hand-render both DCs dual-stack; build the generator for Roosevelt.** Fork 2's + lean, and "render once to learn the parameter surface" has real force. Against: + roughly 170 dual-family literals by hand, at the point the hand-render has already + produced two defects on the simpler v4-only artifact. + +- **(C) Split at the seam: build the BACK half now, add the FRONT half later. + RECOMMENDED.** Reconcile once, by hand, INTO a per-DC values file carrying every + plane CIDR, VIP band, app-to-VIP map (one reconciled dual-family string per app, + vault included) and role tag. Render the overlays from it with a thin deterministic + substitution step. Both DCs ship on it. The NetBox front half (record -> values file) + is added later without touching the back half, because the interface is frozen. + +**Why (C).** It captures the whole benefit -- one source of truth, no two lists to +drift -- without absorbing the front half's unknowns (VIP representability, MAC and +VLAN buildout, token handling, network reach), none of which are on this deploy's +critical path. It makes the front half testable BY REPRODUCTION: once the schema is +frozen, NetBox-generated values are validated by diffing against known-good +hand-authored values for a DC that already deployed. Option (A) has no such gate. And +the values file is not scaffolding -- it is the permanent seam between IPAM fact and +deployment convention in the target operating model (pinned separately). + +**Honest cost of (C):** the schema and substitution layer are written before the NetBox +schema is fully known, so the seam may need one refactor when the front half lands. + +**Eliminated by construction.** + +1. The VIP-overlay collision -- one reconciled `vip` per app per DC, emitted once; no + second overlay to race, so deploy input stops depending on overlay order. +2. Vault decorative HA -- an application scaled with an `hacluster` peer and no VIP + becomes a GENERATOR-TIME error instead of a silent runtime condition. +3. Family drift -- D-101's matrix applied once in code instead of by a human ~170 + times. The validator asserts family-per-plane-per-SITE, not mere presence: the + record currently carries GUA storage prefixes for `vr0-dc0` and `willamette` while + VR1 storage is ULA, so a generator selecting on `role` alone pulls the wrong family. +4. Reserved-range skew -- per D-074 the tenant-CIDR reserved set is the six plane + `/22`s (today sourced from `scripts/lib-net.sh` into `scripts/net_overlap.py` as + argv) plus the live-resolved `capi-mgmt-subnet` and the metadata range. Generating + the plane rows from the same values file removes the risk of onboarding a tenant + validated against the WRONG DC's planes. + +**DC-aware gates (part of this proposal).** `scripts/preflight.sh` validates the real +per-DC deploy input via `scripts/provider-bundle-check.py --overlay --dc`, not the base +bundle alone. `scripts/cloud-assert.sh` gains a per-hacluster VIP-owned check, so a +scaled application with no VIP fails rather than passes. Bootstrap carries the +controller-tag constraint. + +**Mechanism (front half, when built).** Extending `netbox/*.py` is preferred over a +third-party NetBox OpenTofu provider. The existing tools already establish the correct +posture: one script per SOURCE (`office1-record-dump.py` vs `prod-draft-dump.py`), +read-only, token from the environment, never embedded or printed. A provider must +additionally be mirrorable into the per-DC airgap mirror (D-107) or it is disqualified +on Roosevelt, and it would put a write-capable credential in the tofu run while +write-back is deferred (DOCFIX-195). Reads only; no write-back under this decision. + +**Scope boundary.** + +- IN: the six operator planes per DC, VIP bands, plane CIDRs, per-DC role tags, and the + generated per-DC overlays and tfvars. +- OUT -- TENANT SPACE, permanently. Per `docs/dc-dc-netbox-buildout-scope.md:128` + per-tenant `/60`s are Neutron-managed (D-016/D-074) and not enumerated in NetBox. Per + D-074 tenant private CIDRs are TENANT-CHOSEN and MAY OVERLAP, enforced only against + the reserved-range registry. NetBox models pools, aggregates and reserved ranges; it + never models a tenant's own networks. A maintainer extending the generator "one more + table" into tenant space would either fight Neutron or gate tenant provisioning on an + operator-side IPAM write, breaking the tenant self-service model this cloud is sold + on. +- IN: WRITES TO office1-netbox. DOCFIX-195 (operator ruling 2026-07-15) rules that + during VR1 `office1-netbox` takes ALL writes and `netbox.baldurkeep.com` stays a + READ-ONLY v1 reference with merge-back DEFERRED to end-of-deployment. Precedent tool: + `netbox/d120-compose-bands.py --commit` already loaded the D-120 bands into the apex. + (An earlier draft of this entry scoped apex writes OUT on a misreading of DOCFIX-195; + that would have scoped the coupling out of its own core job -- see the gap below.) +- OUT: writes to `netbox.baldurkeep.com` (deferred, DOCFIX-195; P2 merge-back). +- OUT: VLAN / space / fabric -- PERMANENTLY, not pending. office1-netbox holds zero VLAN + objects and that is CORRECT BY DESIGN: VR1 is untagged-per-fabric on every plane in + both DCs (D-133, superseding VR0's VID-103/104), so a VLAN object would document + tagging that does not exist on the wire. The bundle binds by SPACE NAME -> MAAS subnet + by CIDR, never by VLAN/fabric/subnet id. VLAN/space/fabric is MAAS-sourced; NetBox is + IPAM only. +- OUT: MAC pins. They already have a working source of record and are drift-free across + substrate `main.tf` <-> `lib-hosts.sh` `HOST_BOOT_MAC` <-> bundle ovn-chassis <-> + discovery. They stay substrate/lib-hosts-sourced; this is not a NetBox gap. + +**Roosevelt-delta (A1).** Roosevelt is N bare-metal DCs. Hand-rendering is O(N) human +renders of a ~170-literal dual-family artifact; a generator plus one values file per DC +plus one validator is O(1) tooling and O(N) data. The transferable artifact is the +triple: renderer, values file, validator. This is why the back half is worth building +even while the front half waits. + +**Artifact shape (RULED 2026-07-25 -- operator ruling, GA-R5).** Question as presented: +there is no `vr1-dc0-vips.yaml`; dc0's VIPs are inline in `bundle.yaml` while dc1 has an +overlay -- under the family ruling both DCs need v6 legs, so the asymmetry must break: +(i) symmetric (extract dc0 to its own overlay, base becomes VIP-free) or (ii) asymmetric +(dc0 v6 legs into base, two renderer output modes forever)? Operator utterance, verbatim: +"Your question about 1 or 2, go with 1. Base bundle and everything else is applied via +overlay." Effect (option (i), symmetric per-DC overlays): `bundle.yaml` becomes VIP-free +and holds topology only; every per-DC value, dc0's included, arrives via overlay. dc0's +VIPs are extracted from base into `overlays/vr1-dc0-vips.yaml`. The renderer therefore has +ONE output shape per DC and Roosevelt's Nth DC is the same shape as its first. + +**PREREQUISITE, sequenced FIRST (per the recon's sequencing insight).** Extend +`netbox/sandbox-fidelity-check.py` BEFORE the coupling is built. Today it covers only the +D-115/D-117/D-120 delta and diffs the FROZEN 90-prefix `netbox.baldurkeep.com` draft, not +the current 139-prefix office1-netbox apex; it is blind to D-124 and D-134. It gates every +apex reconciliation this coupling depends on, and the coupling's whole value proposition +is eliminating the drift those reconciliations chase. An unextended checker cannot verify +the apex mutations in the gap list above, so building the generator first would mean +writing to the apex with no trustworthy fidelity gate. + +**A hard tooling coupling nobody has flagged.** `scripts/lib-net.sh:59` sets +`VIP_COUNT_EXPECT=11`, consumed by `scripts/pre-flight-checks.sh` at :79 (warn on vip +line count) and :99-102, where a count mismatch is a **fail**, not a warn. Adding vault's +VIP takes the count to 12, so `VIP_COUNT_EXPECT` must move to 12 in the same change or +the gate fails. Note that CURRENT-STATE flags `pre-flight-checks.sh` as VR0-frozen (no +`$DC` selector), so whether it gates VR1 today needs confirming -- but the constant is in +the shared library and any DC-aware successor inherits it. **Sibling constant (flagged by +the 2026-07-25 pack review, missed by the pack):** `scripts/lib-net.sh:58` sets +`VIP_OCTET_MAX=60`, consumed by the octet range-check at `pre-flight-checks.sh:81`; vault's +`.61` violates it, so `VIP_OCTET_MAX` must bump alongside `VIP_COUNT_EXPECT` (`.61` is +band-legal per the D-134-amended `.50-.99` VIP band -- the fix is the constant, not the +octet). A second stale `VIP_COUNT_EXPECT=11` restatement lives in D-134's band-table prose, +and `lib-net.sh:157` UNSETS both for the vr1-dc1 arm. A generator emitting the VIP set +should DERIVE this count rather than carry it as a second literal. + +**Gate relationship.** This decision does NOT gate the dc1 deploy. What the family +ruling gates, and what must be resolved before deploy, is three items: (1) the vault +VIP plus its band octet (a D-020 conformance repair, OPS not ARCH); (2) the octavia +family sub-ruling; (3) the reconciled dual-family VIP literals for both DCs, in the +symmetric overlay shape above. The subcarve is NOT among them -- D-111 (ADOPTED +2026-07-11) already ratifies the per-plane v6 subcarve and the values are imported for +both DCs. + +**Open questions (operator).** + +1. Metal VIP host addresses come straight out of the plane `/64` and share it with node + addresses; D-111's VIP-dedicated `/64` at `:11` exists only on the provider/GUA leg. + Confirm no MAAS deployed-node band claims the VIP octet on the metal planes. A + collision is repaired by a D-111 amendment (a metal VIP sub-range), not by values. +2. Octavia family. D-101 places `lb-mgmt` in the IPv6-only ULA group, but + `overlays/dc-dc-ipv6-family-matrix.yaml:63-82` documents live upstream IPv6 + breakage on that path (LP #1911788, #1913409). Recommendation: dual-stack octavia's + API endpoint like its ten peers, keep `lb-mgmt` v4 as a logged D-101 exception, on + the ruling's own standard -- a path with open upstream bugs is not where IPv6-only + "makes sense". This is an exception to an ADOPTED decision and is the operator's. +3. Does the reserved-range registry become a first-class NetBox object, or continue to + be derived (plane rows from prefixes; `capi-mgmt-subnet` and metadata live-resolved + per D-074/D-035)? Deriving is recommended; D-074's stated gain is that NetBox's + uniqueness duty shrinks to the routed space. Operator has asked for more information + before ruling. +4. Values-file format and location; and whether generated artifacts are COMMITTED + (reviewable diffs, recommended) or generated at deploy time (unreviewable). +5. Automation runner, event delivery, and status-back (CORRECTED 2026-07-25 -- the + earlier "GitBucket has no runner" gap framing is VOID). **Jenkins IS the runner** -- + operator-side and already running (v1 K8s + app deploys); nothing is stood up and no + git-host / Actions-style runner is required. Carry-forward: this is the revisit trigger + for the deferred "Jenkins CI poll job" at `docs/handoff-20260703-open-items.md:159`. The + real requirements are TWO, NEITHER yet verified against the running GitBucket instance + (do NOT promote to a SpryGit must-have until checked): (a) **reliable event emission** -- + webhook delivery with retry, a delivery log, and manual replay (a silently dropped + webhook renders nothing and nobody notices -- worse than no automation); (b) a + **status-back API** -- Jenkins writes the `provider-bundle-check` and `repo-lint` + verdicts onto the merge request as commit status/checks, so the RENDERED-DIFF approval + gate (the operating model's gate) can actually see the validator verdict -- this is the + requirement that protects the gate. PLACEMENT is OPEN (operator ruling required): the + Jenkins job on `voffice1` for VR1 (D-128 already routes Plane 2 -- MAAS/NetBox/inner + tofu -- there, and the render reads Office1-side NetBox + git), with per-DC utility-node + agents as the Roosevelt shape once D-132 ratifies -- BUT D-132 is PROPOSED and per-DC + utility nodes are NOT built in VR1 (D-134's .4-.49 band is reserved for FUTURE nodes; + D-135's mirror at .4 is a RACK-HOST service address, not a node); and whether an Office1 + job emitting BOTH DCs' overlays brushes D-105's cross-DC roll-up prohibition. The HTTPS + credential is a security-ledger row, not part of this decision. + +**Related:** D-020 (dual provider+metal VIPs -- NOT D-036, mis-cited as such in SEVEN +live sites; see DOCFIX-201 in this session's changelog), D-100/D-101 (addressing, +families, mesh), D-111 (per-DC v6 subcarve, ADOPTED), D-052/D-053 (bindings, six-plane +split), D-104 + 2026-07-24 amendment (controller VM), D-107 (per-DC airgap mirror), +D-108 (cross-DC), D-119 (region-qualified tags), D-121 (Option C + R-3), D-016/D-074 +(tenant CIDRs), D-035 (capi-mgmt reserved), D-067 (barbican-vault on metal-internal), +D-110 (repo has moved; never hardcode its path), D-133 (flat per-NIC carve, no VLAN +stack -- why VLANs are correctly absent from the apex), D-134 + its 2026-07-23 +AMENDMENT (the contiguous band table; the amendment is authoritative and the original +table above it is superseded), D-124 (rack statics), D-131 (DNS forwarder .3), D-135 +(mirror .4), D-105 (per-DC-only, cross-DC roll-up prohibition -- open Q5 placement), +D-128 (Plane-2 operating model -- the VR1 render/Jenkins host), D-132 (per-DC MAAS +topology, PROPOSED -- the Roosevelt per-DC-agent shape), DOCFIX-195 (apex takes all VR1 +writes; baldurkeep merge-back deferred), +BUNDLEFIX-052. Companion audit records: `docs/audit/decision-recon-20260725.md`, +`docs/audit/stage5-expansion-review-20260724.md`, +`docs/audit/committee-20260724-track2-bundle-render.md`. +