| 2026-07-26 |

D-137 phase 3: re-measure the register; SEC-024 opened
...
~21 inferred filenames corrected against their own mint-refs. The session had
owned six; those were only the rows the first sweep physically touched.
Octavia's 8 real basenames plus its THREE SUBDIRECTORIES (the bare
~/octavia-pki/* pattern matched ZERO of the 8 artifacts, two of them CA
private keys); vault's init.txt; the tenant rows' <client>- instance prefix,
with a placeholder matcher so they can match a real basename at all;
kube-config -> config; .maas.cli -> .maascli.db (measured: ~/.maas.cli does
not exist).
sec-ref mis-attribution corrected: both juju-maas-user rows cited SEC-020;
the on-subject rows are SEC-018/-019. This is the SEC-015 class recurring --
one instance was fixed by hand earlier without checking whether it was
systemic. It was.
mint-ref corrected: admin.pass cited a CONSUMER (:452), not the mint (:450);
the RGW key cites a line that takes it as INPUT, so it is operator-terminal.
Rows added: the libvirt SSH power password (standing rotation obligation from
a 2026-06-26 plaintext exposure, in neither the matrix nor the exclusions),
vault-ca-root, and the tfstate siblings. ~/as-executed/ deliberately NOT
declared, reason recorded: it holds session logs, so declaring it made every
log a false UNDECLARED.
Four further defects in my own phase-2 changes, found by RUNNING the sweep:
shlex.quote broke ~ expansion; a non-existent parent read as unreadable
rather than absent; site-key `-` needed shared-location semantics; the
tfstate siblings needed rows.
SEC-024 OPENED -- new exposure found by the fixed checker on its first clean
run: opentofu/terraform.tfstate.backup is mode 0664 and carries the MAAS API
key in plaintext (DOCFIX-175); the live state file is 0600. Invisible to
every prior control because the world-readable check was custody-gated and
the siblings were undeclared. LOGGED, NOT ACTIONED (hard rule 1).
Findings 35 -> 13, all true. Matrix 81 rows. Harness 44/44, creds-audit
15/15, gauntlet ALL GREEN (80), repo-lint 0-fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 phase 2: fix the eight confirmed false-greens, regression-lock each
...
Tier 2 is now scoped by (host-role, SITE-KEY). Keying on role alone merged
all three jumphost creds folders into one basename namespace, so a credential
present at ANY site satisfied the row for EVERY site -- how SEC-021's dc0
opnsense-api.txt absence passed a green sweep while tier 1 flagged it. Four
committee lenses converged on this. vm-secret-locations gains a site column.
Also fixed, each reproduced by the committee and each now regression-locked:
- probe_remote called a successfully-read location UNREACHABLE when its glob
matched nothing, gating the role and turning every absence FAIL there into
an [ok]; a __DONE__ liveness marker now separates "ran, found nothing" from
"could not reach".
- literal-file locations skipped absent/unreadable detection entirely (the
T33 false green surviving in the other path form).
- an EMPTY locations list bypassed ruling 3's refusal.
- a zero-row matrix reported clean over the empty set.
- a mint-ref pointing at a directory crashed with exit 1 -- indistinguishable
from findings -- and skipped S5/S6/S7, silently dropping the conflation and
asymmetry findings.
- mode enforcement was custody-gated, leaving 43 of 77 rows unchecked
including the SEC-020 and SEC-022 stores; a world-readable check now
applies at any custody.
- S5's dc1->dc0 direction was untested; deleting it left the harness green.
- rows with no declared location for their scope are disclosed, not skipped.
- declared paths/targets are charset-validated and shell-quoted: an
unvalidated `;` reached a remote shell, under sudo when --privileged.
- creds-audit sprawl globs now cover dotfiles; the prior widening could not
see .admin.pass, the dotfile sibling of its own motivating secret.
T24 replaced with a checked-in finding-CLASS baseline. The old form grepped
for the literal "IDENTITY CONFLATION", so REMEDIATING that defect would have
turned the gauntlet red for whoever did the right thing -- making "edit the
test" the cheapest response exactly when the guard mattered.
Harness 35 -> 43, creds-audit 13 -> 15, gauntlet ALL GREEN (80), lint 0-fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 committee audit + phase 1: correct the record
...
Six-lens read-only review found the delivered tier-1/tier-2 build has eight
reproduced false-greens and that the records overstated what was verified.
Four lenses converged independently on tier 2 being SITE-BLIND.
Record corrections only in this commit (no code, no data):
- NEW docs/audit/d137-location-listing-20260726.txt: the actual stat listing.
CURRENT-STATE asserted remote filenames/modes citing the tier-2 capture,
which is a VERDICT file containing none of them -- a GA-R1 rule 2 defect.
- .maas.cli was NEVER MEASURED and is wrong; the MAAS snap CLI profile is at
~/snap/maas/current/.maascli.db (measured). It sat inside the fix for the
inferred-filename miss, so the corrected count is ~21, not six.
- WITHDRAWN: "ZERO undeclared files ... fully accounted for" (rested on the
site-blind check). CORRECTED: "SEC-021 on-disk half REPRODUCED" now reads
power-key artifacts only -- opnsense-api.txt's absence was masked.
- CURRENT-STATE's "virsh gap NOT fixed" corrected; it was fixed in 6d43619,
whose commit did not touch the status authority because L10's trigger set
excludes .claude/settings.json.
- Superseding figures recorded; stale "14 NOT JUDGED" -> 2; glob is *.pass.
- 12-column amendment recorded on D-137 in design-decisions.md; it previously
lived only in a changelog (non-citable per GA-R2 rule 1) and the build spec.
- Repaired citations my own edits broke (guard moved 71-73 -> 90-92), the
plan's 12-vs-27 operator-terminal count, the "two the ruling did not name"
claim (it is one), and an unsupported ruling date in the notes.
Code and data remediation follows in later commits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
permissions: close the ssh 'virsh quoting gap (twin of the sudo gap)
...
Bash(ssh * virsh *) had the same defect as the sudo rule: it needs a literal
space before `virsh`, so a quoted remote command ssh <host> 'virsh ...'
matched no rule and fell through to the classifier. Added the single- and
double-quoted variants.
Verified against real command shapes: quoted, double-quoted and bare forms
all resolve to an ask rule; ssh <host> 'sudo virsh ...' resolves via the sudo
rule; an unrelated ssh <host> 'ls -la' still matches nothing, so the widening
did not become a catch-all. All rules are `ask`, never `allow`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 tier 2: privileged sweep completed; permission gap fixed; 6 inferred filenames corrected
...
The block was a PATTERN GAP, not a policy conflict. Bash(ssh * sudo *) was
already in the project ask list, but it needs a literal space before `sudo`
and the command was ssh <host> 'sudo ...' -- the quote meant no rule matched,
so it fell through to the classifier. Added the quoted variants plus a
targeted rule for the privileged invocation; all `ask`, never `allow`.
FLAGGED NOT FIXED (hard rule 1): Bash(ssh * virsh *) has the identical gap.
MEASURED (docs/audit/d137-tier2-privileged-20260726.txt): the region secrets
dir holds exactly admin.apikey/admin.pass/db.pass/lxd-trust.pass at 0600 --
precisely the SEC-020(i) carve-out list; netbox holds admin.pass/api.token/
secret_key at 0600; the SEC-022 shadow stores are confirmed and now audited.
ZERO undeclared files at any declared location: SEC-020 and SEC-022 are
accounted for.
OWNED: the first matrix draft carried SIX filenames I authored plausibly
instead of measuring -- a hard-rule-2 miss. The checker's undeclared-detection
caught every one on first contact with real hosts. Corrected to measured
values, real locations declared, and five colocated non-secret artifacts
declared so the check stays meaningful. Matrix 72 -> 77 rows.
9 findings remain, all true: SEC-021 (3 declaration + 2 on-disk), the ruling-5
conflation, and 3 S5 asymmetries -- dc0's snap power key is the generic
id_ed25519 while dc1's is the dedicated id_dc1_power, the SEC-012/SEC-016
per-DC-isolation divergence made visible.
Harness 35/35, gauntlet ALL GREEN (80), repo-lint 0-fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 tier 2: wire --privileged escalation (run BLOCKED by the auto-mode classifier)
...
--privileged retries a remote probe with `sudo -n` ONLY where the
unprivileged probe returned `unreadable`, keeping the privileged surface as
small as ruling 3 keeps the search surface. Metadata only; the T22
metadata-only guard still passes; verified inert (zero escalations) when
nothing is unreadable.
The operator approved the privileged read, but the auto-mode classifier
denied the remote-sudo shape -- the same wall the 2026-07-23 close recorded,
whose noted fix is manual permission mode. NOT worked around: reading a
root-owned directory is exactly the privilege being denied, so any
alternative would fail identically or misrepresent the result.
SEC-022 shadow-store contents and the SEC-020 region secrets stay
UNCONFIRMED; the sweep reports them as E0 UNREADABLE FAILs, which is the
correct outcome for an audit that could not look.
Also de-staled the docstring tier table (tier 2 is built).
Harness 35/35, gauntlet ALL GREEN (80), repo-lint 0-fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 tier 2: live read-only sweep + two probe false-greens it exposed
...
SEC-021's ON-DISK half REPRODUCED: vr1-dc0-maas-power_ed25519{,.pub} are
genuinely absent from the dc0 jumphost creds folder, not merely undeclared.
Capture: docs/audit/d137-tier2-sweep-20260726.txt (read-only, no sudo,
stat-over-ssh metadata only; jumphost + voffice1 + office1-netbox).
Running it against real hosts exposed two false-greens in the probe:
1. "absent" vs "could not look". /root/maas-secrets and /root/netbox-secrets
reported "does not exist" -- they exist; SEC-020 is precisely about
admin.pass living in the first. An unprivileged [ -d ] on a root-owned
parent is indistinguishable from a missing directory. The probe now tests
parent traversability and reports UNREADABLE as a FAIL, not a skip.
2. Role-level aggregation manufactured false absences: skipping one location
let a role's other locations cover it, so credentials in the unprobed
location reported EXPECTED-BUT-ABSENT. Absence is now asserted only over
fully-probed roles (14 rows report NOT JUDGED). Refinement: "absent" is a
conclusive observation and does not gate a role; only unreadable and
unreachable do.
Harness 33 -> 35 (T33 unreadable location, T34 absent location).
Gauntlet ALL GREEN (80), repo-lint 0-fail.
OUTSTANDING: the two root-owned dirs need a privileged read, so SEC-022's
shadow-store contents and the SEC-020 region secrets stay UNCONFIRMED. That
is a remote-sudo shape and was not run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 tier 2 (EXISTENCE): declared-location bound, existence checks, widened globs
...
Tooling only -- the LIVE three-site sweep has NOT run, so SEC-021's on-disk
half, SEC-022 and SEC-023 are not yet reproduced as live findings.
creds-manifests/vm-secret-locations (ruling 3): the absolute bound on
discovery. Covers the jumphost creds folders, the SEC-022 headend shadow
stores, the SEC-020 region secrets dir, the three dirs outside the SEC-009
convention, plus two the ruling's note did not name -- the in-clone PKI
overlay and the DOCFIX-175 plaintext tfstate. Tenant dirs are LOCAL-only:
no tenant VM/project/host is reachable from this file, so D-069 holds by
construction rather than by good behaviour. `<repo>` is substituted at
runtime (D-110: the repo name is never hardcoded).
creds-matrix.py --tier2 [--remote]: E1 expected-but-absent, E2 mode, E3
undeclared-at-a-declared-location. Probes are `stat` over ssh -- metadata
only, never content. An unreachable host SKIPS with an explicit [ok],
because "could not look" must never read as "nothing there".
--pending-stage is supplied by the CALLER so a not-yet-reached mint stage
defers instead of failing; encoding which stages have run would put a
status claim in code, and GA-R1 puts status in CURRENT-STATE only.
creds-audit.sh sprawl globs widened for the SEC-023 blind spots
(admin.pass, *.apikey, *.key, *.pem, *_ed25519, *_rsa, *openrc*). The old
six patterns could not have matched the SEC-020 secret that sat undetected
12 days, any bare SSH key or PEM, or the predicted Stage-5 ~/admin-openrc.
Harnesses: creds-matrix 33/33 (8 new tier-2 cases), creds-audit 13/13
(was 7; five blind-spot fixtures plus T13 asserting the widening did NOT
become trigger-happy). Gauntlet ALL GREEN (80), repo-lint 0-fail.
FLAGGED DIVERGENCE from the plan's Files list: --remote is implemented in
creds-matrix.py, not creds-audit.sh. Tier 2 IS the preflight Pn and the
matrix plus locations list both live there; splitting it would mean two
implementations of one bounded walk. Ruling 3 names a bound, not a script.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 tier 1 (STATIC): credential matrix, checker, harness -- RED by design
...
Schema amended 10 -> 12 columns (operator-ruled 2026-07-26, "go with the
12-column amendment"): adds `custody` + `notes-ref` and re-keys a row to one
(credential, location) pair. A read-first round-trip check of the three
manifests proved the 10-column form could not carry what D-137 ruling 2
forbids dropping -- and ruling 3 puts four deliberate credential copies inside
--remote's declared locations, where creds-audit.sh:63-67 would report each as
UNDECLARED. Implements ruling 2 ("prose must become MATRIX FIELDS"); OPS under
GA-R3, the five sub-rulings untouched.
Ships: creds-matrix.tsv (72 rows), creds-matrix-notes.md (32 keys),
scripts/creds-matrix.py (S1 schema / S2 both-bounds manifest coverage / S3
render drift / S4 mint-ref resolution / S5 per-DC symmetry / S6 ruling-5
principal invariant / S7 notes integrity), tests/creds-matrix 24/24.
Gauntlet ALL GREEN (80, was 79); repo-lint 0-fail.
RED BY DESIGN, 5 findings -- do not go green by deleting rows: the ruling-5
identity conflation on maas-region-admin (the live SEC-020 defect), 3x
EXPECTED-BUT-ABSENT for SEC-021's declaration half, and the S5 asymmetry on
dc0's divergently-named headend power key. 27 operator-terminal rows are the
FINDING 1 reproducibility debt, counted not faulted.
Two self-inflicted false-greens found and closed during the build: S2
enumerated only matrix-mentioned sites (an empty matrix read CLEAN -- the
sandbox-fidelity-check.py:131-143 defect one level up; now the union of both
sides, regression-locked by T9), and the uniqueness key false-failed two
identities sharing one file (T6).
NOT done, stated so the boundary is unambiguous: tiers 2-3, vm-secret-locations,
the SEC-009 demotion, and the ruling-1 preflight Pn -- whose blocking-vs-deferred
sequencing is an OPEN operator question. No manifest rewritten; no cloud state
touched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

audit: transcribe the full 55-row credential inventory in-repo (matrix seed)
...
The capture's body previously said the per-row tables lived in the session
transcript. The operator is clearing that session to start the build, so the
matrix SEED would have been lost and would have cost another full inventory
run to re-derive.
Appendix now carries all of it: section A (25 scripted MINT rows), section B
(30 runbook/prose MINT rows), section D (charm-minted, with the keystone admin
password called out as in-scope per the materialization ruling), section E
(MATERIALIZE/UPLOAD checked and excluded, with file:line so the exclusions are
auditable), and section G (coverage boundary, incl. what was deliberately not
opened). Corrected the body pointer that referenced the transcript.
Notable rows the build will need: A6 the MAAS rack-enrollment secret (a mint
nothing else records), B5-B12 the eight Octavia PKI artifacts, B13 the overlay
that lands a CA key + plaintext passphrase inside the repo clone, B14 the
juju-<dc> superusers stored nowhere, B22 the per-DC power key whose dc0 copy is
missing (SEC-021), and A21-A25 which DUPLICATE A11-A14 -- one credential set,
two creation locations, do not double-count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137: repo-carried build spec for the fresh-session handoff
...
Operator directed the build to a fresh session. The design and committee review
lived in a plan file under ~/.claude/plans/ -- outside the repo -- so a session
bootstrapping via the GA-R4 path (CURRENT-STATE -> session-ledger ->
ledger-scan) would never have surfaced it. That is a real durability gap given
repo-is-authoritative, so the spec now lives at
docs/D-137-implementation-plan.md (precedent: docs/D-068-vault-migration-plan-draft.md).
Carries the five ruled decisions and their consequences, the matrix schema
(logical keys only per the SEC-004 ruling), the checker model with file:line
precedents (provider-bundle-check for structure, sandbox-fidelity-check:131-143
for both-bounds), the three tiers, inherited constraints (metadata-only
source-grep guard, gauntlet excerpt regex, preflight 0/1/2, L1/L10), the file
list, and the acceptance test.
Explicitly instructs the next session NOT to make the first run green: the
acceptance test is that the checker REPRODUCES SEC-021/-022/-023 and the
predicted admin-openrc case as NAMED failures. A checker that passes on today's
tree is wrong. Going green is a separate remediation project.
CURRENT-STATE item 9 + the ledger addendum point at it. Auto-memory pointer
updated: policy authority is D-137 per sub-ruling 4, with a verify-first caveat
that creds-audit CLEAN is not evidence of completeness.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 sub-ruling 5 RULED + D-137 ADOPTED: implementation unblocked
...
Question as presented: "D-137 ruling 5 of 5 -- identity fold-in ... the matrix
has a principal column, which would make the conflation machine-detectable.
Fold it in, or keep it separate?" Operator selection, exact: "Fold in as a
D-137 invariant (Recommended)".
D-137 gains the invariant ONE IDENTITY SERVES ONE PRINCIPAL TYPE, enforced by
the matrix principal column: a credential serving both human and service is a
FAIL, not an observation. The SEC-020 conflation becomes machine-detectable and
needs no separate D-number -- the question SEC-020 left proposable-but-unassigned
is absorbed here. First run is expected RED by design: admin serves a human GUI
row and a service API row simultaneously, which is the defect.
ALL FIVE SUB-RULINGS RULED -> Status flips to ADOPTED 2026-07-26. Each was its
own operator exchange, committed and pushed before the next was asked (GA-R5).
Also fixes the Status LINE to lead with ADOPTED so ledger-scan attributes it
correctly (it keys on the last **Status:** line, and the entry was still being
counted as an open decision); scan now shows 4 open decisions. Ledger machine
block re-seeded, POST-CLOSE ADDENDUM added per the 07-18/07-21 precedent.
Implementation is UNBLOCKED but NOT STARTED.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 sub-ruling 4 RULED (GA-R5): D-137 is the credential-lifecycle authority
...
Question as presented: "D-137 ruling 4 of 5 -- policy home. Where does the
credential-lifecycle RULE live as authority?" Operator selection, exact:
"D-137 is the authority (Recommended)".
D-137 becomes the policy authority; the SEC-009 standing-convention block in
docs/security-ledger.md demotes to a pointer, and the ledger returns to being
purely a register of exposures and rotation obligations -- its stated purpose
and the GA-R4/F3 intent. Gates can then cite a D-number rather than an exposure
register. Satisfies the GA-R3 A1 test: a Roosevelt session greps
design-decisions before touching a built surface, so the rule must live there.
Implementation care recorded: the demotion MOVES the convention text, it does
not delete it. SEC-009's block carries the founding history (the NetBox-token
miss) which stays in the ledger as history while the RULE moves to D-137.
D-137 stays PROPOSED; sub-ruling 5 OPEN; nothing built until all five.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 sub-ruling 3 RULED (GA-R5): remote discovery = declared locations only
...
Question as presented: "D-137 ruling 3 of 5 -- remote discovery scope ... The
tension is tenant isolation on a commercial multi-tenant cloud." Operator
selection, exact: "Declared locations only (Recommended)". Dated 2026-07-26 --
this session crossed the date boundary; sub-rulings 1-2 were pushed 2026-07-25.
A declared list (creds-manifests/vm-secret-locations, <host-role> <path>) bounds
--remote absolutely; it never walks outside the list. No tenant surface touched,
D-069 custody boundary and hard-isolation posture preserved, fast on a live
region.
Records a faithful-implementation note (NOT an override): the list must be
populated with everything already known to hold credential material or the
ruling leaves measured defects uncovered -- per-site creds folders on the
headend as well as the jumphost (SEC-022 shadow stores), the region secrets
dir (SEC-020), and the three directories outside the SEC-009 convention found
by the creation-point inventory. Accepted residual: a secret minted at an
undeclared location stays invisible, so adding a row is part of DoD for any new
mint site.
D-137 stays PROPOSED; sub-rulings 4-5 OPEN; nothing built until all five.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-07-25 |

D-137 sub-ruling 2 RULED (GA-R5): derive manifests from the matrix
...
Question as presented: "D-137 ruling 2 of 5 -- derivation ... How do the
matrix and the manifests relate?" Operator selection, exact: "Derive
manifests from matrix (Recommended)".
Matrix becomes SINGLE SOURCE; --render regenerates creds-manifests/*.manifest
and the gauntlet fails on rendered-vs-checked-in drift. Kills the dual
maintenance the committee flagged as the GA-F06 staleness risk.
Accepted cost recorded at ruling time: manifests become generated artifacts,
so their governance prose (SEC linkage, mint dates, off-manifest carve-outs,
un-consolidated findings) must become matrix FIELDS or be regenerated into the
rendered header -- not silently dropped. That prose is load-bearing: it is what
recorded the dc0 edge-keypair backfill debt.
D-137 stays PROPOSED; sub-rulings 3-5 OPEN; nothing built until all five.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 sub-ruling 1 RULED (GA-R5): enforcement = blocking in preflight
...
Question as presented: "D-137 ruling 1 of 5 -- enforcement strength. This
decides whether the rule binds or merely advises, and it gates rulings 2-5."
Operator selection, exact: "Blocking in preflight".
Consequence: the credential check lands as a new Pn in scripts/preflight.sh
and HARD-FAILS on any expected-but-absent, undeclared, or per-DC-asymmetric
credential -- no deploy proceeds past a credential gap. Not adopted: the
PreToolUse guard (an agent is constrained at the deploy gate, not at write
time) and advisory-only, which is precisely the posture that failed -- the
phase-3 prose rule was advisory and never fired at either DC standup.
D-137 stays PROPOSED. Sub-rulings 2-5 (derivation, --remote scope, policy
home, identity fold-in) are OPEN and NOTHING is built until all five are
ruled, one exchange each per GA-R5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

audit: credential creation-point inventory -- 12 declared secrets have NO mint command
...
Read-only research capture seeding the D-137 credential matrix. Nothing built;
the five D-137 forks remain unruled.
FINDING 1 (qualifies the premise that creation points are already known):
`grep -rnI "ssh-keygen" .` returns ZERO hits repo-wide -- no mkpasswd, pwgen,
uuidgen, random_password or tls_private_key either. Yet the manifests declare
six SSH keypairs plus the OPNsense root password/hash, whose only provenance
record is a manifest COMMENT. Twelve standing credentials therefore have no
reproducible mint. If the jumphost is rebuilt, no repo artifact says how to
recreate them -- a Roosevelt-transfer defect, not merely hygiene. Consequence
for the design: mint-ref must admit three provenance kinds (script:line,
runbook:step, operator-terminal), and the operator-terminal rows are the debt
the matrix exists to surface, not parse errors.
FINDING 2: three credential DIRECTORIES sit outside the SEC-009 per-site creds
convention and outside creds-audit entirely -- the Vault init directory (5
unseal shares + root token), the Octavia PKI directory (8 artifacts including
two CA private keys), and the per-tenant directories. All are named in
CLAUDE.md, so they are known; they are simply uncontrolled. Materially extends
SEC-023. Also flags overlays/octavia-pki.yaml, which lands a CA key blob plus a
plaintext passphrase inside the repo clone, gitignored -- with SEC-004 the repo
still PUBLIC.
FINDING 4 corroborates SEC-020 independently: phase-4 runbook:128 mints the
juju-<dc> superusers with no --password (stored nowhere), and
site-headend-install.sh:452 uses --password in argv.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

SEC-021/-022/-023: live credential defects found during D-137 matrix research
...
Recorded, NOT actioned (hard rule 1). Open SEC rows 16 -> 19. These surfaced
while gathering inputs for the credential matrix and are live defects
independent of any D-137 ruling.
SEC-021 dc0 custody defects. (a) changelog-20260719-dc0-deploy-stepB.md:263
records the dc0 edge REST API credential consolidated to
~/vr1-dc0-creds/opnsense-api.txt; that file exists for office1 and dc1 only --
no dc0 copy -- and creds-audit vr1-dc0 says CLEAN because it was never
manifested. A changelog asserting consolidation is not evidence it survived.
Not a lockout: the dc0 edge SSH key is present, D-112(c) SSH is the primary
path. (b) dc0 power key = maas-virsh_ed25519 on voffice1 vs dc1's declared
vr1-dc1-maas-power_ed25519{,.pub} on vcloud -- name/host/custody divergence.
SEC-022 two UNAUDITED shadow *-creds/ stores on voffice1 holding real key
material. creds-audit.sh:29-31 resolves under $HOME on the host it runs on
(vcloud) and has no ssh, so an entire parallel store is structurally
invisible. A scope gap in the control, not a one-off: D-128 puts Plane-2
execution on voffice1.
SEC-023 sprawl globs (creds-audit.sh:72-73) miss admin.pass, *.apikey, *.key,
*.pem, *_ed25519, maas-api-key.txt. Plus a PREDICTED exposure recorded before
it exists: phase-03-admin-openrc.sh:32,72 writes OS_PASSWORD into
$HOME/admin-openrc at Stage 5, home root, matching no glob.
Also reconciles the CURRENT-STATE G14 evidence cell to a measured 19 (it read
12 from 07-23) since this commit adds three of them, and re-seeds the ledger
machine block.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-137 PROPOSED: credential mint-and-consolidate pipeline -- make consolidation structural [ARCH]
...
Operator ask: a better method for minting/saving credentials, plus "a durable
rule to make sure when accounts are created there is a consolidation that
happens every time so credentials aren't misplaced or lost."
PROPOSED only -- no tooling built, no enforcement wired, no SEC-009 demotion.
Three forks are presented for ruling, one exchange each (GA-R5).
Measured diagnosis: the existing SEC-009 control CANNOT DETECT THE FAILURE
CLASS IT WAS BUILT FOR, because creds-audit is DECLARATION-based -- the
manifest IS the declaration, so an undeclared secret is structurally
invisible and a green CLEAN is not evidence nothing was missed.
- creds-audit vr1-office1 read CLEAN on 2026-07-15 while four secrets minted
on the region VM on 2026-07-13 (site-headend-install.sh:416-455) sat
undeclared; admin.pass surfaced only 2026-07-25 (SEC-020), 12 days later,
and only because a task went looking.
- No --all (creds-audit.sh:25 takes one mandatory site).
- Enforcement is PROSE in exactly one place (phase-3 runbook:498), absent from
preflight/cloud-assert/repo-lint/gauntlet -- and it did not fire at EITHER
DC standup (dc0 3 + dc1 1 undeclared at this session's open).
- No D governs credential mint/custody generally (D-069 = Vault unseal keys,
D-126 = per-env key isolation), so the rule lives only in the security
ledger -- a register of exposures, which no gate can cite as authority.
Proposal: mint-and-consolidate as ONE operation (creds-mint.sh), plus the
DISCOVERY inversion (creds-audit --all/--remote against declared VM secret
locations -- the only piece that would have caught admin.pass), plus gate
wiring, plus a site-headend-install.sh amendment at the source.
The SEC-020 identity-conflation question stays SEPARATE (not batched, GA-R5)
and remains D-unassigned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

session-ledger: 2026-07-25 close bookend (SEC-020 MAAS admin recovery, 15 lines)
...
GA-R4 rule 1 bounded summary for the MAAS admin-account recovery session.
Landed EARLY per the 07-21 precedent -- this ledger carries FOUR
"disconnected before its bookend" entries and this is a background session,
so durable-early beats narrating a close that may not happen. Further work
this session appends a POST-CLOSE ADDENDUM.
Also FLAGS, without acting on it, a pre-existing GA-R4 F1 breach: the live
ledger was already 370 lines at session OPEN against the 300-line cap, so
rotation of the oldest closed-session summaries is OWED. Not done here --
that is a judgment call about what to archive, it predates this session, and
unilaterally rotating 90+ lines of other sessions' summaries is exactly the
record-churn the GA-R7 circuit-breaker warns about. Better placed at the
imminent Stage 4 close, with the operator aware.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

SEC-020: MAAS admin-account recovery -- consolidate `admin`, mint `operator`, creds-audit CLEAN x3
...
Operator task: recover the MAAS admin accounts and save the creds retrievably.
The task premise was FALSIFIED by measurement before any mutation: the ledger's
2026-07-25 "MAAS web-GUI login does NOT exist / never minted" was wrong. The
`admin` password was minted 2026-07-13 by site-headend-install.sh:452 and is
MEASURED working (login 204 vs 400 wrong-password control). It was merely
root-only on voffice1 and un-consolidated -- the SEC-009 miss class, not a
missing account. Scope re-ruled by the operator after that finding:
"New account + consolidate only" -- no existing password rotated.
- NEW MAAS superuser `operator` for human GUI login; password generated on
vcloud, set via stdin (never argv -- SEC-018 discipline; changepassword has
no --password and createadmin --password would expose it).
- `admin` password CONSOLIDATED byte-identical (sha256-verified) to
~/vr1-office1-creds/. NOT rotated, so the VM copy REMAINS source-of-record;
a future rotation must update both or the VM copy becomes a stale trap.
- juju-vr1-dc0/dc1 untouched BY RULING: their SEC-018/019 API keys are
load-bearing for the blocked Stage-5 bootstrap.
- Manifest backfill -> creds-audit CLEAN on all three sites (was RED: dc0 3 +
dc1 1 undeclared), closing the dc0 edge-keypair queued finding named in
vr1-dc1.manifest:18-20.
- Root cause recorded: `admin` doubles as the automation identity (19 `maas
admin` call sites in 4 scripts), so automation never needed the password and
nothing forced it into the folder. Proposable as the next-free D-number
[ARCH]; NOT assigned (GA-R3 doubt-resolves-DOWN).
- Falsified ledger claim struck+corrected; machine-derived block re-seeded
(was stale on every line: SEC 12->16, next-free D 133->137, DOCFIX 200->204).
Evidence: docs/audit/maas-admin-recovery-20260725.txt (generated, no secret
values; scanned for password bytes = 0 hits with a planted-control check).
repo-lint 0 fail; gauntlet ALL GREEN (79).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
skill: stage-close sweep keeps the authoritative dir current + regenerates the Chat snapshot
...
SKILL.md stage-close rule now states the authoritative, versioned skill is the
.claude/skills/openstack-cloud-ops/ dir (SKILL.md + references), kept current at each
stage-close sweep, and the dated single-file Chat-upload snapshot is REGENERATED from it
then (a derived artifact, never the source; the stale Chat mount is a known failure mode).
Regenerated openstack-cloud-ops-consolidated-20260725.md from the updated skill so the two
stay in sync. ASCII/LF verified; repo-lint 0 fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|
settings: allowlist read-only whole-host budget calculator
...
From the /fewer-permission-prompts pass: adds Bash(python3
scripts/dc-dc-whole-host-budget.py*) -- a read-only capacity calculator, sibling of
the already-allowed provider-bundle-check.py. Edit was operator-approved earlier this
session; committing now for a clean tree.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|
session-ledger: 2026-07-25 close block (GA-R4, 14 lines)
...
Bounded session-close summary at the ledger tail (machine-derived fence untouched).
LEAD item = the MAAS web-GUI login gap (operator locked out; SEC-018/019 are
API/Juju-only, service accounts have random unstored passwords) -- flagged as the
IMMEDIATE-NEXT to process first on the fresh session. Records the completed record
phase (rulings/D-136/DOCFIX-201-203), DC0 mirror complete, and the pending gated
execution path. Durable for /clear.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|
DOCFIX-203: mark D-134's superseded original band table in place
...
The 2026-07-23 AMENDMENT header says it supersedes the original D-134 band table,
but the original carried no in-place marker -- a reader grepping D-134 hits the
original first and gets the REJECTED bands (control .10-.19 / compute .20-.49 /
storage .201-.254). The Chat seat did exactly this 2026-07-25. Added a SUPERSEDED
marker immediately above the original, pointing at the authoritative contiguous
table; original kept as history. Single text addition, no behavioural effect.
repo-lint 0 fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|
DOCFIX-202: demote buildout-scope section 5 to a D-111 pointer
...
docs/dc-dc-netbox-buildout-scope.md section 5 framed the per-DC v6 subcarve
(sub-decision 1) as open/un-ratified at lines 5/132/138, contradicting the
ratified pointer already at line 146 ("RATIFIED as D-111 2026-07-11"). D-111 is
ADOPTED and names this exact sub-decision; the stale open framing misled the Chat
seat 2026-07-25 (treated the subcarve as a blocking trust gate). Demoted the three
sites to acknowledge D-111; reasoning kept as history. Single-file text; no
behavioural effect. repo-lint 0 fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|

D-136 PROPOSED (NetBox-coupled render pipeline) + Ruling 3 + DOCFIX-201
...
Register append + CURRENT-STATE same commit (GA-R1/C1); DOCFIX-201's docs/audit
edits ride the same CURRENT-STATE touch (L10).
- D-136 PROPOSED [ARCH]: NetBox-coupled per-DC render pipeline; option (C)
recommended (values-file back half now for both DCs, NetBox front half for
Roosevelt); non-gating for dc1; prerequisite = extend sandbox-fidelity-check.py.
- Ruling 3 (GA-R5) recorded INLINE in D-136 with the verbatim question + utterance
("Your question about 1 or 2, go with 1. Base bundle and everything else is
applied via overlay") -- committee BLOCKER-1 fix (durable home, not the
delete-after-use pack file).
- CURRENT-STATE Stage-4: bullet (a) vault-VIP D-020 fix + per-DC widen, bullet (c)
artifact-shape RULED, coupling bullet -> PROPOSED as D-136, FOLLOW-UP gate
blockers (pre-flight-checks CHECK 1, provider-bundle-check vault VIP, VIP_OCTET_MAX).
- DOCFIX-201: dual-VIP decision is D-020 not D-036, SEVEN sites (7th caught by the
pack-review committee; verify -> 0 VIP-context D-036 remain).
- Chat correction (2026-07-25): D-136 open Q5 rewritten -- Jenkins IS the runner
(gap void); real requirements webhooks + status-back; placement OPEN.
repo-lint 0 fail. No cloud state mutated. D-136 PROPOSED -- operator rules the
mechanism (GA-R5).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|
Ruling 2 recorded (GA-R5): keystone policy-override VERIFY-LIVE gate
...
Operator ruling 2026-07-25. Question: add a VERIFY-LIVE gate that policyd-override
lands on every keystone unit after dc-ha-scaleup scales keystone to 3 (a broken
override is atomic -- whole policy discarded, silently reverting every tenant
domain-manager to a plain user). Utterance (verbatim): "dc-ha-scaleup: Yes to the
verify-live".
CURRENT-STATE Stage-4 VERIFY-LIVE gate added: juju status shows PO: on EVERY
keystone unit after scale-up, never PO (broken):. Protects D-051/D-064 (SCS Domain
Manager). Verbatim also in changelog item 13 (durable home). GA-R5: recorded before
the deploy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|
fix(lint): reword D-101 ruling-note header to clear L5 collision
...
5cf15b3 pushed with a repo-lint L5 FAIL (owned -- lint was not gated before the
commit). L5 counts `^##+ D-NNN` headers unless they contain AMENDMENT/RESOLVED;
the ruling note led with `### D-101 --` (a confirmation, not an amendment), so it
collided with the `## D-101:` definition. Reworded to `### RULING NOTE 2026-07-25
-- D-101 ...` so it no longer leads with the identifier. Content unchanged;
repo-lint 0 fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|

Ruling 1 recorded (GA-R5): address-family policy (D-101 confirmed, no amendment)
...
Operator ruling 2026-07-25. Question: v4-only vs dual-stack for dc1 phase-4.
Utterance (verbatim): "Dual stack to be used where IPv4 is required, IPv6 where
IPv6 only makes sense." Clarifying: "Dual stack deployment for DC0 and DC1. This
is the deployment when the dual-stack is added".
- docs/design-decisions.md: dated RULING NOTE under ## D-101 with the verbatim
question + utterance (confirms D-101's matrix; changes nothing).
- docs/CURRENT-STATE.md: resolve-before-deploy bullet (b) RESOLVED -- v4-only
phasing CLOSED for both DCs; VIP-overlay reconciliation now non-deferrable.
- changelog item 12.
The verbatim is transcribed into durable committed homes (D-101 note + changelog),
NOT left in the delete-after-use _handoff-not-committed/ pack (committee BLOCKER-1).
GA-R5: this ruling lands before the dependent VIP-reconciliation / deploy work.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUjEb7onHpdqFHUnio6iVw
|