Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260723-g12-dc1-edge.md

2026-07-23 -- G12 [V] leg: dc1 edge bootstrap + v4 addressing (session changelog)

Session scope: resume the G12 [V] leg from the 2026-07-22 close handoff (edge bootstrap onward). Branch dc-dc-g12-dc1-substrate. One changelog per session (GA-R2/D1); every item carries its revert. Live mutations gated (operator ran in manual permission mode -- see Item 0). Edge-phase mutations mostly run UNWRAPPED (approval prompt = the gate); the console bootstrap went through the ~/as-executed/2026-07-22-dc1-deploy.log wrap.

Item 0 -- permission-mode friction (recorded; no repo change)

The session opened in auto mode, whose classifier BLOCKS remote sudo/mutation shapes (matched by the project ssh * sudo * ask rule) instead of surfacing them for approval -- so it cannot deliver "operator approves each mutation." Resolved by switching to manual mode (alias for default; no classifier): allow-rules flow read-only rack probes, project ask rules prompt every gated mutation, deny rules + guard-destructive.py stay active. Session allow-rules for the dc1/dc0 rack ssh shapes were added to .claude/settings.local.json (the operator later broadened to ssh *). Revert: none (session-scoped settings; remove the added allow lines at close if desired).

Item 1 -- dc1 edge service keypair minted + manifest

~/vr1-dc1-creds/vr1-dc1-edge_ed25519 (0600/0644) minted on vcloud, creds-manifests/vr1-dc1.manifest extended (edge key + opnsense-api.txt), creds-audit vr1-dc1 CLEAN (5 entries). Revert: git checkout the manifest; delete the keypair.

Item 2 -- D-112(c) console bootstrap COMPLETE (edge SSH + service key)

