Purpose. A single followable checklist across the whole VR1 (two-DC virtual regional) buildout, spanning many sessions. docs/dc-dc-buildout-design.md is the WHY/WHAT (goals, gates, D-refs per phase); this doc is the HOW-TO-FOLLOW-IT sequence, cross-referenced to that plan, to D-100..D-110, and to what already exists in this repo vs what still needs to be authored. Update the "state" column as stages complete -- this doc is a tracker, not a one-time snapshot.
Governing constraint (unchanged throughout): minimize delta to Roosevelt. Reuse VR0 DC0 material wherever it transfers; the only deliberate no-Roosevelt-analog steps are the Section 9 shim register in the buildout design (OpenTofu node-VM creation, tc netem WAN simulation, single-unit controllers/rbd-mirror).
Nothing in Stage 1 onward starts until this clears. D-100..D-110 are ALL PROPOSED, not ADOPTED (see docs/design-decisions.md).
docs/dc-dc-buildout-design.md in full.size=3 vs a documented size=2 fallback (gated on Phase-0 disk math)tc netem link profile (same-metro lean vs a specific Roosevelt target)docs/design-decisions.md (same convention as every prior D-NNN ruling in this repo).Exit gate: every D-100..D-110 is ADOPTED or explicitly deferred with a documented fallback. bash scripts/ledger-scan.sh should show none of them under "PROPOSED / OPEN decisions" (or only the explicitly-deferred ones).
State: NOT STARTED.
| Goal | Turn the bare vcloud host into a substrate Office1 can deploy from. |
| Build | Nested KVM enabled; libvirt storage pools + thin-provisioning; dark-fiber + per-site ISP-edge virtual networks; the six per-DC plane segments as virtual networks; MTU plan set on the underlay. |
| Gate | Nested KVM verified; disk budget computed + Ceph size decision recorded; measured MTU recorded; virtual networks present and isolated as designed. |
| Owns | D-100 (fabric), D-101/D-102 (MTU), buildout-design Section 3 (host/disk/MTU gates). |
| Reuse vs new | NEW. No VR0-DC0 analog -- that testcloud IS the single vcloud host, already prepared; this is preparing the host to carry TWO independent DC substrates plus Office1. |
| Authoring status | Runbook not started. opentofu/ SCAFFOLD started 2026-07-09: modules/dc-planes (six per-DC planes) + modules/mesh-link (dark-fiber legs) + modules/dc-storage-pool wired for DC1 + Office1 -- see opentofu/README.md for what's built vs deliberately deferred (node-VM/domain resources, DC2 CIDRs, netem). UNVALIDATED: no tofu binary available to run scripts/opentofu-validate.sh yet. |
State: NOT STARTED.
| Goal | Stand up the operator's deployment headend first, before any DC. |
| Build | MAAS region controller; OpenTofu; NetBox (vcloud-local, importer extended for multi-DC + dual-stack); GitBucket (vcloud-local mirror); Tailscale front door (Office1 only). |
| Gate | MAAS region reachable; NetBox authoritative + populated (planes, per-DC v4, ULA/GUA carve); GitBucket serving; OpenTofu reaches vcloud host libvirt; Tailscale confirmed to Office1 only. |
| Owns | D-103 (Office1 VMs are OpenTofu-created), D-107 (headend is not a core-service provider). |
| Reuse vs new | NEW infra (Office1 didn't exist for VR0 DC0), but the practices transfer directly: NetBox-as-IPAM-apex discipline, the netbox/*-import.py pattern, and this repo's own git/GitBucket hosting model are all precedent, just extended. |
| Authoring status | No runbook yet. |
State: NOT STARTED.
| Goal | From Office1, script each DC's "physical" layer into existence. |
| Build | Per DC: OpenTofu creates node-VM libvirt domains (SHIM, Section 9), the DC's plane networks, and its OPNsense edge; registers the DC libvirt host to that DC's MAAS rack controller as a virsh VM-host. |
| Gate | MAAS rack controller per DC up; MAAS discovers the OpenTofu-created node VMs; edge routing + simulated ISP uplink up; netem parameters applied and measured. |
| Owns | D-103 (seam), D-100 (edges). |
| Reuse vs new | NEW (no OpenTofu/multi-rack precedent in VR0 DC0). Explicitly tagged shim: node-VM creation has no Roosevelt analog (physical racking + BMC enlistment replaces it there) -- do not treat as reusable production IaC. |
| Authoring status | Runbook not started. The plane/link/pool layer of opentofu/ (see Stage 1) is reusable here too -- what's still missing for THIS stage specifically is the node-VM (libvirt_domain) module, the virsh-VM-host-to-MAAS-rack registration, and the OPNsense edge itself; all deferred, see opentofu/README.md. |
State: NOT STARTED.
| Goal | Bring the discovered node VMs to a deployed OS, ready for Juju. |
| Build | Per DC: MAAS commissions + deploys the node VMs; provider NIC left RAW (br-ex discipline, D-100); VLAN-103 metal-internal bridge stack pre-built by MAAS. |
| Gate | Nodes deployed; six planes present per node with correct fabrics/VLANs; provider NIC raw; PXE (v4) working; per-DC mirror reachable; NTP from edge working. |
| Owns | D-103, D-100 (br-ex), D-107 (mirror + NTP). |
| Reuse vs new | PARTIAL reuse. runbooks/phase-00-teardown-maas-reset.md carries the MAAS commission/deploy + br-ex discipline this repo already validated for a single rack; the per-DC multi-rack registration is new, the deploy mechanics are precedent. |
| Authoring status | No dc-dc runbook yet; adapt phase-00's MAAS section per-DC. |
State: NOT STARTED.
| Goal | A running independent OpenStack cloud per DC. |
| Build | Per DC: bootstrap the single-unit Juju controller in-DC; deploy the Caracal bundle; Vault init with a per-DC independent root, issuing v4 + IPv6-SAN certs; apply the family matrix (ULA on data/storage/replication, dual-stack metal-internal API, GUA provider). |
| Gate | preflight.sh PASS before add-model; post-deploy cloud-assert.sh --capture per DC; controller backup taken + stored; Ceph-over-v6 and geneve-over-v6 verified. |
| Owns | D-104 (controllers), D-109 (Vault), D-101 (families). |
| Reuse vs new | This is the ground-truth/copy point (docs/dc-dc-buildout-design.md line 12: seeded from openstack-caracal-ipv4 at 8813efc). bundle.yaml and runbooks/phase-01..08-*.md are the per-DC template -- run essentially the existing VR0 DC0 deploy loop TWICE (once per DC), adapted for: independent Keystones (no cross-DC federation), the IPv6 family matrix (D-101/D-102), and per-DC Vault roots (D-109) instead of the single shared root. |
| Authoring status | Existing phase-01..08 runbooks are the starting point; need a per-DC parameterization pass (D-101 family matrix, D-109 Vault) before reuse, not a from-scratch write. |
State: NOT STARTED. This is where the existing repo tooling (preflight.sh, cloud-assert.sh, scripts/lib-net.sh/lib-hosts.sh, run-logged.sh) does the most direct work -- confirmed repo-agnostic as of the 2026-07-09 sweep, so they apply to either DC's clone/session without modification.
| Goal | Prove Tier-A recoverability, one-way first, then two-way. |
| Build | Replication-plane peering (ULA); radosgw multisite zonegroup (two DCs as zones); rbd-mirror for the Glance pool; cinder-backup to the replicated object store; consistency groups where needed. Run the failover/failback drill. |
| Gate | One-way drill clean (RTO/RPO measured); then two-way; controller restore drill (D-104) exercised; RTO recorded as a measured output, not an SLA. |
| Owns | D-108 (replication), D-104 (control-plane backup/restore in the drill). |
| Reuse vs new | Ground truth: docs/dc-dc-replication-DR-seed.md (the original Tier-A/bidirectional/cinder-backup+rbd-mirror decision) -- D-108 corrected its carrier from the seed's v4 replication plane to IPv6-only ULA. The failover/failback drill skeleton is already drafted (buildout design Section 8): confirm DC1 down -> promote Glance -> restore Cinder -> rebuild instances -> record RTO/RPO; failback is split-brain-safe (no auto-resume, controlled window to flip primary back). |
| Authoring status | Skeleton exists; needs a runbook with command-level steps + harness. |
State: NOT STARTED.
| Goal | Complete the service set in both DCs. |
| Build | Per DC: Designate (designate-bind) with per-DC zones + A/AAAA per the D-008 bootstrap order; COS observability; Magnum/CAPI with in-cloud single-homed capi-mgmt (domain-setup re-run per D-046). |
| Gate | Designate resolving A + AAAA; FQDN-SAN certs valid (D-019/D-021 root cause closed); COS scraping; a CAPI workload cluster comes up per DC. |
| Owns | D-106 (Designate), D-105 (CAPI/COS). |
| Reuse vs new | HIGH REUSE for CAPI/Magnum: runbooks/phase-06-incloud-mgmt-cluster.md + phase-07-conductor-graft.md + phase-08-workload-cluster-acceptance.md are direct precedent, per-DC. Designate is a REVERSAL, not new build: VR0 DC0 deferred it (D-019, "DNS scope reduction"); D-106 reactivates it (supersedes D-019, reactivates the original D-008 bootstrap order) -- read D-008 and D-019 together before writing this stage's runbook, the static-/etc/hosts workaround they document goes away. |
| Authoring status | No dc-dc runbook yet; adapt phase-06/07/08 per-DC, write Designate reactivation fresh. |
State: NOT STARTED.
git pull -> bash scripts/repo-lint.sh (0 fail / 1 documented legacy WARN expected) -> bash scripts/ledger-scan.sh to reconcile open decisions/next-free numbers against docs/session-ledger.md.bash scripts/run-logged.sh <label> first (as-executed log).bash scripts/preflight.sh PASS.bash scripts/cloud-assert.sh (add --capture at real deploy-completion milestones for the committed BOM).docs/design-decisions.md for the governing D-NNN. PROPOSED/OPEN means the operator has not ruled -- present options, do not implement.repo-lint clean, the touched script's harness green (write one if missing), a changelog entry with a per-item revert, and a docs/session-ledger.md update.bash scripts/ledger-scan.sh for next-free (it is the numbering authority, not repo-lint's informational count).Confirmed by grepping the actual repo (scripts/, tests/, netbox/, runbooks/), not inferred from the design doc alone. Ordered roughly by "you'll hit this first," not by size -- #1 is cheap and unblocks a reuse claim made elsewhere in this doc;
#2 is the single largest build.
$DC selector convention (BLOCKS Stage 5's reuse claim). scripts/lib-net.sh and lib-hosts.sh are hardcoded to ONE deployment's literals (one set of six plane CIDRs, one KEYSTONE_VIP_DEFAULT, a fixed 4-host list) -- confirmed by reading both files directly, no DC parameter exists anywhere in them. Running the "reused" phase-01..08 runbooks against DC2 today would silently reuse DC1's values. Needs the same treatment as the 2026-07-09 $REPO convention: a $DC (or --dc) selector, with both libraries becoming DC-keyed lookups instead of flat constants. Do this BEFORE writing the Stage 5 runbook, or the "high reuse" claim above is not actually true operationally.opentofu/ now has the network + storage-pool layer: modules/dc-planes (six per-DC planes), modules/mesh-link (the D-100 dark-fiber triangle legs), modules/dc-storage-pool, wired for DC1 + Office1 (DC2 deliberately not wired -- its CIDRs aren't assigned yet, D-101). Provider schema (confirmed against the actual current docs, not memory) turned out to differ materially from older common examples of this provider. Still missing, deliberately deferred (see opentofu/README.md for why): the libvirt_domain node-VM module (schema too large/unfamiliar to author safely without a real tofu providers schema pass), libvirt_volume, OPNsense edge config, and tc netem application. UNVALIDATED -- no tofu binary was available to run scripts/opentofu-validate.sh (the harness written alongside it) this session; run it on a connected machine before trusting any of this.netbox/ has exactly two scripts (ipv4-prefixes-import.py, ipv6-mark-reserved.py) -- single-DC v4 import and v6-reservation-marking only. D-101 requires DC2's supernet, the org ULA /48, and per-DC GUA carves to be NetBox-authoritative via "the extended import pipeline" -- that pipeline is not written. Blocks Stage 0's exit condition (literals must be real, not placeholders) and Stage 2.opentofu/modules/mesh-link creates the inter-site L2 segments (DC1<->DC2, DC1<->Office1, DC2<->Office1) an OPNsense VM's interfaces would eventually attach to.libvirt_domain module (see gap #2). OPNsense is a VM; it inherits that same schema-verification prerequisite. No image source (official OPNsense qcow2/ISO vs a custom-built image) has been chosen either.config.xml), not something the libvirt provider touches at all -- options are a pre-baked config baked into the image, a cloud-init-driven first-boot script, or OPNsense's own API/CLI driven post-boot by a provisioner. None evaluated yet.tc netem WAN-simulation -- a wholly separate mechanism, not an OPNsense setting. No Terraform/OpenTofu-native equivalent exists at all (confirmed against the libvirt_network schema, gap #2); it is a Linux tc qdisc setting applied to the host-side bridge interface after the network exists, needing either a provisioner or a small companion script -- neither written. The exact parameters (latency/jitter/loss/ rate) are themselves still an unruled D-100 open sub-item (gap #11), so this needs a decision before it can be scripted, not just scripting.osd-blank-check.sh is unrelated). D-108's entire mechanism -- zonegroup setup, rbd-mirror daemon peering, the failover/failback drill commands -- is net-new. Blocks Stage 6.runbooks/phase-01-bundle-deploy.md states the bundle explicitly ships "NO designate (D-019)" -- reactivating it per D-106 means editing bundle.yaml itself (a built-surface change, gated behind D-106 leaving PROPOSED), in addition to writing the reactivation runbook. Blocks Stage 7.phase-01..08 / phase-06/07/08 (Stage 5's pass is gated on #1 above).tests/<name>/run-tests.sh harness -- applies to everything in #2-7 above. None exist yet because none of those scripts exist yet; expect roughly 5-6 new test suites alongside whatever gets built.| Layer | Status |
|---|---|
Design (dc-dc-buildout-design.md) |
DONE, PROPOSED |
| Decisions (D-100..D-110) | DRAFTED, ALL PROPOSED -- Stage 0 is the very next action |
| DR mechanism seed | DONE (dc-dc-replication-DR-seed.md), superseded-carrier corrected into D-108 |
| Stage 0-7 runbooks | NOT YET WRITTEN -- see Tooling gap register above for the itemized breakdown |
opentofu/ (networks/pools) |
SCAFFOLDED 2026-07-09, UNVALIDATED (no tofu binary available this session) -- see gap #2 and opentofu/README.md |
opentofu/ (node VMs, OPNsense, netem) |
NOT STARTED -- deliberately deferred, see opentofu/README.md |
| Reusable as-is, repo-agnostic (2026-07-09 sweep) | bundle.yaml, phase-01..08 VR0 DC0 runbooks (Stage 5/7 template, once DC-parameterized -- gap #1), preflight.sh, cloud-assert.sh, repo-lint.sh, run-logged.sh, ledger-scan.sh |
lib-net.sh / lib-hosts.sh |
NOT yet DC-agnostic -- single-deployment hardcoded literals; see gap #1 |
Environment/skill docs (environment.md, README.md) |
Still describe VR0 DC0/v1 single-DC scope wholesale -- flagged, not yet rewritten for VR1; not a blocker for Stage 0 but will need doing before/during Stage 1 |