| 2026-07-13 |

D-116 ADOPTED: no Office1-local GitBucket -- keep using git.baldurkeep.com
...
Operator ruling: "we've decided not to build a separate one on the test cloud. We will continue to
use the current setup and create new repos for deployment tasks as needed."
SUPERSEDES the GitBucket half of D-103 (which gave Office1 three service VMs) and removes GitBucket
from Stage 2's build list entirely. Office1's MAAS-composed service machines are now exactly TWO --
office1-netbox and office1-tailscale -- and both are LIVE.
WHAT THIS ACTUALLY CLOSES, and why it is a simplification rather than a deferral: the Stage 2 runbook
carried an OPEN QUESTION about the Office1-local GitBucket -- "what it mirrors, and under what repo
path, is not decided" -- because a SECOND GitBucket alongside the pre-existing git.baldurkeep.com
raised a real authority/mirroring question nobody had answered. That question is now MOOT. There is
one git service, it is the existing one, and it is authoritative.
Recorded explicitly in D-116 so nobody misreads the scope: D-107's per-DC ARTIFACT mirror (OS/package
artifacts for node provisioning) is a SEPARATE concern and is NOT affected. And if Roosevelt later
wants a site-local git service, that is a fresh decision against real requirements -- not something
this ruling forecloses.
Applied everywhere it was an instruction, not just recorded:
- runbooks/dc-dc-phase1-office1-standup.md: Step 11 replaced with a TOMBSTONE (kept rather than
deleted -- step numbering is referenced elsewhere, and someone WILL come looking after reading
D-103); removed from the sequence, the compose steps, and the open-questions list.
- docs/dc-dc-deployment-workflow.md: dropped from Stage 2's Build and, importantly, from its GATE --
"GitBucket serving" is no longer a gate condition.
- docs/design-decisions.md: D-103 annotated in place with the supersession, so grepping for GitBucket
lands on the amendment rather than the stale instruction.
- docs/vr1-office1-as-built.md + session ledger updated.
repo-lint 0 fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|