Driver d112c-console-dc1.py (dc1 adaptation of the dc0-PROVEN v6, retrieved verbatim from vvr1-dc0:~/d112c-console.py): serial console -> factory root/opnsense -> shell -> ship+run bootstrap PHP (enable ssh, permitrootlogin, install dc1 edge pubkey, write_config) -> materialize PHP (local_user_set(), dc0 lesson v). Transcript ~/d112c-console-dc1.log on the rack: CONFIG-WRITTEN, AK-IN-CONFIG=152, USER-MATERIALIZED; /root/.ssh/authorized_keys (115 B) created (was absent). Edge banner confirms vtnet0 = LAN 192.168.1.1/24, vtnet1 = WAN (dc0 mapping). FINDING (logged, not fixed mid-step): the dc0 d112c-bootstrap.php on the rack required only config.inc, so my first bootstrap PHP threw Call to undefined function shell_safe() at config.inc:311 (in write_config()->make_config_revision_entry()) and aborted BEFORE writing -- this is dc0 lesson (iv) (shell_safe lives in util.inc, measured on dc0's identical 26.7 image). Added require_once("util.inc"); two diagnostic lines made csh-safe (edge root shell is tcsh; 2>&1 = "Ambiguous output redirect"). Re-ran clean. The proven dc0 .b64 bootstrap artifact is missing this include -- a real gap if replayed; queue: fold util.inc into the canonical console bootstrap payload. Revert: config-only; re-run factory reset on the edge to undo (or leave -- it is the intended state).

Item 3 -- key-only SSH to the edge VERIFIED (D-112(c) proof)

Reached the edge via a ProxyCommand chain from vcloud (rack key for the jump, edge key for the final hop -- per-hop keys; a plain -J applies one key to all hops and the rack rejected it). root@192.168.1.1 -> uname -r = 15.1-RELEASE-p1, ifconfig -l = vtnet0/vtnet1/lo0/enc0/pfsync0/pflog0. Second D-112(c) datapoint after dc0.

Item 4 -- D-113(a2) API key MINTED + smoke test PASS

Interim reach leg 192.168.1.2/24 added on the rack provider-public bridge virbr4 (measured this session) to reach the factory edge LAN. Edge-config scripts + edge key staged on the rack (dc0 method; the mint script SSHes edge-direct, no ProxyJump). Key minted via the vendor opnsense-mint-apikey.php (apikeys->add() -- the GUI's own path); key/secret 80 chars, 173 B -> ~/opnsense-api.txt (0600, secret never printed). Smoke test from the rack: GET core/firmware/status -> exit 0, product_abi 26.7. First proof the D-113(a2) API path works on 26.7 for dc1. FINDING (logged): opnsense-bootstrap-apikey.sh's first scp to the edge failed once with scp: Connection closed -- TRANSIENT: the edge sshd was not ready in the moment right after the console bootstrap's configctl openssh restart (both default scp and scp -O succeeded minutes later; the edge is reachable and scp works). Unblocked via an ssh-pipe mint (vendor PHP shipped by ssh 'cat >', retrieved by ssh -n 'cat' > file) -- during which a missing -n on the edge ssh calls let them EAT the rack bash -s heredoc stdin (mint ran, retrieve/cleanup lines were swallowed); re-run with -n retrieved the already-minted key cleanly. Queue: a post-restart sshd readiness wait / one scp retry in opnsense-bootstrap-apikey.sh. Revert: delete the API key via the edge API + remove ~/vr1-dc1-creds/opnsense-api.txt.

Item 5 -- edge v4 addressing (WAN then LAN; reach-handoff)

opnsense-set-interface-v4.sh run VERBATIM from the rack (plain scp works after warm-up; no repo edit). Values confirmed from committed lib-net.sh (provider-public 10.12.64.0/22 gw 10.12.64.1, D-124 amendment) + the D-124 uplink 172.30.3.0/24.

  • WAN first (script ordering rule): dhcp -> 172.30.3.2/24 gw 172.30.3.1, applied + read back on the kernel (vtnet1). Edge egress VERIFIED: ping 1.1.1.1 = 0% loss.
  • Permanent rack leg 10.12.64.2/22 added on virbr4 (so the rack keeps reach after the edge LAN moves).
  • LAN last: 192.168.1.1/24 -> 10.12.64.1/22 (vtnet0). The apply drops the 192.168.1.1 session mid-command (expected; the foreground ssh hung on the dead interface and was TaskStop-ped -- config was already saved+applied). Verified independently at the NEW LAN via the rack's 10.12.64.2 leg: ifconfig vtnet0 = inet 10.12.64.1 netmask 0xfffffc00 (/22); API GET core/firmware/status at 10.12.64.1 -> exit 0, abi 26.7.
  • Interim 192.168.1.2/24 removed; virbr4 now carries only 10.12.64.2/22 (dc0 end-state exactly). Revert: re-address LAN/WAN back via the same script; the leg adds/dels are ip addr add/del (non-persistent).

Item 6 -- creds consolidated + rack edge-key WIPED (SEC-015 closed transient)

API creds pulled rack -> ~/vr1-dc1-creds/opnsense-api.txt (0600, 1 key + 1 secret). Rack edge-key copy shred -u'd; staged edge-config scripts + console driver + rack creds copy removed (ls confirms none remain). The non-secret console transcript ~/d112c-console-dc1.log retained as an audit artifact. SEC-015 opened for the transient edge-key-on-rack exposure (minted->wiped same phase; disclosed). Revert: n/a (cleanup).

Milestone

dc1 edge fully bootstrapped + addressed -- D-112(c) console bootstrap and D-113(a2) REST API both PROVEN on 26.7 (second datapoint after dc0). Edge: WAN 172.30.3.2/24 gw 172.30.3.1 (egress 0% loss), LAN 10.12.64.1/22 (the ruled provider-public gateway), API answering at the new LAN.

Item 7 -- dc-rack-net.sh dc1 site-table arm + harness (D-131 sub-1 delivery)

Added the dc1) arm to scripts/dc-rack-net.sh (the STANDING per-DC forwarder

  • rack-legs pattern), MEASURED/apex-committed values, network-name keyed (no virbrN literal -- T6 holds): vr1-dc1-metal-admin 10.12.68.2/22 rack MAAS/DHCP leg (apex-committed rack IP) vr1-dc1-metal-admin 10.12.68.3/22 node-DNS forwarder listen alias (D-120 static) vr1-dc1-provider-public 10.12.64.2/22 edge-LAN leg (measured live on the bridge 2026-07-23) DNS_LISTEN=10.12.68.3 DNS_UPSTREAM=10.10.0.20 (region BIND over transit). Harness tests/dc-rack-net/ extended T15-T18 (dc1 identity + MEASURED-tag); 18/18, gauntlet ALL GREEN (76), repo-lint 0-fail. This forwarder is the D-131 fix that pre-empts dc0's commissioning SERVFAIL on the isolated rack. Revert: git checkout scripts/dc-rack-net.sh tests/dc-rack-net/run-tests.sh.

Item 8 -- dc-rack-net install on the dc1 rack (persistent legs + forwarder)

install dc1 run on the rack (operator-gated). Check PASS 10/10 (docs/audit/dc1-rack-net-install-20260723.txt): persistent metal-admin legs 10.12.68.2/22 + 10.12.68.3/22 on virbr6, provider-public 10.12.64.2/22 on virbr4, dc1-rack-legs + dc1-node-dns enabled+active. Behavioral proof: the forwarder answers authoritative maas-internal SOA via region BIND -- the D-131 fix that pre-empts dc0's commissioning SERVFAIL. Revert: dc-rack-net.sh install is idempotent; to undo, disable the two units + remove the generated files + the interim legs.

Item 9 -- region-side MAAS config for dc1 metal-admin (DHCP + DNS + range)

MAAS auto-discovered dc1 planes from the rack interfaces: metal-admin 10.12.68.0/22 = subnet id 11 (VLAN fabric 142/vid 0), provider-public 10.12.64.0/22 = subnet id 10. On subnet 11 (all operator-gated):

  • D-120 dynamic range 10.12.68.100-10.12.68.200 created (iprange id 3) -- the ruled band applied to dc1's CIDR.
  • D-131: dns_servers=10.12.68.3 allow_dns=false (nodes resolve via the rack forwarder, not MAAS -- the SERVFAIL fix).
  • DHCP: VLAN fabric 142/vid 0 dhcp_on=true primary_rack=nmpcq4. Verified BEHAVIORALLY (dc0 lesson -- not the self-report): dhcpd RUNNING on the rack (dhcpd -4 ... virbr6, dhcpd.conf freshly generated); no Temporal incident (the dc0 region restart fixed it fleet-wide). Revert: maas admin vlan update 142 0 dhcp_on=false; subnet update 11 dns_servers= allow_dns=true; ipranges delete 3.

Item 10 -- dc1 enlistment PROVEN (canary)

virsh reset vr1-dc1-control-01 -> the node PXE-booted, got DHCP from nmpcq4, and ENLISTED in MAAS ~2 min later (machine count 11 -> 12). The DHCP->PXE->enlist chain works end to end for dc1; with the forwarder pre-installed, dc0's two stacked commissioning faults are pre-mitigated. Revert: n/a (enlistment; the machine is deleted/re-commissioned as needed).

Item 11 -- SEC-016 ruling: dedicated dc1 MAAS->libvirt power key (GA-R5)

Commissioning needs the region MAAS snap to SSH the dc1 rack libvirt (power_type=virsh). The dc0 rack authorizes the SEC-012 MAAS key; the dc1 rack does not. Operator RULED (AskUserQuestion, exact utterance): "Mint a dedicated dc1 power key" -- per-DC isolation, NOT cross-DC reuse of SEC-012. Recorded as SEC-016 (security-ledger). Wiring (dependent work, next): mint vr1-dc1-maas-power_ed25519; authorize its pubkey on the dc1 rack; install privkey in the region MAAS snap + snap ssh config Host 172.31.0.6; give the maas-node-power script's virsh reach from voffice1 via the dc1 SERVICE key (dc0 split). Then maas-node-power.sh dc1 (dry -> --commit), commission 9/9. Revert: deauthorize the pubkey on the rack, remove the snap key + config.

Next (gated)

Wire SEC-016 dedicated power key -> maas-node-power.sh dc1 (dry then --commit) (provider-public 10.12.64.2/22, metal-admin 10.12.68.2/22, forwarder 10.12.68.3 -> region BIND 10.10.0.20) + harness update, then install dc1; region-side MAAS (metal-admin dns_servers=10.12.68.3 allow_dns=false, DHCP primary_rack=nmpcq4, D-120 range 10.12.68.100-.200); maas-node-power.sh dc1 arm; commission 9/9 to Ready (D-121 Option C); then G12 close-out (gauntlet, repo-lint, consolidation, memory review, skill sweep, operator-gated merge to main).