Worker: W0.4 (target-topology options). Feeds: the Phase-0 OPERATOR GATE (SCOPE-AND-EXECUTION-PLAN.md Section 4). Scope discipline: READ-ONLY; every claim below cites path:line or a named durable record; anything not resolvable from the repo is marked OWED rather than invented (hard rule 2).
vcloud (outer libvirt, qemu:///system) -> vvr1-dcN (containment VM, inner libvirtd) -> node VMs. The outer root creates vvr1-dc0/vvr1-dc1 (opentofu/main.tf:410-519,537-623); the INNER root (opentofu/vr1-dc0-substrate/main.tf:1-266) creates the 6 planes + WAN bridge + edge + 12 node VMs (9 D-121 role nodes + the D-104 juju-controller vr1-dc0-juju-01 + the D-132 MAAS-region vr1-dc0-maas-01 + the D-129(iii) Tailscale router vr1-dc0-tailscale-01, opentofu/vr1-dc0-substrate/main.tf:96-247) via a qemu+ssh provider dialled FROM Office1 (opentofu/vr1-dc0-substrate/main.tf:12-22).SCOPE-AND-EXECUTION-PLAN.md:34-36): collapse to vcloud (libvirt) -> node VMs directly.docs/archive/model-a-fallback-plan.md ("Model A", git tag model-a-fallback at 114d392, R-3-compliant, tofu validate 11/11 modules). Both options below are read against that precedent rather than invented from scratch.docs/design-decisions.md:7064-7126, RULED 2026-07-30): the cloud-facing Juju client + openstack CLI (phase-03..phase-06) MUST run from a host with L3 reach to the DC's node planes (voffice1 cannot reach them; SEC-010/D-052/D-125 forbid routing there). CURRENT STATE: that host is vvr1-dc0 itself -- "the dc0 rack" (docs/CURRENT-STATE.md:1046,3014-3016; lib-hosts.sh:212-213 shows vvr1-dc0 holds a live metal-admin address, qemu+ssh://jessea123@10.12.8.2/system, the D-134 rack-utility .2 -- distinct from .5 juju-01, .6 maas-01, .7 tailscale-01).docs/design-decisions.md:7135-7204, RULED 2026-07-30): each DC gets its OWN MAAS region (now a node VM, vr1-dc0-maas-01), not a migrated/shared Office1 region.docs/security-ledger.md:79,81): "the rack" is now a credential-bearing host -- it holds the DC-scoped MAAS admin key (SEC-018) and the per-DC Juju service key (SEC-028), OPEN rotation obligations, and an explicit per-DC isolation requirement ("each DC's client host receives ONLY THAT DC's credential").vcloud (host, L0, qemu:///system)
|-- vr1-dc0-client small VM (4 vCPU / 8 GiB / 80 GiB, D-138 role only -- see (d))
| legs: metal-admin + office1<->dc0 transit; NO nested libvirt,
| NOT a hypervisor for anything -- a flat sibling VM like any other
|-- vr1-dc0-control-01..03 node VMs (16/65536/150) \
|-- vr1-dc0-compute-01..02 node VMs (12/49152/100) \ vcloud-level libvirt siblings,
|-- vr1-dc0-storage-01..04 node VMs (8/24576/550) > attached DIRECTLY to the 6
|-- vr1-dc0-juju-01 node VM (4/8192/100) / vcloud-level plane networks
|-- vr1-dc0-maas-01 node VM (4/8192/150) /
|-- vr1-dc0-tailscale-01 node VM (2/2048/25) /
|-- vr1-dc0-opnsense edge (2/2048, 2-NIC: provider-public LAN + vr1-dc0-wan WAN)
|-- vr1-dc0-{provider-public,metal-admin,metal-internal,data-tenant,storage,replication}
| 6 isolated-L2 libvirt networks (dc-planes), AT VCLOUD LEVEL
|-- vr1-dc0-wan NAT /24 simulated ISP uplink, AT VCLOUD LEVEL (site-wan, direct)
`-- mesh-vr1-dc0-office1 transit leg (office1 <-> dc0), unchanged
nesting depth = 2 (vcloud -> node VM -> nova KVM guest) <- VR0-PROVEN, same as Model A
site-down = scripted group-destroy of the vr1-dc0-* domain set (Model A's mechanism)
Directly on vcloud's outer libvirt provider (qemu:///system), as siblings -- identical to docs/archive/model-a-fallback-plan.md:24-39's Model A shape, but with the 3 utility node classes (juju-controller, MAAS region, Tailscale router) added as additional flat siblings using the SAME modules/node-vm bodies already authored in opentofu/vr1-dc0-substrate/main.tf:250-265 (only the provider target changes, per that file's own header note at lines 1-10: "All module bodies are UNCHANGED from the outer root -- only the provider they run against moved").
Back at vcloud level as modules/dc-planes outputs, reversing the D-123 move documented at opentofu/main.tf:22-33 ("the 6 vr1-dc0 planes MOVED to the INNER root ... under Model B"). lib-net.sh's PLANE_CIDRS/PLANE_NAME (unchanged, scripts/lib-net.sh:21-30) and the D-101/ D-134 CIDR values stay IDENTICAL -- only the libvirt network objects' host moves, not their IPAM identity. metal-admin remains the PXE/boot plane, NIC_PLANE_ORDER unchanged (lib-hosts.sh:69).
opentofu/main.tf:125-141).module "vr1_dc0_uplink" NAT + wan-bridge module + the IP-less 2nd NIC + br-vr1-dc0-wan netplan bridge, opentofu/main.tf:360-397,505-518) is REMOVED. The DC edge's WAN attaches to a vcloud-level NAT directly, exactly like Office1's edge does today (opentofu/main.tf:99-117) -- this is Model A's item 8 (docs/archive/model-a-fallback-plan.md:67-75): "same 172.30.2.0/24 ... no uplink NIC, no bridge, no wan-bridge module ... NO re-address needed".lib-hosts.sh:70-72), a per-node fact independent of where the node's hypervisor sits.A dedicated, small, per-DC client VM on vcloud libvirt, occupying the ROLE Model A's original rack headend already had by construction: two legs (metal-admin + office1 transit, docs/archive/model-a-fallback-plan.md:25-26), 4 vCPU / 8 GiB / 80 GiB. Model A's own headend was authored BEFORE D-138/D-132 existed and was scoped as "MAAS rack headend" only -- this option REPURPOSES that same artifact shape to also satisfy D-138 (cloud-facing client execution) and SEC-026/SEC-028 (the DC-scoped credential residency), because it already has the one property those rulings require: an L3 leg on metal-admin. It is explicitly not a hypervisor for anything -- no expose_nested_virt, no inner libvirtd, no nodes attached to it -- so it does not reintroduce a container layer; it is a flat utility sibling, same class as vr1-dc0-juju-01. Per-DC credential isolation (SEC-026 "(1) ISOLATION IS THE LOAD-BEARING CONTROL") is preserved exactly as it is today, since this VM plays the identical role the current rack plays. maas-vm-host registration (Step 9, deferred DOCFIX-179) targets vcloud's own virsh, not this VM's (Model A table row 4, docs/archive/model-a-fallback-plan.md:48) -- MAAS discovers the vcloud-level node domains directly.
Directionally FREES host RAM, does not need it. Today's containment VM is sized to hold BOTH the node fleet AND its own containment overhead (opentofu/variables.tf:143-150: RAM raised 416->480 GiB per DC, "384 GiB node fleet + 96 GiB overhead"; the 96 GiB is inner-libvirtd/OS/page- cache overhead that exists ONLY because of the nesting). Flat placement removes that per-DC 96 GiB overhead layer and replaces it with the small client VM's 8 GiB -- a swing of roughly (96-8) x 2 DCs ~ 176 GiB of host RAM, all else equal. vCPU impact is smaller: Model B's overhead is 4 vCPU/DC (opentofu/variables.tf:137-141, unchanged since the RAM-only raise), and Model A's headend is also 4 vCPU, so vCPU is roughly a wash. This is a qualitative, repo-grounded direction, not a verified number -- see Section 4 OWED items: the sizing calculator (scripts/dc-dc-whole-host-budget.py) does not yet have flags for the 3 utility node classes (juju/maas/tailscale) added after its authoring, so an exact FIT verdict needs either extending it or hand-totaling before the operator relies on a number.
opentofu/vr1-dc0-substrate/, its own state file, its qemu+ssh provider dialled from Office1) is retired; its module bodies fold back into the outer root.opentofu/main.tf:301-331) goes away -- no bootstrap gate, no cross-root apply sequencing.site-headend-install.sh's "node-host mode" (nested libvirtd, kvm nested=1, inner pool, the SEC-010 nftables writer scoped to the inner host, D-125 --uplink-if/--wan-bridge verification) is retired; only whatever the small client VM still needs (metal-admin address assignment, the D-138 client role) remains, in a much smaller mode.docs/security-ledger.md:21) is nftables-sec010.nft + sec010-fw.service written onto vvr1-dc0's MEASURED interface names (enp1s0 transit / br-vr1-dc0-wan/enp2s0 uplink, captured live 2026-07-20). The new client VM is a DIFFERENT artifact with its own NIC-naming trap (opentofu/main.tf:473-481 documents this exact trap for vvr1-dc0's q35 shape) -- its transit-scoped FORWARD-drop has to be re-authored and re-measured, not copy-pasted.VIRSH_POWER_ADDRESS_FROM_OFFICE1/_FROM_DCREGION (lib-hosts.sh:212-213,246-250) currently point at the rack's transit/metal-admin address for qemu+ssh NODE POWER CONTROL through the inner libvirt. With nodes flat on vcloud, power control becomes vcloud's own local virsh (qemu:///system or a local-equivalent address) -- these constants and every consumer that reads them (maas-node-power.sh, the teardown runbook's virsh probes) need retargeting.virsh destroy vvr1-dc0" (opentofu/main.tf:405) reverts to Model A's scripted group-destroy of the vr1-dc0-* domain set (docs/archive/model-a-fallback-plan.md:36,64). This is a REAL regression against what D-123 was ruled for, not a free simplification -- flagged here so Phase 4's [ARCH] decision framing carries it honestly, per SCOPE-AND-EXECUTION-PLAN.md:197-200.opentofu/vr1-dc0-substrate/main.tf:87-245) needs a fresh capture pass once nodes are re-created flat -- moot for the 10.13 redeploy itself since the whole fleet is being torn down and rebuilt regardless.docs/archive/model-a-fallback-plan.md:97, 100: "nova-compute guests fail to boot or are unusably slow at 3x-nested KVM"; "exposenested virt=true ... destabilises the headend").qemu+ssh-from-Office1 provider dial for the substrate apply.wan-bridge module, the IP-less uplink NIC, the br-vr1-dc0-wan bridge, the "unprovable pre-apply" deploy-time gate at opentofu/main.tf:375-378) -- the DC edge's WAN becomes an ordinary direct NAT, one fewer bespoke module and one fewer "unprovable until you try it" gate.vvr1-dc0 and voffice1 -- each independently measured enp1s0/enp2s0 on first boot; flat placement needs this measured once, for the single small client VM, not for a containment VM AND its inner substrate).lib-net.sh plane CIDRs/names are untouched (still 6 planes/DC, same D-101/D-134 values) -- only the low-level libvirt/carve/power scripts (lib-hosts.sh's power-address constants, maas-node-power.sh, dc-node-carve) need retargeting, not the IPAM layer.Strongly POSITIVE. Bare metal has no hypervisor containment layer at all -- Roosevelt's node "VMs" are literal servers on the DC's physical planes, so flat placement is the closer bare-metal analog and nesting was always a simulation-only artifact (Model A's fallback doc never claimed Model B carried Roosevelt fidelity; its one stated advantage was the single-object site-down convenience, which is a simulation-only DR primitive, not a transferable property). The retained small client VM in (d) DOES transfer: D-138 names its own Roosevelt analog explicitly -- "each DC has a management entry point inside it; the NOC reaches that entry point ... the per-DC management bastion holds only its own DC's cloud credential" (docs/design-decisions.md:7121- 7124). So this option's client VM is not throwaway scaffolding; it is that bastion, rehearsed early, minimizing delta-to-Roosevelt exactly as SCOPE-AND-EXECUTION-PLAN.md:203-204 asks.
vcloud (host, L0) <-- juju/openstack CLI + the D-138/SEC-026/SEC-028 credential live HERE, | on the jumphost OS itself, via a new host-side NIC/veth onto the | (now vcloud-level) metal-admin plane bridge |-- vr1-dc0-control-01..03 / compute-01..02 / storage-01..04 / juju-01 / maas-01 / tailscale-01 | (same flat node-VM siblings as Option 1 -- (a)/(b)/(c) IDENTICAL to Option 1) `-- ... (planes/uplink/mesh identical to Option 1; NO small client VM)
Everything in (a)/(b)/(c)/(f)/(g) is IDENTICAL to Option 1 -- the only difference is (d): no per-DC client VM at all; the D-138 execution host collapses one layer further, onto vcloud's own OS.
The juju/openstack CLI, and the SEC-026/SEC-028 per-DC credentials, would live directly on the shared jumphost that ALSO runs Plane-1 substrate tooling for BOTH DCs and Office1 (docs/design-decisions.md:5354-5358, D-128 Plane 1). This inverts SEC-026's own stated control: "(1) ISOLATION IS THE LOAD-BEARING CONTROL: each DC's client host receives ONLY THAT DC's credential" (docs/security-ledger.md:79) -- on vcloud, BOTH DCs' credentials would sit on the one host that already has the widest blast radius in the whole project (it is the CLAUDE.md "live operations clone", governed by the PreToolUse guard and every permission ask rule). A credential-scoping boundary that today survives a "rebuild the rack VM" event would no longer exist to survive anything short of rebuilding vcloud itself.
Same node-fleet math as Option 1 (nodes are identical either way); this option additionally saves the ~8 GiB the small client VM would have cost. Negligible vs. Option 1's ~176 GiB swing.
No bare-metal analog exists for "run the DC client from the shared jumphost" -- Roosevelt's own management model is per-DC bastions (D-138's own analog, quoted above), which is what Option 1's small client VM already rehearses. Option 2 would need to be UNDONE and re-built as something like Option 1's shape at the pre-Roosevelt bare-metal test, making it throwaway work rather than transferable.
| Dimension | Option 1 (small per-DC client VM) | Option 2 (client on vcloud) |
|---|---|---|
| Nesting depth | 2 (same as Option 1's node placement) | 2 |
| D-138 execution host | dedicated, isolated, per-DC | shared jumphost, cross-DC |
| SEC-026/SEC-028 isolation | preserved (matches today's model) | WEAKENED (both DCs' admin-scoped MAAS keys land on one shared host) |
| Host RAM freed vs. today | ~176 GiB (2 DCs, qualitative -- see OWED) | ~184 GiB (marginally more) |
| Roosevelt-delta | POSITIVE -- the client VM IS the D-138-named per-DC bastion | NEGATIVE -- no bare-metal analog, throwaway |
| Git/spec precedent | Model A archive (docs/archive/model-a-fallback-plan.md), already validated 11/11 |
none -- net-new design |
| D-123 site-down benefit | lost (reverts to Model A's group-destroy) | lost, identically |
| New engineering needed | SEC-010 re-author for the new small VM; power-address retarget; MAC re-capture | same, PLUS a new host-side bridge/veth into a MAAS plane on vcloud itself (unprecedented surface) |
Option 1 (flat nodes + a small, single-purpose, per-DC client VM). Rationale, in order of weight:
SCOPE-AND-EXECUTION-PLAN.md's minimize-delta-to- Roosevelt framing directly.Framing note for the operator gate: "eliminate the container layer" is fully satisfied by Option 1 under the reading that matters -- no VM in this topology is a HYPERVISOR for another VM (no nested libvirt, no expose_nested_virt, no inner OpenTofu root). The retained small VM is a flat utility sibling, the same class of thing as the Juju-controller or MAAS-region node VMs already in the fleet, not a resurrection of the containment pattern. If the operator's intent is literally zero additional VMs of any kind, that is Option 2, with the tradeoffs above accepted knowingly.
scripts/dc-dc-whole-host-budget.py:66-67 carries a committed default it labels "MEASURED host budget" (256 vCPU / 1024 GiB / 10240 GiB), but its CURRENCY for the 10.13 redeploy (same hardware? any change since it was set?) needs a fresh read-only measurement on vcloud before any FIT verdict is finalized.--control/ --compute/--storage flags do not yet cover the 3 utility node classes added after it was authored (vr1-dc0-juju-01, vr1-dc0-maas-01, vr1-dc0-tailscale-01 -- opentofu/vr1-dc0-substrate/main.tf:133-245). Extending the script (or hand-totaling) and re-running --model A vs. --model B with the CURRENT 12-VM/DC roster is owed before citing a precise freed-capacity number in the Phase-4 change-set.