# 2026-07-22 -- G12 [V] leg: dc1 apex write + substrate build step A (session changelog)

Session scope: continue the G12 [V] leg from the reconciled tree (predecessor
delivery landed as changelog-20260721-g12-dc1-authoring.md items 3-4, commit
`d2bf743`): the one owed apex write, tfvars, and the gated outer build.
Branch: `dc-dc-g12-dc1-substrate`. One changelog for the session (GA-R2/D1);
every item carries its revert.

## Item 1 -- apex `--commit` EXECUTED (the one owed dc1 apex write; operator-gated)

Same-session read-only preflight FIRST (verify-before-mutate): dry-run re-run
against the live apex = would-create exactly 2, already-present 0 --
`docs/audit/dc1-rack-import-dryrun-20260722.txt`, identical to the 07-21
preflight. Then the gated write (piped to office1-netbox over ssh; token
sourced on-host from `/root/netbox-secrets/api.token` per
`creds-manifests/vr1-office1.manifest`, value never printed or brought into
context):

- CREATED `172.31.0.4/30` (prefix id=139) role=transit scope=dcim.site:vr1-dc1
- CREATED `10.12.68.2/22` (ip id=4) dns=vvr1-dc1

Post-commit idempotency read-back: would-create 0 / already-present 2. Both
runs captured in `docs/audit/dc1-rack-import-commit-20260722.txt`. Values
verbatim from the D-124 amendment (ruled 2026-07-21); exactly the two objects
the dc0 precedent registered.

Revert: delete the two apex objects by id (prefix 139, ip-address 4) via the
NetBox UI/API on office1-netbox; delete the capture files.

## Item 2 -- vr1-dc1 service keypair MINTED (manifest-prescribed at-deploy step)

`~/vr1-dc1-creds/vr1-dc1_svc_ed25519{,.pub}` generated on vcloud (ed25519,
modes 600/644) per `creds-manifests/vr1-dc1.manifest` ("MINTED AT DC DEPLOY").
`bash scripts/creds-audit.sh vr1-dc1` = CLEAN. D-126 option (a) per-env key;
pubkey feeds vvr1-dc1 cloud-init (item 3), private half jumphost-local.

Revert: remove both key files (couples to item 4 -- vvr1-dc1's cloud-init
authorizes this pubkey; re-mint requires a seed rebuild).

## Item 3 -- dc1 tfvars authored (LOCAL, gitignored -- recorded here, not committed)

`opentofu/d124-rack.auto.tfvars` gained the five vr1_dc1_* values, verbatim
from the D-124 amendment's own tfvars line (rack_metal_admin_ip=10.12.68.2,
rack_transit_ip=172.31.0.6, rack_transit_prefix=30,
rack_transit_peer_ip=172.31.0.5) + `vr1_dc1_ssh_pubkey_path` to the item-2
pubkey. Stale "dc1 DEFERRED" header comment replaced. `tofu fmt` applied;
`scripts/opentofu-validate.sh` PASS (all roots).

Revert: remove the vr1_dc1_* block from the local file.

## Item 4 -- outer apply: dc1 substrate step A (operator-gated, logged, saved-plan exact)

Preconditions measured in-session: host RAM 1007 GiB with 450 GiB committed
(voffice1 32 + edge 2 + vvr1-dc0 416) -> +416 GiB fits with ~140 GiB headroom;
vCPU 234/256 post-apply; dc-dc-whole-host-budget 13/13 PASS.

Saved plan `tfplan-dc1-20260722` = **5/0/0 exact** (vr1-dc1-uplink network +
vvr1-dc1 domain/disk/seed/cloudinit; ZERO touches to live resources) --
capture `docs/audit/outer-plan-20260722-dc1-substrate.txt`. Applied via the
saved plan under a per-command as-executed wrap
(`~/as-executed/2026-07-22-dc1-deploy.log`; index row added): **5 added, 0
changed, 0 destroyed**. Convergence re-plan = **zero diff**
(`docs/audit/outer-plan-20260722-postdc1-converged.txt`). Live verify:
vvr1-dc1 RUNNING (Id 8), voffice1/office1-opnsense/vvr1-dc0 untouched and
running; vr1-dc1-uplink + both dc1 mesh legs active.

Revert: `runbooks/dc-dc-teardown-rollback.md` decision tree; the targeted
destroy set is the five applied resources (module.vvr1_dc1.* +
module.vr1_dc1_uplink.*); delete the plan/capture files and the tfplan.

CORRECTION (same session): commit `0bd6342` accidentally included the spent
binary saved plan (repo-lint L1 x2 -- the lint's exit code was masked by a
pipeline; owned). The file deviated from the `.gitignore` naming convention
(`opentofu/**/*.tfplan`) -- saved plans must be named `*.tfplan`. Removed in
the follow-up commit; lint back to 0-fail. The applied-plan record remains
the two dated captures.

## Item 5 -- cloudinit-vm interface_macs port + voffice1 dc1-transit NIC (gated apply)