D-115 ADOPTED: v4 stays role-based; Office = /22 per office; DC2 moves INSIDE the Cloud /16
...
Operator ruled all three questions:
1. v4 stays ROLE-BASED, fill the missing roles (v6 keeps its region hierarchy; v4 is pooled by
function -- v6 is abundant, v4 is scarce). No DC renumber; D-101's inherit-DC0-unchanged
economy is preserved.
2. /22 per office. VR1 Office1 = 10.10.0.0/22 -- which BLESSES the deployed LAN (10.10.0.0/24) and
LXD compose net (10.10.1.0/24) exactly. ZERO deployment renumber.
3. Register AND deploy dual-stack on Office1 now.
THE CARVE. New v4 roles: Office (10.10.0.0/16, /22 per office) and Edge (172.30.0.0/16, mirroring
v6's Edge Networks fe::/48) so the simulated ISP uplinks stop living inside the Corp OOB block --
office1-wan's 172.30.1.0/24 becomes legitimate. DC2 moves from 10.13.0.0/19 to 10.12.64.0/19,
INSIDE the Cloud /16: free (DC1 holds 10.12.{4,8,12,16,32,36}), still routable to DC1 over the
dark fiber. docs/dc-dc-netbox-buildout-scope.md is corrected -- its 10.13.0.0/19 recommendation sat
outside every allocated block and would have had DC2 squatting too.
v6 is determined by the existing pattern, no design freedom: Office1 = 2602:f3e2:f01 :/56 with
a /64 office subnet, plus a "VR1 Off1" site. (VR0 Off0 = e01 :/56 -> /64; Eugene's Charnelton =
101 :/56. VR1's office /48 already existed and we were simply not using it.)
MEASURED CONSTRAINT, attached before anyone builds on a false premise: IPv6 DOES NOT EGRESS THE LAB.
vcloud has global v6 and a v6 default route, and the v6 gateway IS reachable (RA and L2 work) -- but
v6 INTERNET is unreachable (2606:4700:4700::1111 and 2001:4860:4860::8888 both 100% loss; v4 from the
same host is fine). So deploying dual-stack on Office1 proves v6 ADDRESSING/RA/services-binding-v6 --
most of D-101's family matrix, worth doing -- but it CANNOT prove v6 internet reachability. Public
GUA reachability (v6 public API endpoints, tenant GUA egress) is UNPROVABLE IN VR1 until the lab's
upstream carries v6. That is outside this cloud and this repo. Do not schedule a VR1 test that
depends on it, and do not read a green internal-v6 result as "v6 works end to end".
The NetBox WRITE is deliberately NOT done unilaterally: netbox.baldurkeep.com is production, the
standing architecture is read=source / write=feedback-only, and the Office1 sandbox NetBox (composed,
Ready, not yet deployed) is where this carve gets simulated first.
repo-lint 0 fail -- and its L5 collision guard did its job, catching that my amendment header read
as a second DEFINITION of D-115. Renamed to the AMENDMENT form the guard exempts rather than
weakening the rule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|

D-115 PROPOSED: Office1 addressing -- the office carve, and the four v4 role gaps it exposed
...
Operator challenge during deployment testing ("I thought there were corp office subnet carves in
the planning") -- and they were right. I had checked for COLLISIONS but never asked the IPAM apex
whether Office1 had an ASSIGNED carve. It does, in v6, and we were not using it.
MEASURED against the live NetBox (read-only), not the repo -- the repo's planning docs cover only
the DC planes, so grepping them would never have found this:
IPv6 is a complete region-hierarchical model. Every region gets a /40; sites are /48s on a FIXED
slot pattern: X00=infrastructure, X01=OFFICES, X02/X03=datacenters. VR1's office /48 ALREADY EXISTS
(2602:f3e2:f01::/48). Offices take one /56 each, then a /64 subnet -- VR0 Off0 = e01 :/56 ->
e01 :/64; Eugene's Charnelton = 101 :/56. So Office1's v6 has NO design freedom: it is
2602:f3e2:f01 :/56 + a /64. Office1 currently has NO IPv6 AT ALL -- D-101's dual-stack family
matrix silently skipped the office.
FOUR v4 GAPS, none previously known:
1. There is NO Office role in v4. Office1's LAN (10.10.0.0/24) and LXD compose net (10.10.1.0/24)
are squat -- colliding with nothing, blessed by nothing.
2. DC2's planned 10.13.0.0/19 is OUTSIDE the Cloud /16 (10.12.0.0/16). The scope doc recommends it;
NetBox never allocated it. DC2 was already set to squat and nobody had noticed.
3. office1-wan (172.30.1.0/24) squats INSIDE 172.16.0.0/12 "Corp OOB Private" -- and it is not OOB,
it is a simulated ISP uplink. A wrong-ROLE problem, not just an unregistered one.
4. "Edge Networks" has a v6 /48 (2602:f3e2:fe::/48) but NO v4 counterpart, so the ISP-sim WAN
segments have nowhere legitimate to live in v4.
THE TENSION TO RULE ON: v6 here is region-hierarchical, v4 is role-based. That asymmetry is
defensible -- v6 is abundant so it gets geography, v4 is scarce so it gets pooled by function.
Mirroring v6's region tree in v4 would force RENUMBERING DC1 off 10.12.x, breaking exactly the
inherit-DC0-unchanged economy D-101 bought to reuse VR0's bundle. Large cost for tidiness.
Recommended option (a): keep v4 role-based and fill the MISSING roles -- an Office /16 carved /22
per office (VR1 Office1 = 10.10.0.0/22, which BLESSES the deployed LAN + compose net exactly, zero
renumber), an Edge role (172.30.0.0/16, mirroring v6's fe::/48) so the ISP-sim WANs stop living in
the OOB block, and DC2 pulled INSIDE the Cloud /16 at 10.12.64.0/19 (free; DC1<->DC2 stay routable).
Option (b) mirrors v6 and renumbers the DCs. Option (c) ratifies the squat verbatim -- rejected.
PROPOSED, not adopted: presenting options, not picking. Nothing is blocked -- Office1 is live and
routing. But the renumber cost is LOWEST RIGHT NOW, before NetBox and GitBucket deploy onto it.
repo-lint 0 fail. No live system touched (NetBox reads were read-only).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|

D-114 ADOPTED: VR1 site containment VMs + MAAS-composed LXD VMs for the non-stack machines
...
Operator ruling. AMENDS D-103 (which gave Office1 three sibling service VMs on vcloud).
WHAT THE OPERATOR'S SCREENSHOTS PROVED -- and where I was wrong. I had grepped bundle.yaml,
found only Juju's `lxd:N` CONTAINER placements, and concluded "there is no VR0 precedent for
MAAS composing LXD VMs -- zero." FLATLY WRONG. VR0's MAAS (3.7.2) has an `lxd` machine
registered back into MAAS as an LXD KVM host (LXD 5.21.4), and `tailscale` is a VM COMPOSED
BY MAAS INSIDE IT. The bundle's Juju LXD containers and MAAS's LXD VM host are TWO DIFFERENT
LXD usages in the same cloud; reading only the bundle collapses them into one. Recorded in the
decision because the error is instructive.
The ruling:
1. Non-stack machines (NetBox, GitBucket, Tailscale) become MAAS-COMPOSED LXD VMs -- MAAS-visible,
enlisted/commissioned/deployed/powered. This is VR0's proven `lxd` + `tailscale` pattern per
site, at ~2 cores / 2 GiB each. The Juju-deployed OpenStack services stay in Juju-created LXD
CONTAINERS, invisible to MAAS, exactly as in VR0 -- per the operator, that visibility is not wanted.
2. Each site gets a containment VM (voffice1/vdc1/vdc2). The decisive rationale is the operator's:
full-facility snapshots, and simulating total geographical failure of a facility. D-108's DR
drill currently has NO honest "hard-down a DC" primitive -- `virsh destroy vdc1` IS that primitive.
The flat model cannot offer this at any price.
3. Office1 FIRST, fully up; DC1 GATED behind it.
4. LXD pinned to the 5.21 LTS track: MAAS 3.6/3.7 is INCOMPATIBLE with LXD >= 6.7 (endpoint
consolidation vs pinned pylxd 2.3.5). VR0 is on 5.21.4 -- safe by timing, now safe by decision.
KNOWN RISK: vcloud is itself a KVM guest (measured), so a containment VM puts servers one level
deeper than VR0 runs them. Office1 IS the nesting probe -- LXD VMs are qemu/KVM guests, so it
needs nested KVM at the same L3 depth the DCs would (an earlier draft of the entry said Office1
needed none; corrected in-entry). Office1 is simply the cheap place to prove it: no OpenStack,
no Ceph. DC1's gate additionally proves L4, the depth VR0 has never exercised.
Bonus: voffice1 booting on office1-local takes the FIRST REAL DHCP LEASE from Kea -- a path
proven only at the daemon level, never end to end.
repo-lint 0 fail. No live system touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|

D-113(a2) COMPLETE: DELETE the config.xml path (template, renderer, ISO builder)
...
Operator ruling ("I'll defer to your lean"): the OPNsense config.xml template is DELETED, not
reduced.
WHY DELETED RATHER THAN REDUCED: the (a2) plan was to shrink the template to a minimal bootstrap
(sshd + root key + console + a seeded API key). That proved UNNECESSARY -- every one of those is
covered without a config.xml at all:
sshd + root key -> the D-112(c) console bootstrap (proven on Office1)
an API key -> scripts/opnsense-bootstrap-apikey.sh (OPNsense's OWN model; no GUI
click, no re-implemented crypto)
DHCP/firewall/interfaces -> the REST API (proven read AND write)
So the provisioning chain has NO config.xml anywhere:
boot factory nano -> console bootstrap -> mint API key -> configure over REST
A config.xml renderer that nobody should ever run is not a safety net -- it is a loaded gun
pointed at a live router. The 2026-07-13 safety sweep is the evidence: the repo still contained
runbook steps telling an operator to render a config and push it to the edge, which by then would
have CLOBBERED live API-managed DHCP.
DELETED: opentofu/templates/opnsense-config.xml.tmpl; scripts/opnsense-render-config.sh +
tests/opnsense-render-config/; scripts/opnsense-build-config-iso.sh +
tests/opnsense-build-config-iso/; the opnsense-edge module's config_seed volume + cdrom disk and
its config_iso_path variable; the xorriso/genisoimage prereq (it existed ONLY for the ISO
builder). Gauntlet 54 -> 52 harnesses (the two deleted scripts took their harnesses with them --
expected arithmetic, not a regression). All files remain in git history.
opentofu/templates/README.md is now a TOMBSTONE explaining what happened and what to use instead.
THE LIVE CHANGE IS NOT APPLIED IN THIS COMMIT. Removing the module's ISO wiring touches an
INSTANTIATED resource (libvirt_volume.config_seed is in tfstate). Measured with `tofu plan` first:
module.office1_opnsense.libvirt_domain.vm will be updated IN-PLACE
module.office1_opnsense.libvirt_volume.config_seed will be destroyed
Plan: 0 to add, 1 to change, 1 to destroy.
NO replacement of the running domain -- confirmed explicitly. A replacement would have DESTROYED
the live, routing, DHCP-serving edge, and would have been grounds to stop.
A LINT GUARD DID ITS JOB: repo-lint's L3 (runbooks must not reference missing scripts) went RED on
the tombstone notes, because they named the deleted paths. The rule was RIGHT and it has no
opt-out (only L4 does). Rather than weaken a guard that exists to stop runbooks pointing at dead
scripts, the tombstones were reworded to name the bare filename instead of the scripts/ path. The
guard stays fully intact; the information is preserved.
Verification: repo-lint 0 fail; opentofu-validate PASS (root + 10/10 modules standalone);
gauntlet ALL GREEN (52).
Revert: git revert this commit (restores template/renderer/ISO builder/harnesses), then
`cd opentofu && tofu apply` to re-create the (inert) config_seed volume + cdrom.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6Fzre1CxCY8tzwFCudu57
|

D-113 ADOPTED: option (a2) -- stay on OPNsense, move config to the REST API
...
Operator ruling 2026-07-13. Two constraints surfaced during the discussion narrowed the field
decisively, and both are recorded so the reasoning is not re-run from the wrong premise:
1. GUI ACCESS IS A REQUIREMENT (operator). This eliminates VyOS and plain-Linux outright --
neither has a GUI -- and both had been the leading candidates until it was stated.
2. VyOS LTS binaries are subscription-gated (verified against vyos.net/get and the Software
Access Subscription page). Free VyOS means the ROLLING release, a moving target that fights
this repo's appendix-B version-pinning discipline. Real friction, independent of the GUI.
Field reduced to OPNsense / pfSense CE / OpenWrt / IPFire. pfSense rejected (same GUI-owned XML
lineage -- a migration that buys nothing). OpenWrt was the credible alternative: its UCI config
is text-first BY DESIGN, structurally unlike OPNsense's GUI-owned config.xml. It loses on cost:
a new image pipeline, a fresh bootstrap problem, and dnsmasq instead of Kea -- all to escape a
problem that is fixable in place.
The ruling rests on this: the bug class was never OPNsense. It was hand-authoring config.xml
when OPNsense ships a documented REST API with mature Ansible collections that model exactly
what we got wrong (dhcp_subnet, firewall rules, system settings as typed resources). NONE of
DOCFIX-191/192/193 is expressible through the API -- you cannot forget to enable sshd in a
format where sshd is a typed field with a default. (a2) keeps the GUI, keeps a working
routing+DHCP edge, keeps the libvirt fixes, and removes the defect source.
(a1) config.xml templating is now explicitly REJECTED: its cost compounds per feature and it
depends on an undocumented self-heal of an internal format (the 667-element finding).
Live-edge recon (read-only): lighttpd listening on 443/80, API answers 401 (alive), root has
0 API keys. Minting a key via the GUI is the bootstrap step -- hand-editing apikeys into
config.xml would be the exact anti-pattern being retired.
Revert: git revert this commit (docs only; nothing migrated, nothing built).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6Fzre1CxCY8tzwFCudu57
|

D-113 (PROPOSED): is OPNsense the right edge platform? -- and a confound in D-112
...
Operator asked, after a five-bug day, whether something is better suited than OPNsense for
the simulated edges. Written up with alternatives; NOT RULED.
Honest cost accounting: of the five bugs, TWO (DOCFIX-188 memory_unit, DOCFIX-190 ACPI) were
libvirt module bugs that would have hit any guest and whose fixes now protect every VR1 VM --
not OPNsense's fault. The other three (DOCFIX-191 no sshd/key, DOCFIX-192 no console,
DOCFIX-193 no DHCP) share ONE root cause: hand-authoring the appliance's internal, GUI-owned
config.xml. 2026-07-13 adds a fourth of the same kind -- a full-config push drops ~667
migration-populated elements including the only two firewall pass rules, and works ONLY
because OPNsense regenerates them on boot. We depend on an undocumented self-heal of an
internal format. That, not the boot bugs, is the ongoing cost.
THE CONFOUND, and the reason this is worth ruling rather than waving through: D-112 adopted
option (c) with the explicit rationale "C since it is the way people automate opnsense" --
i.e. the REST API (opn-cli, the Ansible collection). What we actually BUILT is config.xml
templating delivered over scp. That satisfies (c)'s letter (provision over the network, post
boot) but NOT the rationale it was ruled on. Every OPNsense-specific bug we are charging
against the platform is an artifact of hand-authoring XML -- precisely what the API exists to
avoid. So the fair comparison is not OPNsense vs VyOS; it is:
(a1) OPNsense + config.xml templating -- status quo; cost COMPOUNDS per feature
(a2) OPNsense + REST API -- what D-112's rationale actually called for
(b) VyOS -- blank-sheet best; discards a working edge
(c) plain Linux (nftables/Kea/FRR + cloud-init) -- max fidelity to our own tooling
(d) pfSense -- REJECT, same config-format lineage
Recommendation (operator rules): stay on OPNsense, and the real choice is a1 vs a2 -- I lean
a2. The hard part is done and measured working, and edges 2/3 reuse the module, so the cost
paid was one-time. But (a1) compounds, and (a2) is the honest reading of D-112's own ruling.
Rule BEFORE Stage 3: it builds two more edges from this module, so ruling later means
migrating three edges instead of one.
Revert: git revert this commit (docs only; nothing built or migrated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6Fzre1CxCY8tzwFCudu57
|
| 2026-07-12 |

D-112: record option (a) as MEASURED INFEASIBLE; rule the (c) bootstrap; make the edge console interactive
...
Operator asked to re-examine option (a) (bake /conf/config.xml into the nano) before
committing to (c). Done, and it is BLOCKED on this host -- measured, not assumed:
CONFIG_UFS_FS=m but "# CONFIG_UFS_FS_WRITE is not set" -> UFS driver is READ-ONLY
partition table: MBR, a single FreeBSD a5 partition -> UFS only, no FAT to write
fuse-ufs / ufs2-tools -> not present, not packaged
libguestfs (guestfish / virt-copy-in) -> not installed
Two ways (a) could be forced, both rejected and recorded so they are not re-litigated:
a FreeBSD helper VM (works, xorriso is present to remaster a live ISO -- but it means
maintaining a builder VM forever to deliver one 4.5 KB file), and a raw byte-patch of
the image (probably works; silently corrupts if the file is not block-contiguous, and
is NON-TRANSFERABLE -- you cannot byte-patch a real appliance on Roosevelt hardware,
which is exactly what this repo's governing constraint forbids).
Examining (a) properly STRENGTHENED (c): (a) is not merely less mainstream here, it is
unavailable without a detour costing more than (c)'s entire bootstrap.
Bootstrap RULED (operator): B1 = temporary host leg on virbr2 (reversible; no change to
the D-100 network definition). B2 = one interactive console login to enable SSH and
install the ed25519 pubkey already generated in ~/vr1-office1-creds/, after which
everything is plain SSH/SCP.
Module change enabling B2: the edge's serial becomes a unix socket + a log file, rather
than a write-only file source. Both roles are needed and they are different:
source.unix -> INTERACTIVE bidirectional console. A `file` source is write-only capture
and cannot be typed into, so it cannot bootstrap anything. A unix socket
is scriptable with `socat - UNIX-CONNECT:`, unlike a pty, which needs a
controlling TTY.
log -> preserves full boot capture from power-on (a socket loses anything
emitted before a client connects). That capture is what made both
2026-07-12 boot bugs legible; it is not being dropped.
S1+S2 guards PASS, harness 6/6, repo-lint 0 fail, tofu fmt/validate clean.
Live recreate + host leg remain separately gated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y117t1F525ba9r1vXSBETk
|

D-112 ADOPTED: option (c) post-boot network provisioning (operator ruling)
...
Operator ruled (c): "C since it is the way people automate opnsense" -- prefer the
mainstream automation path (the OPNsense REST API, as used by opn-cli and the
Ansible OPNsense collection) over image-baking or a media-import trick. The
rendered config (opnsense-config.xml.tmpl) is REUSED unchanged; only the DELIVERY
mechanism changes.
IMPLEMENTATION NOT STARTED -- deliberately. "Push the config over the API" is not
actually reachable from a factory-default OPNsense, and the bootstrap sub-choices
are unruled. Recorded in D-112 so the next session does not discover this the hard
way:
1. NO ROUTE. Factory-default LAN is 192.168.1.1/24 on office1-local, an ISOLATED
libvirt network with no host leg on virbr2 (measured). The WAN side is reachable
at 172.30.1.126 but OPNsense blocks all inbound on WAN by default, so it is not
a way in.
2. NO CREDENTIALED API. The REST API authenticates with an API key+secret (not the
root password), minted via the GUI. SSH is disabled in the factory default.
3. OUR SERIAL IS WRITE-ONLY. The module attaches <serial type='file'> -- a one-way
capture that cannot be typed into. An interactive console means type='pty' +
virsh console (module change + recreate).
Sub-choices B1 (how to reach it) and B2 (how to authenticate first) are left
UNRULED in D-112 rather than picked.
SELF-CORRECTION recorded: I had earlier described option (b) as making the ISO
mechanism "work as designed, unchanged". That was over-claimed -- even with a
read-only root the importer prompts with a 7s timeout and asks for a device name,
so (b) may have been (d) in disguise. Logged so nobody revives (b) on my bad
summary. Note a console LOGIN SHELL is not the rejected (d): (d) was rejected for
racing the importer's 7s timeout; a login shell has no timing race.
The config-ISO path (opnsense-build-config-iso.sh + harness, the module's
config_seed volume + cdrom disk) is RETIRED but left in place -- it is inert, not
harmful. Remove it in the same change that lands (c), not before.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y117t1F525ba9r1vXSBETk
|

D-112 (PROPOSED): OPNsense Config Importer cannot fire on a nano image -- by design
...
Bug 3 of the Office1 edge boot. With memory (DOCFIX-188) and ACPI (DOCFIX-190)
fixed, OPNsense boots -- but on FACTORY DEFAULTS. The config ISO was never read.
Root cause is architectural, not a bug. rc.syshook.d/import/20-importer runs
`opnsense-importer -b`, whose first act is:
INSTALL="/.probe.for.readonly"
touch ${INSTALL} 2> /dev/null
if [ -f ${INSTALL} -a -f /conf/config.xml ]; then
bootstrap_and_exit 0
fi
That touch is a PROBE FOR A READ-ONLY ROOT. On installer media root is read-only,
the touch fails, the marker is absent, and the importer scans attached media
(cd9660 included). On a pre-installed nano the root is WRITABLE and a factory
/conf/config.xml already exists -- so both conditions hold and it exits
immediately, never enumerating a single device.
Our ISO was correct all along: verified ISO9660 'OPNSENSE_CFG' containing
CONF/CONFIG.XML with our 10.10.0.1, and the guest sees it as cd0 at exactly the
right byte size. Nothing was ever going to read it.
The README's research correctly established that the Importer's scan path supports
ISO9660 -- but never that the Importer RUNS on a nano image. It does not. The
module header has always flagged this mechanism UNVERIFIED; that flag is now
discharged NEGATIVE.
Recorded as D-112 PROPOSED with four options (bake config into the image / switch
to the installer image / post-boot API push / serial expect-scripting). NOT
implemented -- this replaces the config-delivery mechanism of a built surface and
also gates Stage 3, so it is the operator's ruling to make.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y117t1F525ba9r1vXSBETk
|
| 2026-07-11 |

D-111: align VR1 v6 subcarve to deployed NN mnemonic; fix ULA-gen doc bug
...
Basing VR1 NetBox on the real netbox.baldurkeep.com modeling (operator
directive). A read-only live export confirmed VR0-DC0/Willamette use an NN
net-byte mnemonic with /60-per-plane + /64-active; dc-dc-prefixes-import.py
did not match it (arbitrary contiguous indices, /64-only).
DOCFIX-182 / D-111: carve_v6 replaces carve_ula/carve_gua -- provider :10
(+API-VIP :11) GUA, metal /60 shared by admin :20 / internal :21, data :30,
storage :40, repl :50, all /60+/64 ULA; the ULA /56 is indexed to the GUA site
nibble so the 4th hextet reads DC.NN in both families. Direct-math _sub_at (no
subnet enumeration -- preserves the DOCFIX-181 no-hang property). Harness
40 -> 53 PASS. D-111 ADOPTED in design-decisions.md; scope-doc sub-decision #1
ratified.
DOCFIX-183: fix the invalid-IPv6 ULA-generation sed in the netem/ULA proposal
doc (4+6 -> 2+4+4 hextets, caught by actually running it); ratified VR1 ULA is
fd50:840e:74e2::/48.
repo-lint 0 fail; tests/dc-dc-prefixes-import 53/53.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
| 2026-07-10 |

DOCFIX-150: Stage 0 ratification -- D-100..D-110 ADOPTED
...
Operator ruled live on the buildout design's 6 redline items and on each of
D-100 through D-110 individually.
Redline rulings: Ceph size=3 by default (size=2 only as an explicit,
logged Phase-0 fallback); tc netem same-metro lean confirmed; D-102 folds
into D-101, D-109 stays standalone; metal-admin GAINS a ULA leg (reverses
the design's own stated lean -- a real ruling, not a rubber-stamp); COS
per-DC-only confirmed; mirror sync topology independent-per-DC confirmed.
All 11 decisions flipped PROPOSED -> ADOPTED in docs/design-decisions.md.
D-102's body is preserved under a MERGED-INTO-D-101 status line per this
repo's append-only discipline, not deleted. D-101 itself amended: the
metal-admin ULA ruling folded into its family matrix, D-102's tenant/MTU
content folded in as a new section. docs/dc-dc-buildout-design.md Section
10 rewritten from an open list to a ruling record; the stray "Section
D-102" cross-reference corrected to D-101.
Verified, not just asserted: ledger-scan.sh's PROPOSED/OPEN list no longer
contains any of D-100..D-110 -- only D-068/D-071 remain, pre-existing and
unrelated to this gate. Stage 0 of docs/dc-dc-deployment-workflow.md is
CLEARED; workflow doc + visual tracker updated to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QrcJx8TUar7pYAvpGJw57A
|
| 2026-07-09 |
|
| 2026-07-07 |
addendum 37: D-075 ADOPTED+EXECUTED -- dashboard VIP root redirect(/->302 /horizon)+no-autoindex via conf-enabled override (RedirectMatch backend-port leak caught in verify, replaced with per-dir rewrite); DOCFIX-118 phase-03 PER-REBUILD block + appendix-A "Index of /" entry
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
| 2026-07-06 |
addendum 29: D-074 ADOPTED+EXECUTED -- overlap-allowed tenant CIDRs (Phase 0 gates proven live: exact-overlap subnet + Magnum cluster via API-LB), stage-4 guard rewritten with real overlap math (DOCFIX-106), repo_lint L5 numbering print removed (DOCFIX-107), D-016 amended, contract/runbook/intake aligned
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QFdCdEuxynmM8Q6HichbHn
|
addendum 27: d011-batch3 window EXECUTED -- D-073 APPLIED, foil1 onboarded, d011-04/05 PASS, D-011 closed-except-item-6; DOCFIX-095/096/097
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QFdCdEuxynmM8Q6HichbHn
|
addendum 24: D-073 implementation staged -- identity:list_trusts hardening (38->39 rules), zip rebuilt, drift check PASS
...
Live apply remains GATED (attach-resource block in the D-073 amendment;
behavioral verify at the batch-3 window). Gauntlet ALL GREEN, lint 0 fail.
Revert: per addendum-24 REVERT line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
addendum 20: operator ruling sweep -- D-050 closed via D-051, D-073 adopted (list_trusts hardening, impl pending), SEC re-triggers, D-072 upstream bug withdrawn
...
Also fixes a self-inflicted scan poisoning: the 2026-07-05 ledger finding note
quoted high-numbered decoy identifiers literally, inflating ledger-scan's
DOCFIX/BUNDLEFIX next-free; tokens de-fanged + standing rule recorded. D-050
block Status line made scan-closable (D-063 precedent, keywords avoided).
Scan verified post-change: open = D-068, D-071; next-free D-074/DOCFIX-091/
BUNDLEFIX-012; fences 4/0/0; files ASCII.
Revert: git revert this commit (doc-only; no cloud state touched).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
| 2026-07-05 |
D-072 dashboard TLS repair (executed) + DOCFIX-089 fail-closed check + BUNDLEFIX-011
...
Identifiers consumed: D-072, DOCFIX-089, BUNDLEFIX-011.
Since-deploy dashboard VIP TLS defect fixed: cluster binding -> metal-admin
(live bind + durable bundle edit); https 200 CA-verified on both reachable
VIPs, all gates green, post-fix BOM committed. phase-03 3.3 rewritten
fail-closed; appendix-A symptom entry added; Launchpad bug draft ready for
operator submission.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FCGjrRXtbPRu5qEThw3CDz
|
ops-update-20260705 window close: runbook as-executed corrections (DOCFIX-088) + D-071 amendment
...
Identifiers consumed: DOCFIX-088. D-071 AMENDMENT recorded (no new number).
Window complete: controller + 91 agents at 3.6.25, 17 apps refreshed,
all gates green, guests untouched. Runbook fold-back per first execution:
juju-status version sweep, create-backup as standard 2.1 step
(admin/controller), no separate controller-model upgrade, G3 probe on the
D-044 http leg with the VIP-TLS since-deploy defect cross-referenced,
mid-window target-drift expectation row. D-071 risk premise corrected
(supported backup exists; restore rehearsal -> Roosevelt open questions).
Ledger window section closed; post-window queue recorded (addendum 16).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FCGjrRXtbPRu5qEThw3CDz
|

vault bundle revert to 1.8/stable (BUNDLEFIX-010) + D-068 AMENDMENT + G2/G3 close (ops-update-20260705)
...
Identifiers consumed: BUNDLEFIX-010. Also records the operator's D-068
AMENDMENT (no new D number) and window addendum 15.
- bundle.yaml: vault channel 1.16/stable -> 1.8/stable, comment replaced
with amendment rationale (1.16 = incompatible charm line for this
reactive cloud). Gates: provider-bundle-check PASS, channel_assert PASS
(29 pins), repo-lint 0 fail.
- docs/design-decisions.md: D-068 AMENDMENT (2026-07-05) verbatim from
operator; 1.16 ruled out, off-EOL-1.8 remains OPEN; D-002 item-3
supersession withdrawn. Evidence: docs/D-068-vault-1.8-vs-1.16-analysis.md.
- changelog addendum 15: G2 (octavia 542) + G3 (dashboards 750/122/168)
complete; dashboard-VIP-HTTPS RCA = since-deploy build defect (haproxy
L4-masked vhost-less backend), phase-03 3.3 check fails open; G3 stands,
no revert; upstream bug + DOCFIX candidates queued post-window.
- session-ledger: jumphost fence updated; machine-derived reseeded
(BUNDLEFIX next-free 011).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FCGjrRXtbPRu5qEThw3CDz
|
| 2026-07-04 |
ops-update-procedure runbook + D-071 update policy (jumphost stream)
...
Identifiers consumed by this push: D-071, DOCFIX-086. No BUNDLEFIX consumed
(BUNDLEFIX-010 remains next-free; see addendum-11 FINDING 2 on the scan's
wrapped-pointer over-report).
- NEW runbooks/ops-update-procedure.md (DOCFIX-086): routine update window,
gated: controller patch -> model agents -> in-channel charm refreshes.
Vault explicitly excluded (D-068 unruled). Not yet as-executed.
- docs/design-decisions.md: D-071 PROPOSED (update cadence + controller
patch policy, explicit no-backup single-controller risk section).
- runbooks/README.md: index ops-update-procedure + missing ops-restart entry.
- docs/v1-redeploy-changelog.md: addendum 11 (what/why/revert + 2 findings).
- docs/session-ledger.md: numbering re-seeded (D=072, DOCFIX=087,
BUNDLEFIX=010); D-071 contention resolved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FCGjrRXtbPRu5qEThw3CDz
|
|
|
|
|
|
|
| 2026-07-03 |
|
| 2026-07-02 |
|
|
|
|
|
| 2026-07-01 |
|