diff --git a/docs/changelog-20260714-opnsense-set-interface-v6.md b/docs/changelog-20260714-opnsense-set-interface-v6.md new file mode 100644 index 0000000..b632bdb --- /dev/null +++ b/docs/changelog-20260714-opnsense-set-interface-v6.md @@ -0,0 +1,85 @@ +# 2026-07-14 -- D-113 AMENDMENT: the LAN IPv6 the REST API cannot set + +Retroactive changelog for commit `522ba7d` (the script) and `3b01dd4` (the D-113 amendment). +The code shipped with a green harness and a clean lint, but **without this entry** -- the +delivery rule requires all three. Closing that gap; no code changes here. + +## The measurement that forced it + +D-113(a2) asserts the OPNsense edge is configured "via the REST API", and lists **interfaces** +among the API-covered surfaces. **Measured on the live Office1 edge (OPNsense 26.1): that is +false.** `Interfaces > [LAN]` is served by the LEGACY page `/interfaces.php?if=lan`. Only the +Devices and Neighbors sub-trees were ever migrated to MVC. **There is no REST endpoint for a base +interface's `ipaddrv6`.** + +The claim was **inferred** from DHCP and firewall being API-native, and never measured. It sat in +an ADOPTED decision as if it were a fact. D-113 now carries the amendment. + +## What shipped (already committed) + +- `scripts/opnsense-set-iface-v6.php` -- runs ON the edge; loads the LIVE config through + OPNsense's own `Config` singleton, mutates exactly two leaf fields (`ipaddrv6`, `subnetv6`), + asserts the interface count is unchanged, and lets the vendor's own code serialize it. +- `scripts/opnsense-set-interface-v6.sh` -- ships the setter over SSH, runs it, applies with + `configctl interface reconfigure`, then proves the address is **on the wire**. +- `tests/opnsense-set-interface-v6/run-tests.sh` -- offline harness, **40/40 PASS** (ssh/scp + stubbed; no edge contacted). + +**DRY BY DEFAULT.** Without `--commit` nothing is written and nothing is applied. + +## Why this is NOT the forbidden config.xml push + +The distinction is the whole point, and it is the difference between this and the path D-113(a2) +deleted. **It never authors a config.** It mutates two leaves of the edge's own live config +object through the vendor's own model -- the identical code path `interfaces.php` runs on Save. +Nothing is replaced wholesale, so the **API-managed Kea DHCP and the firewall rules cannot be +clobbered** (standing lesson 1: a full rendered config.xml push WOULD clobber them). + +Same principle as `opnsense-bootstrap-apikey.sh`: **call the vendor's interface, do not +re-implement the internals.** + +## Two traps it is built against + +- **root's shell on the edge is `tcsh`.** A `$(...)` inside a quoted remote command dies with + "Illegal variable name", and it dies QUIETLY. Every remote command here is fed to `sh -s` over + stdin (standing lesson 5). +- **Saving the config does NOT reconfigure the kernel.** Hence the explicit `configctl interface + reconfigure`, and hence the verify reads `ifconfig` -- the KERNEL -- and not the config file. A + `{"result":"saved"}`-style self-report is not evidence. + +## Status: WRITTEN AND TESTED, NEVER RUN AGAINST THE LIVE EDGE + +This is Stage 2 close-out item **C1**, and C1 is still **OPEN**. The script exists; the address is +not on the edge. Executing it is a live mutation and remains operator-gated. + +Two things to carry into that execution: + +- **The RA half is API-native** and is a separate step (`/api/radvd/settings/addEntry`). The + address alone is not the deliverable; the LAN needs RA to be useful. +- **IPv6 does not egress the lab** (measured 2026-07-13: v6 gateway reachable, v6 internet 100% + loss, v4 fine). So this proves v6 ADDRESSING / RA / services-binding -- most of D-101's family + matrix -- but NOT internet GUA reachability, which is unprovable in VR1. The WAN v6 leg is + DEFERRED per D-115's amendment; it would be a path to nowhere. + +## Reusable by the DC edges + +Stage 3 builds DC0's and DC1's edges from the same module. This script takes them verbatim -- only +the host and address change. That was the operator's stated reason for scripting it rather than +clicking it once in the GUI: **the same treatment gets paid three times.** + +## Known limitation (logged, not actioned) + +The interface-name-to-device mapping is a hardcoded `lan->vtnet0 / wan->vtnet1` sed. It is correct +for the Office1 edge as built, but it is an ASSUMPTION about device ordering that a DC edge with a +different NIC count could break. It would fail loudly (the verify reads the wrong device and the +script exits 2), not silently. Worth deriving from the edge's own config before Stage 3 reuses it. + +## Revert + + git rm scripts/opnsense-set-interface-v6.sh scripts/opnsense-set-iface-v6.php + git rm -r tests/opnsense-set-interface-v6/ + git rm docs/changelog-20260714-opnsense-set-interface-v6.md + +Nothing live depends on these: **the script has never been run with `--commit`,** so the live edge +is unaffected by its existence. The D-113 amendment prose (`3b01dd4`) is a separate revert and +should be KEPT regardless -- it records a measurement, not a tool. diff --git a/docs/session-ledger.md b/docs/session-ledger.md index b84b35d..a545050 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -30,19 +30,25 @@ ## Machine-derived (re-seed from `scripts/ledger-scan.sh`; do not hand-edit) -_As of the 2026-07-13 scan (the collapse). Re-run the scan to refresh._ +_Re-seeded from the 2026-07-14 scan. Re-run `bash scripts/ledger-scan.sh` to refresh._ - **PROPOSED / OPEN decisions:** D-068 (Vault substrate hardening, Roosevelt -- 1.16 ruled OUT per amendment; the off-EOL-1.8.8 path remains OPEN), D-071 (routine update cadence + Juju controller patch policy -- AMENDED, still PROPOSED, awaiting operator ratification; gated on the pre-DC-DC controller HA/backup session). -- **OPEN security rows:** SEC-001, SEC-003, SEC-004, SEC-005, SEC-006, SEC-007. - (The prior machine block listed only SEC-001/003/004 -- it was STALE by three rows.) -- **Next-free numbers:** D = 114, DOCFIX = 195, BUNDLEFIX = 013. - (The prior machine block said D=111 / DOCFIX=162 / BUNDLEFIX=012 -- STALE.) +- **OPEN security rows:** SEC-001, SEC-003, SEC-004, SEC-005, SEC-006, SEC-007, **SEC-008**. + (SEC-008 -- the vcloud re-enrolment key -- was MISSING from the prior block.) +- **Next-free numbers:** D = **119**, DOCFIX = 195, BUNDLEFIX = 013. + (The prior block said D=114 -- STALE by five; D-114..D-118 are all assigned and ADOPTED.) - **Standing numbering rule:** never write an identifier-shaped token (D-/DOCFIX-/BUNDLEFIX-NNN) ABOVE the real high-water mark anywhere in `docs/` or `runbooks/` prose -- `ledger-scan` reads prose and a decoy token inflates the next-free counter. This has bitten twice. +- **KNOWN `ledger-scan` DEFECT (found 2026-07-14, logged not fixed):** the scan reports **D-115 as + PROPOSED/OPEN. It is not -- D-115 is ADOPTED.** The scan matches the words "PROPOSED/OPEN" + anywhere in an entry's prose, and D-115's Status line reads "ADOPTED ... Originally PROPOSED/OPEN + the same day". It should read the `**Status:**` line, not the body. This is the *status* twin of + the decoy-token rule above. **Until it is fixed, do not trust the scan's PROPOSED/OPEN list + without checking the Status line.** D-115 is the only current false positive. @@ -59,7 +65,19 @@ - **VR0 / DC0 testcloud:** fleet fully current (juju 3.6.25, 91 agents); D-011 CLOSED except item 6 (manual unseal, blocked on SEC-003). devteam is a live tenant running their own cluster. No beta cluster, no foil tenant, no orphan trustees (see State facts). -- Branch `dc-dc-stage1-phase0-first-exec`, pushed. VR1 Stage 1 COMPLETE; Stage 2 partially done. +- Branch `dc-dc-stage1-phase0-first-exec`, **pushed and level with origin (2026-07-14)**. VR1 Stage 1 + COMPLETE; Stage 2 in CLOSE-OUT (C1..C4 open, C5 done). +- **SESSION CONSOLIDATION (2026-07-14).** Work had fragmented across FOUR concurrent Claude sessions + on this one clone -- including two long-lived `--permission-mode auto` background agents (idle 14-15h + but still holding auto-approve rights against the live cloud), and a second interactive session that + received the SAME "resume last night's work" prompt six minutes before this one and began bootstrapping + in parallel. All three were terminated and the supervising daemon stopped (it had respawned the agents + once). **Standing lesson: the daemon ADOPTS AND RESPAWNS background workers -- killing the child is not + enough; stop the daemon (`claude daemon stop --any`).** The working tree was clean and 3-ahead + throughout; nothing was lost or clobbered. ONE session is now the rule. +- **Last night's three commits (D-113 amendment + the v6 script) were made by the auto-permission + background agent and left UNPUSHED and UNDOCUMENTED.** Both closed 2026-07-14: pushed, and + `docs/changelog-20260714-opnsense-set-interface-v6.md` written. --- @@ -98,7 +116,17 @@ 1a. **PINNED -- NEXT AFTER NETBOX: deploy Office1's LAN IPv6** (operator ruling 2026-07-13, D-115 question 3: "register AND deploy dual-stack on Office1 now", sequenced AFTER the NetBox deploy). Add `2602:f3e2:f01:100::1/64` to the OPNsense edge LAN + RA, and let `voffice1` take its address. - Do it via the D-113(a2) REST API -- NEVER a config.xml push. + **CORRECTED 2026-07-14 (D-113 AMENDMENT -- the old instruction here was WRONG):** the ADDRESS half + CANNOT be done via the REST API. Measured on the live edge (OPNsense 26.1): `Interfaces > [LAN]` is + the LEGACY page `/interfaces.php?if=lan`; only Devices and Neighbors were migrated to MVC, and there + is **NO REST endpoint for a base interface's `ipaddrv6`**. D-113(a2)'s "interfaces are API-covered" + was INFERRED, never measured, and is false. + - **Address:** `scripts/opnsense-set-interface-v6.sh` (dry by default; harness 40/40). It is NOT a + config.xml push -- it mutates two leaves of the edge's LIVE config through OPNsense's own model, + so API-managed Kea and the firewall rules cannot be clobbered. **WRITTEN AND TESTED, NEVER RUN + WITH `--commit`.** + - **RA:** that half IS API-native -- `/api/radvd/settings/addEntry` via `scripts/opnsense-api.sh`. + Still forbidden either way: a rendered config.xml push. **KNOWN LIMIT, do not forget it:** IPv6 DOES NOT EGRESS THE LAB (measured 2026-07-13: v6 gateway reachable, v6 internet 100% loss, v4 fine). So this proves v6 ADDRESSING / RA / services-binding, which is most of D-101's family matrix -- but NOT internet GUA reachability, which is unprovable