What: `modules/cloudinit-vm` gained `interface_macs` (ported verbatim-in-intent
from `modules/node-vm`, same validations; harness +4 cases, cloudinit-vm 10/10)
-- the 2026-07-20 voffice1 Kea/MAC-regen incident class, closed at the module
layer. Root wiring: `module.voffice1` NIC3 -> `mesh-vr1-dc1-office1` with all
three MACs pinned (NIC1/NIC2 = measured live values; NIC3 = pre-pinned
`52:54:01:d1:fe:01`, dc1 scheme, fe = region-side); `module.vvr1_dc1` pin-adopts
its two measured step-A MACs (standup DoD invariant). Saved plan
`voffice1-dc1nic-20260722.tfplan` = **0/2/0, zero replaces**, all five MAC
values verified in the diff (`docs/audit/outer-plan-20260722-voffice1-dc1nic.txt`);
applied logged (trap 1e: BOTH domains bounced -- presented as such). Post-bounce
battery ALL PASS: 4/4 domains running, voffice1 MACs exactly as pinned, Kea
lease 10.10.0.20 intact, region dhcpd up, netbox 302 + tailscale up (nested LXD
VMs self-recovered, ~3 min), dc0 rack: dhcpd + dc0-node-dns + dc0-rack-legs
active, forwarder SOA answers. Convergence re-plan ZERO DIFF.

Revert: remove NIC3 + interface_macs from main.tf, revert the module +
harness (`git checkout` the four files), re-plan/apply (bounces again).

## Item 6 -- voffice1 dc1 transit leg (in-guest netplan, gated)

`/etc/netplan/61-transit-dc1.yaml` (0600) on voffice1: enp3s0 static
`172.31.0.5/30` (per-DC drop-in; dc0's `60-transit.yaml` untouched). Verified:
enp3s0 UP with .5/30, ping 172.31.0.6 = 0% loss, first ssh into vvr1-dc1 over
the transit with the dc1 key OK (hostname vvr1-dc1, nested-KVM module
present, region route + 10.10.0.20 reach pre-staged by step-A cloud-init).
Revert: remove the drop-in + `netplan apply`.

## Item 7 -- rack bootstrap EXECUTED (site-headend-install, dc1-parameterized)

Enrollment secret staged region->rack as a host-to-host pipe (0600
`/root/region-enroll.secret`, 32 bytes verified by count, value never in
context). Snap system proxy set to `http://10.10.0.20:8000` (dc0-measured
mechanism; env proxy for apt). Dry-run FIRST exposed that the script's
host-nodes DEFAULTS are dc0-flavored -- all overridable by existing flags;
run with `--wan-bridge br-vr1-dc1-wan --inner-pool-path
/var/lib/libvirt/vr1-dc1-inner --opnsense-base ...26.7...`. First real run
FAILED exit 4: stale base-image apt index -> 404s on superseded debs via the
proxy; fixed with `apt-get update`, idempotent re-run **exit 0**: rack
ENROLLED, nested KVM on, inner pool + AppArmor, SEC-010 rack end, WAN bridge
verified with enp2s0 enslaved. Region-side verify: `maas admin
rack-controllers read` lists **vvr1-dc1 (nmpcq4)**. SEC-010 REGION end:
voffice1's `/etc/nftables-sec010.nft` extended with the enp3s0 drop pair
(dc0 idiom), table reloaded clean, ruleset = both legs dropped.
`--check` capture: `docs/audit/dc1-stepB-check-20260722.txt` (sole [--] =
opnsense base, item 8).

QUEUED findings (logged, not fixed mid-step): (a) site-headend-install
NOTE/hint prose still says "26.1" and "vr1-dc0-substrate" even when
parameterized for dc1 (cosmetic, misleads operators); (b)
`nftables-sec010.nft` reload is NOT idempotent -- `nft -f` on a live table
appends, so a service restart duplicates rules (observed; cleaned via
`nft delete table` + restart; the file should flush first); (c)
`opnsense-prep-image.sh` dies on `BASH_SOURCE[0]: unbound variable` when
piped via `bash -s` under `set -u` (ran into it before the mirror guard).

Revert: rack side -- `snap remove maas`, remove nftables-sec010 +
sec010-fw + kvm-nested modprobe + inner pool dir on vvr1-dc1; region side --
remove the enp3s0 pair from voffice1's nft file + restart unit; delete
the staged secret file.

## Item 8 -- OPNsense 26.7 base staged on dc1 (proven-artifact copy)

`opnsense-prep-image.sh` requires `OPNSENSE_MIRROR_BASE` (deliberately not
repo-recorded; mirrors change). Instead of choosing a mirror, the dc0 rack's
OPERATOR-RULED and boot-PROVEN 26.7 base was streamed rack->rack through the
jump path (direct rack-to-rack is SEC-010-dropped, correctly) and
sha256-verified on arrival against the dc0 source hash
(`3981821e3a3c...476627d`). Same bits that passed the D-112 boot path on dc0.
Revert: delete `/var/lib/libvirt/vr1-dc1-inner/opnsense-26.7-nano.qcow2`.

## Next (gated, not run here)

Inner apply FROM voffice1 (D-128 Plane 2; `opentofu/vr1-dc1-substrate/`,
54 MACs pre-pinned) after the step-B `--check` re-run is EXIT 0; then the
D-125 egress isolation gate on `br-vr1-dc1-wan`, edge bootstrap (D-112(c) +
D-113(a2) on 26.7), rack standup DoD (dc-rack-net.sh install dc1, forwarder
10.12.68.3, region-side DHCP on metal-admin, maas-node-power). Runbook +
CURRENT-STATE govern.
