diff --git a/docs/session-ledger.md b/docs/session-ledger.md index d5099bd..49464a6 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -18,6 +18,7 @@ --- + ## Machine-derived (re-seed from `scripts/ledger-scan.sh`; do not hand-edit) _As of repo HEAD 2026-07-04 (re-run the scan to refresh):_ @@ -38,6 +39,8 @@ --- + + ## Active build (this session) - **validate.sh D-011 runner -- modular.** Foundation committed: `scripts/lib-validate.sh` @@ -98,6 +101,7 @@ - D-050 (keystone policyd override) and the D-063-adjacent `identity:list_trusts=""` hardening remain PROPOSED/OPEN, not actioned. + ## Jumphost stream -- ops-update-20260705: WINDOW CLOSED 2026-07-05 (addenda 13-16) @@ -166,6 +170,7 @@ + ## State facts to remember - beta cluster left at **node_count=2** (deliberate; bonus resize acceptance coverage). @@ -178,3 +183,17 @@ - Consolidate 10 per-phase do-documents into `docs/v1-deploy-runbook.md`. - Set repo visibility PRIVATE (SEC-004). - v2-deferred: SSH on GitBucket (port 29418), IPv6 dual-stack, NetBox import bundle. + +### Cross-session pins (append-only) + +- D-071 completion sweep (2026-07-05, DONE): the ops-update window closed; D-071 was AMENDED + (create-backup premise corrected -- `juju create-backup`/`download-backup` DO exist on juju 3.6; + a 902MB controller backup was taken). D-071 REMAINS PROPOSED -- not ratified to ADOPTED. Operator + ratification is pending; the single-controller (no-HA) half of the risk is unresolved by backups + alone. See D-071 + its 2026-07-05 amendment. +- Pre-DC-DC controller HA/backup planning (PINNED): before the Roosevelt (DC-DC) phase, hold a + dedicated planning session on Juju controller High-Availability / backup architecture. Backups + now exist (per D-071 amendment); controller HA is the open design item. This resolves the + single-controller risk recorded under D-071 for production. + + diff --git a/docs/v1-redeploy-changelog.md b/docs/v1-redeploy-changelog.md index 64f585e..d1c64e3 100644 --- a/docs/v1-redeploy-changelog.md +++ b/docs/v1-redeploy-changelog.md @@ -2037,4 +2037,20 @@ readback grep (juju-ssh quoting); the render had in fact succeeded -- verified manually before proceeding. Gate philosophy held: it stopped on missing evidence. -Next-free after this push (per scan, keep this line unwrapped): D-073, DOCFIX-090, BUNDLEFIX-012. +### 2026-07-05 (addendum 19, main-chat stream) -- ledger delimited-section fences + fence-validator + D-068 appendix pointer (DOCFIX-090) + +Reconciliation after the jumphost ops-update window closed. Four coordinated doc/tooling changes: +- scripts/ledger-scan.sh: added a `--fences` MODE that validates session-ledger delimited-section + fence integrity (matched/ordered/non-overlapping/uniquely-named SECTION/END pairs; unknown-name + and missing-OWNER are WARN). DEFAULT output is byte-for-byte unchanged (separate mode). Harness + tests/ledger-scan/run-tests.sh extended (35 checks, all green); wired into the gauntlet. +- docs/session-ledger.md: applied the delimited-section convention -- machine-derived / main-chat / + jumphost (pre-existing) / shared fences. Additive only: original body byte-identical; validated + by `ledger-scan.sh --fences` (4 sections, 0 errors). Added a shared-section "Cross-session pins" + block (D-071 sweep outcome; pre-DC-DC controller HA/backup planning). +- runbooks/appendix-B: vault row note now points to docs/D-068-vault-1.8-vs-1.16-analysis.md + (1.8/stable is a deliberate compatibility pin, not currency; 1.16 ruled out; EOL is an open D-068 item). +- D-071 NOT ratified here (operator authority); its status stays PROPOSED, recorded in the pins. +REVERT: git checkout HEAD~ -- scripts/ledger-scan.sh tests/ledger-scan/run-tests.sh docs/session-ledger.md runbooks/appendix-B-asbuilt-version-lock.md + +Next-free after this push (per scan, keep this line unwrapped): D-073, DOCFIX-091, BUNDLEFIX-012. diff --git a/runbooks/appendix-B-asbuilt-version-lock.md b/runbooks/appendix-B-asbuilt-version-lock.md index d83f7f0..b52c694 100644 --- a/runbooks/appendix-B-asbuilt-version-lock.md +++ b/runbooks/appendix-B-asbuilt-version-lock.md @@ -77,6 +77,10 @@ ncc/dashboard/vault routers at 1136) on the SAME `8.0/stable` channel. This is benign under channel-pinning (all resolve to current `8.0/stable` on redeploy); recorded only for completeness. +- vault is pinned `1.8/stable` (rev 714) DELIBERATELY, not merely as currency: the `1.16` line is + a fundamentally different, incompatible charm (certs interface V0->V1, Raft-only, no upgrade + path, broken with Charmed Ceph). See docs/D-068-vault-1.8-vs-1.16-analysis.md + D-068 amendment + (2026-07-05). 1.8.8 is EOL -- getting off it is an OPEN D-068 item, with 1.16 ruled out. - EXCLUDED from the bundle: the `k8s` charm (channel `1.32/stable`) deployed on Juju machine 4 / MAAS `capi-mgmt` (10.12.4.100). That is the retired D-033 out-of-cloud node, slated for Phase 7 teardown; the in-cloud mgmt cluster diff --git a/scripts/ledger-scan.sh b/scripts/ledger-scan.sh index 506d380..83e1c10 100644 --- a/scripts/ledger-scan.sh +++ b/scripts/ledger-scan.sh @@ -15,6 +15,57 @@ REPO="${VR_REPO:-$(cd "$HERE/.." && pwd)}" DD="$REPO/docs/design-decisions.md" SL="$REPO/docs/security-ledger.md" +SESSL="$REPO/docs/session-ledger.md" + +# --- fence validator (delimited-section convention) -------------------------- +# Validates STRUCTURAL integrity of the ledger ownership fences: +# ... +# FATAL: unmatched / overlapping (no nesting) / duplicate-name / unclosed. +# WARN (non-fatal): unknown section name, missing/empty OWNER. No fences = OK +# (transition state). Separate --fences MODE; default report below is unchanged. +_vf_trim(){ local x="$1"; x="${x#"${x%%[![:space:]]*}"}"; x="${x%"${x##*[![:space:]]}"}"; printf '%s' "$x"; } +validate_fences() { + local f="$1" + [ -r "$f" ] || { echo " HOLD: session-ledger not readable: $f"; return 2; } + local errs=0 warns=0 nfence=0 ln=0 + local open_name="" open_line=0 seen=" " known=" machine-derived main-chat jumphost shared " + local line inner name owner ename + while IFS= read -r line || [ -n "$line" ]; do + ln=$((ln+1)) + case "$line" in + *'*}" + name="$(_vf_trim "${inner%%|*}")" + if [ "$inner" != "${inner#*OWNER:}" ]; then owner="$(_vf_trim "${inner#*OWNER:}")"; else owner="__none__"; fi + if [ -n "$open_name" ]; then echo " ERROR line $ln: SECTION $name opened while $open_name (line $open_line) still open -- no nesting"; errs=$((errs+1)); fi + if [ -z "$name" ]; then echo " ERROR line $ln: SECTION marker with unparseable name"; errs=$((errs+1)) + else + case "$seen" in *" $name "*) echo " ERROR line $ln: duplicate SECTION name $name"; errs=$((errs+1));; esac + case "$known" in *" $name "*) : ;; *) echo " WARN line $ln: unknown section name $name (known: main-chat jumphost machine-derived shared)"; warns=$((warns+1));; esac + if [ "$owner" = "__none__" ] || [ -z "$owner" ]; then echo " WARN line $ln: SECTION $name has missing/empty OWNER"; warns=$((warns+1)); fi + open_name="$name"; open_line="$ln"; seen="$seen$name " + fi + ;; + *'*}")" + if [ -z "$open_name" ]; then echo " ERROR line $ln: END $ename with no open SECTION"; errs=$((errs+1)) + elif [ "$ename" != "$open_name" ]; then echo " ERROR line $ln: END $ename does not match open SECTION $open_name (line $open_line)"; errs=$((errs+1)); open_name="" + else open_name=""; fi + ;; + esac + done < "$f" + [ -n "$open_name" ] && { echo " ERROR: unclosed SECTION $open_name opened at line $open_line"; errs=$((errs+1)); } + echo " fences: $nfence errors: $errs warnings: $warns" + if [ "$errs" -gt 0 ]; then echo " FENCE VALIDATION: FAIL"; return 1; fi + [ "$nfence" -eq 0 ] && echo " (no fences yet -- partial/unfenced ledger is OK during transition)" + echo " FENCE VALIDATION: OK"; return 0 +} + +if [ "${1:-}" = "--fences" ]; then + echo "=== ledger-scan --fences: session-ledger fence integrity ($SESSL) ===" + validate_fences "$SESSL"; exit $? +fi echo "=== ledger-scan: open-work ground truth (repo: $REPO) ===" diff --git a/tests/ledger-scan/run-tests.sh b/tests/ledger-scan/run-tests.sh index ed9035e..49e740f 100644 --- a/tests/ledger-scan/run-tests.sh +++ b/tests/ledger-scan/run-tests.sh @@ -58,4 +58,59 @@ echo "ledger" > "$W/docs/session-ledger.md" OUT3="$(VR_REPO="$W" bash "$SCRIPT" 2>&1)"; grep -q 'ledger present' <<<"$OUT3" && ok "detects present session-ledger" || no "detects present session-ledger" + +# ---------------- --fences mode: fence integrity validation ---------------- +ec(){ [ "$1" = "$2" ] && ok "$3" || no "$3 (rc=$1 want=$2)"; } +has(){ grep -q "$1" <<<"$2" && ok "$3" || no "$3 (missing: $1)"; } +FW="$(mktemp -d)"; mkdir -p "$FW/docs" +mkledgerf(){ printf '%s\n' "$1" > "$FW/docs/session-ledger.md"; } +runf(){ VR_REPO="$FW" bash "$SCRIPT" --fences 2>&1; } + +mkledgerf '# Ledger + +stuff + + +more +' +OUT="$(runf)"; ec "$?" 0 "fences well-formed exit0"; has 'FENCE VALIDATION: OK' "$OUT" "fences OK message" + +mkledgerf ' +no end' +OUT="$(runf)"; ec "$?" 1 "fences unclosed exit1"; has 'unclosed SECTION' "$OUT" "reports unclosed" + +mkledgerf 'text +' +OUT="$(runf)"; ec "$?" 1 "fences orphan-END exit1"; has 'with no open SECTION' "$OUT" "reports orphan END" + +mkledgerf ' +' +OUT="$(runf)"; ec "$?" 1 "fences mismatch exit1"; has 'does not match open SECTION' "$OUT" "reports mismatch" + +mkledgerf ' + + +' +OUT="$(runf)"; ec "$?" 1 "fences nested exit1"; has 'no nesting' "$OUT" "reports nesting" + +mkledgerf ' + + +' +OUT="$(runf)"; ec "$?" 1 "fences duplicate exit1"; has 'duplicate SECTION name' "$OUT" "reports duplicate" + +mkledgerf ' +' +OUT="$(runf)"; ec "$?" 0 "fences unknown-name non-fatal exit0"; has 'unknown section name' "$OUT" "warns unknown name" + +mkledgerf '# Ledger +prose only, no fences' +OUT="$(runf)"; ec "$?" 0 "fences none exit0"; has 'no fences yet' "$OUT" "transition-OK note" + +mkledgerf ' +' +OUT="$(runf)"; ec "$?" 0 "fences missing-owner non-fatal exit0"; has 'missing/empty OWNER' "$OUT" "warns missing/empty owner" + +OUT="$(VR_REPO="$W" bash "$SCRIPT" 2>&1)"; has 'next-free' "$OUT" "default mode intact (next-free present)" + echo; [ "$F" = 0 ] && { echo "ALL PASS ($P checks)"; exit 0; } || { echo "FAILURES: $F"; exit 1; }