Read-only research capture for the D-137 credential-matrix design. No credential file was opened; every row below is a file:line reference to CODE or RUNBOOK PROSE that mints, materializes, or uploads credential material.
Classification used: MINT (new secret material comes into existence), MATERIALIZE (an existing secret is fetched/copied/written somewhere new), UPLOAD (public key registered -- no secret created). Only MINT rows are creation points; the other two classes were checked and excluded deliberately.
Operator-ruled scope for the matrix (this session): the materialization test -- in-matrix if a credential is ever written to a file, presented by a human, or consumed out-of-band, regardless of who generated it.
| Class | Count | Where |
|---|---|---|
| Scripted MINT (repo code generates the secret) | 25 rows | scripts/ |
| Runbook/prose MINT (operator runs by hand, no script owns it) | 30 rows | runbooks/, clientdocs/ |
| Declared secrets with NO creation point anywhere in the repo | 12 | see below |
| Charm/platform-minted at bundle apply | summarised | bundle.yaml deploy |
| MATERIALIZE / UPLOAD (excluded, verified non-creating) | 20+ | see source agent record |
This is the most consequential result and it qualifies the "creation points are already known" premise: for twelve standing credentials, they are not.
grep -rnI "ssh-keygen" . returns ZERO hits repo-wide (searched everything except .git/.terraform/tfstate/tfplan, including tests/, docs/archive/, clientdocs/, .claude/). There is likewise no mkpasswd, htpasswd, password_hash, pwgen, uuidgen, nor any Terraform random_password / tls_private_key. Yet the manifests declare:
| Credential | Declared at | Only provenance record |
|---|---|---|
office1_svc_ed25519{,.pub} |
vr1-office1.manifest:30-31 |
vr1-office1-as-built.md:126 (SEC-007) |
vr1-dc0_svc_ed25519{,.pub} |
vr1-dc0.manifest:16-17 |
a manifest COMMENT (:10-12) |
vr1-dc1_svc_ed25519{,.pub} |
vr1-dc1.manifest:16-17 |
a manifest COMMENT (:10-12) |
vr1-dc0-edge_ed25519{,.pub} |
vr1-dc0.manifest:22-23 |
manifest comment (backfilled 2026-07-25) |
vr1-dc1-edge_ed25519{,.pub} |
vr1-dc1.manifest:21-22 |
manifest comment (:18-20) |
vr1-dc1-maas-power_ed25519{,.pub} |
vr1-dc1.manifest:29-30 |
manifest comment (:26-28, SEC-016) |
opnsense-root-password |
vr1-office1.manifest:34 |
vr1-office1-as-built.md:128, SEC-007 -- HUMAN GUI login |
opnsense-root-hash |
vr1-office1.manifest:33 |
same (bcrypt of the above, for config.xml) |
tailscale-authkey.txt |
vr1-office1.manifest:36 |
SEC-008 -- operator-supplied, third-party control server (correctly has no repo mint) |
vr1-netbox.env |
vr1-office1.manifest:37 |
minted on the upstream apex; SEC-006 (BURNED) |
vr1-office1.env / vr1-stage1.env |
.manifest:39-40 |
vr1-office1-as-built.md:132-133 |
Six SSH keypairs and the OPNsense root password/hash are pure operator-terminal mints whose only record is a manifest comment. Consequence: they are not reproducible. If the jumphost is rebuilt, no repo artifact states how to recreate them -- which is a Roosevelt-transfer defect, not merely a hygiene one. The tailscale key is genuinely third-party and correctly has no repo mint.
*-creds/ conventionSEC-009 says all site secrets live in ~/<site>-creds/. The inventory shows at least three other jumphost directories holding credential material, none of which creds-audit covers (it checks ~/<site>-creds/ folders plus a loose-FILE sprawl glob in $HOME -- it never inspects these DIRECTORIES):
~/vault-init/init.txt -- Vault 5 unseal shares + root token, the cloud's entire root of trust (runbooks/phase-02-vault-bringup.md:71, irreversible one-shot).~/octavia-pki/ -- 8 artifacts: 2 CA passphrases, 2 CA private keys, 2 CA certs, the controller key, the signed bundle (runbooks/phase-01-bundle-deploy.md:299-410).~/tenant-<client>/ -- per-tenant domain-admin / cluster / svc passwords, an application credential, and a Nova private key (scripts/tenant-onboard.sh:70-71, 94-95, 141, 167).All three are named in CLAUDE.md as secret locations, so they are KNOWN -- they are simply outside the control. This materially extends SEC-023.
Related: runbooks/phase-01-bundle-deploy.md:391-410 writes overlays/octavia-pki.yaml containing base64 CA key blobs plus the issuing-CA passphrase in plaintext, inside the repo clone (gitignored). Given SEC-004 (repo currently PUBLIC), that gitignore is the only thing standing between a CA private key and publication.
scripts/site-headend-install.sh -- 7 mints (:418 primitive, :430 DB pass, :445 rack-enrollment secret, :450/:452 admin password, :453 API key, :485 LXD trust). Every Office1/DC-region MAAS + LXD secret originates here.scripts/tenant-onboard.sh -- 7 mints, the entire tenant identity set.runbooks/phase-01-bundle-deploy.md:299-410 -- 8 Octavia PKI artifacts.runbooks/tenant-onboarding-v2-DRAFT.md:162-320 -- 6 mints DUPLICATING #2 (two creation locations, one credential set -- do not double-count).runbooks/phase-02-vault-bringup.md:71-144 -- 4 mints incl. the root of trust.runbooks/dc-dc-phase4-juju-bundle-per-dc.md:119-151 -- 4 mints PER DC.dc-dc-phase4-juju-bundle-per-dc.md:128 mints juju-<dc> via maas createadmin with no --password, so MAAS prompts and the value is stored nowhere -- independently confirming the SEC-020 finding about those two superusers.site-headend-install.sh:452 uses --password in argv, the exposure SEC-020 flagged and which this session's operator mint avoided via stdin.scripts/phase-03-admin-openrc.sh:46-52 is classified MATERIALIZE (charm-generated, operator-materialized) -- exactly the case the ruled materialization test captures and a generation-based test would have missed (SEC-023).The matrix must carry a mint-ref column that can hold three provenance kinds, not one: script:line, runbook:step, and operator-terminal (undocumented). Finding 1 means a meaningful fraction of today's estate is the third kind, so a checker that requires every row to name executable code would fail 12 rows on day one. Those rows are the backlog the matrix exists to surface -- they should be admitted as operator-terminal and tracked as a debt to convert, not treated as parse errors.
Source: three read-only Explore agents, 2026-07-25. The full per-row tables ARE NOW IN THIS FILE (see APPENDIX below), transcribed 2026-07-26 before the originating session was cleared -- they are the creds-matrix.tsv seed and must not depend on a transcript.
Transcribed into the repo 2026-07-26 because the body above pointed at a session transcript that was about to be cleared. These rows ARE the creds-matrix.tsv seed; they are what docs/D-137-implementation-plan.md step 3 tells the build to read. Classification: MINT = new secret material; MATERIALIZE/UPLOAD excluded (section E).
| # | file:line | Credential | For | Written to | Stage/step | Principal | ||
|---|---|---|---|---|---|---|---|---|
| A1 | site-headend-install.sh:416-419 |
newpass() primitive: `openssl rand -base64 30 \ |
tr -dc A-Za-z0-9 \ | head -c 28`, 0600 | voffice1 | (helper) | Stage 2 | -- |
| A2 | site-headend-install.sh:430 (:151 SECRETS=/root/maas-secrets) |
PostgreSQL maas role password |
region DB | voffice1:/root/maas-secrets/db.pass REMOTE |
Stage 2 step 3 | service | ||
| A3 | site-headend-install.sh:450,452 |
MAAS superuser admin password (--password in ARGV) |
region | voffice1:/root/maas-secrets/admin.pass REMOTE; copy consolidated 2026-07-25 |
Stage 2 step 4 | BOTH (conflated) | ||
| A4 | site-headend-install.sh:453 |
MAAS API key for admin |
region | voffice1:/root/maas-secrets/admin.apikey REMOTE, deliberately not consolidated |
Stage 2 step 4 | service | ||
| A5 | site-headend-install.sh:455 |
MAAS CLI profile admin (~/.maas.cli) |
region | region VM home | Stage 2 step 4 | service | ||
| A6 | site-headend-install.sh:445-447 (doc :25-29) |
MAAS rack-enrollment secret (side effect of maas init region+rack) |
region -> DC racks | voffice1:/var/snap/maas/common/maas/secret; 0600 copy to rack via --enroll-secret-file |
Stage 2 mint / Stage 3 consume | service | ||
| A7 | site-headend-install.sh:485-486 |
LXD trust password | voffice1 LXD | voffice1:/root/maas-secrets/lxd-trust.pass REMOTE |
Stage 2 step 6 | service | ||
| A8 | opnsense-mint-apikey.php:43 (write :52-60) |
OPNsense REST API key+secret (vendor model) | any edge | <edge>:/tmp/opnsense-apikey.$$.txt 0600, then wiped |
D-113(a2) | service | ||
| A9 | opnsense-bootstrap-apikey.sh:89-90,114-120 (cleanup :93) |
driver for A8; retrieves key to jumphost | edge -> jumphost | ~/vr1-<site>-creds/opnsense-api.txt 0600 |
Stage 3 replacement chain step 2 | service | ||
| A10 | tenant-onboard.sh:39 newpw() |
secrets.token_urlsafe(24) primitive |
Keystone | (helper) | tenant onboard | -- | ||
| A11 | tenant-onboard.sh:64-65 (write :70-71) |
<client>-domain-admin password |
tenant domain | ~/tenant-<client>/<client>-domain-admin-cred.txt 0600 |
onboard stage1 | human | ||
| A12 | tenant-onboard.sh:90-91 (write :94-95) |
<client>-cluster + <client>-svc passwords |
tenant project | ~/tenant-<client>/*-cred.txt 0600 |
onboard stage2 | cluster=human-ish, svc=service | ||
| A13 | tenant-onboard.sh:141 |
Keystone application credential <client>-svc-cred |
Keystone | ~/tenant-<client>/<client>-svc-appcred.txt 0600 |
onboard stage3 (re-run guard :125-139) |
service | ||
| A14 | tenant-onboard.sh:167 |
Nova keypair PRIVATE key <client>-key |
Nova | ~/tenant-<client>/<client>-key.pem 0600 |
onboard stage3 (guard :154-166) |
service | ||
| A15 | dc-dc-rbd-mirror.sh:88,156 |
Ceph rbd-mirror pool peer bootstrap token | primary-DC Ceph | /tmp/rbd-mirror-bootstrap-token.<rand> 0600 ON THE UNIT |
Stage 6 step 11.x | service | ||
| A16 | dc-dc-radosgw-multisite.sh:172,178 |
RGW multisite system user access-key+secret (radosgw-generated at realm create) | radosgw DC1 | stays in the RGW realm on the unit | Stage 6 step 4 | service | ||
| A17 | phase-06-capi-stack.sh:91 (def :78) |
cluster-admin kubeconfig (sudo k8s config) |
capi-mgmt VM | <mgmt VM>:~/.kube/config 0600 REMOTE |
phase-06 step 6.6a | service | ||
| A18 | phase-06-capi-stack.sh:147-153 |
CAPI/CAPO stack: per-cluster CA + bootstrap tokens at every cluster create | capi-mgmt | in-cluster k8s Secrets | phase-06 step 6.6d | service | ||
| A19 | clientdocs/scripts/acceptance-run.sh:349 |
throwaway Nova keypair ci-accept-key |
tenant | client host; deleted :129 |
acceptance D2 | service (ephemeral) | ||
| A20 | clientdocs/scripts/acceptance-run.sh:464 |
Barbican secret ci-accept-secret |
Barbican | Barbican; deleted :470 |
acceptance G1 | service | ||
| A21-A25 | tenant-onboarding-v2-DRAFT.md:162,225-226,290,295,308-312,319-320 |
SAME credential set as A11-A14 -- the DRAFT runbook duplicates tenant-onboard.sh. Two creation LOCATIONS, one set. Do not double-count. |
Keystone/Nova | $HOME/<client>-* |
onboard-v2 | mixed |
| # | file:line | Credential | Written to | Stage/step | Principal |
|---|---|---|---|---|---|
| B1 | phase-02-vault-bringup.md:71 |
Vault operator init: 5 unseal shares + root token. IRREVERSIBLE one-shot (:14-15,63-64) |
the Vault init dir on the jumphost -- OUTSIDE any *-creds/ |
phase-02 step 2.1 | service (root of trust) |
| B2 | phase-02-vault-bringup.md:127-130 |
short-lived child Vault token (-ttl=10m), so root never hits juju logs |
printed once | phase-02 step 2.3 | service |
| B3 | phase-02-vault-bringup.md:142 |
charm-vault credential (authorize-charm token=) |
inside the charm | phase-02 step 2.3, DOCFIX-011 | service |
| B4 | phase-02-vault-bringup.md:144 |
charm-pki-local root CA (private key + cert) -- the whole cloud's TLS root | inside Vault | phase-02 step 2.3, DOCFIX-014 | service |
| B5-B12 | phase-01-bundle-deploy.md:299-386 |
8 Octavia PKI artifacts: issuing-CA passphrase (:299-301), issuing-CA key EC P-384 AES-256 (:302-304), issuing-CA cert (:305-307), controller-CA passphrase (:321-323), controller-CA key (:324-326), controller-CA cert (:327-329), controller key EC P-256 UNENCRYPTED (:343-344), signed controller.bundle.pem (:375-386) |
the Octavia PKI dir on the jumphost -- OUTSIDE any *-creds/ |
phase-01 step 1.0-GEN.a/b/c | service |
| B13 | phase-01-bundle-deploy.md:391-410 |
overlays/octavia-pki.yaml -- base64 CA cert/key blobs + the issuing-CA passphrase in PLAINTEXT |
INSIDE the repo clone (gitignored) | phase-01 step 1.0-GEN.d | service |
| B14 | dc-dc-phase4-juju-bundle-per-dc.md:128 |
MAAS superuser juju-<dc> -- createadmin with NO --password, so stored nowhere |
nowhere | Stage 5 step 2.0 | BOTH (no custody) |
| B15 | dc-dc-phase4-juju-bundle-per-dc.md:136-139 |
per-DC MAAS API key (apikey --generate, run FROM the jumphost) |
~/vr1-<dc>-creds/maas-api-key.txt 0600 (SEC-018/019) |
Stage 5 step 2.0 | service |
| B16 | dc-dc-phase4-juju-bundle-per-dc.md:141-142 |
Juju cloud credential wrapping B15 | Juju client store on voffice1 -- explicitly OFF-MANIFEST | Stage 5 step 2.0 | service |
| B17 | dc-dc-phase4-juju-bundle-per-dc.md:151 |
juju bootstrap: controller admin credential + controller CA/agent secrets, per DC (D-104) |
controller + ~/.local/share/juju/ |
Stage 5 step 2 | service |
| B18 | dc-dc-phase1-office1-standup.md:464-471 |
Office1 MAAS API key for OpenTofu (TF_VAR_maas_api_key); DOCFIX-175 warns it lands in terraform.tfstate PLAINTEXT |
~/vr1-office1-creds/vr1-stage1.env 0600 |
Stage 2 step 5 | service |
| B19 | dc-dc-phase1-office1-standup.md:643-647 |
NetBox API token | office1-netbox:/root/netbox-secrets/api.token REMOTE; copy in vr1-netbox-sandbox.env (the founding SEC-009 miss) |
Stage 2 step 9 | service |
| B20 | implied vr1-office1-as-built.md:130, prose :631-637 |
NetBox SECRET_KEY (rotated off netbox-docker's PUBLIC default) + NetBox admin password |
office1-netbox:/root/netbox-secrets/ REMOTE |
Stage 2 step 9 | SECRET_KEY=service, admin=human |
| B21 | dc-dc-phase2-tofu-dc-substrate.md:371-395 (D-112(c)) |
edge service SSH keypair + root access via serial-console payload | ~/vr1-dc<N>-creds/vr1-dc<N>-edge_ed25519 |
Stage 3 replacement chain step 1 | service |
| B22 | dc-dc-phase2-tofu-dc-substrate.md:770-778 (SEC-012/-016) |
per-DC MAAS->libvirt power keypair, never cross-DC | private half in voffice1:/var/snap/maas/current/root/.ssh/ (per-snap-revision, re-assert after refresh); dc1 copy on jumphost, dc0 has none (SEC-021) |
Stage 3 DoD item 5 | service |
| B23 | dc-dc-phase5-dr-failover-drill.md:230,261-263 |
RGW multisite system key ("never invent it"; capture via radosgw-admin user info) |
remote RGW realm | Stage 6 step 4 | service |
| B24 | dc-dc-phase5-dr-failover-drill.md:294,411 |
rbd-mirror peer bootstrap token (manual form of A15) | remote unit | Stage 6 step 11.5 | service |
| B25 | phase-07-conductor-graft.md:89 / phase-07-conductor-graft.sh:90 |
Keystone magnum domain + magnum_domain_admin user + password (D-046, re-run after every redeploy) |
Magnum charm config | phase-07 step 7.0 | service |
| B26 | appendix-D-magnum-trust-model.md:18-25,77; tenant-acceptance.sh:54; phase-08-*.md:35,179 |
per-cluster trustee user + Keystone trust + CAPO child app credential, minted at EVERY cluster create (D-039/D-046) | cloud only, no host file | phase-08 / every create | service |
| B27 | changelog-20260725-maas-admin-recovery.md:24-29 |
MAAS superuser operator -- 32-char base64, stdin never argv |
~/vr1-office1-creds/maas-operator-password 0600 |
ad-hoc 2026-07-25 (SEC-020c) | human (first human-only MAAS identity) |
| B28 | clientdocs/self-service-guide.md:51-52; tenant-skill/references/day2-operations.md:147-148 |
tenant end-user Keystone passwords (--password-prompt), client-run |
client host | tenant day-2 | human |
| B29 | clientdocs/ci-automation.md:63-70; ci-integration-guide.md:102-109 |
client-minted app credential pipeline-nightly |
client CI secret store | tenant CI | service |
| B30 | dc-dc-phase3-maas-enlist-deploy.md:491-497 |
not a mint -- the standing instruction enumerating what a DC standup mints and requiring immediate consolidation + a manifest row | ~/vr1-<dc>-creds/ |
Stage 4 close-out | -- |
admin password -- charm-minted; MATERIALIZED by phase-03-admin-openrc.sh:52 via juju run keystone/leader get-admin-password + extract_admin_password.py. In matrix scope per the materialization ruling.bundle.yaml).phase-04-internal-cert-san-verify.sh verifies, does not mint.phase-03-admin-openrc.sh:46-52,66-80 (writes ~/admin-openrc, SEC-023); extract_admin_password.py; phase-06-kubeconfig-gate.sh:36,59-77; phase-07-conductor-graft.sh:121-129 (base64 to the conductor unit, sha256-verified); phase-06-net-setup.sh:23,48 and phase-06-incloud-mgmt-cluster.md:206 (UPLOAD of an existing pubkey); opentofu/main.tf:218-219,444-447,575-578 (cloud-init ssh_authorized_keys via file()); vr1-dc{0,1}-substrate/main.tf:21,18 (qemu+ssh?keyfile= -- path only, D-126 boundary); opnsense-api.sh:32,75 (consume); reenroll-hosts.sh:122-123 (read -rsp a libvirt password into MAAS power params -- rotation warning :22-25); vault-kv-inner-probe.sh:12-13,22 (consume); site-ssh-config.sh / site-forward.sh / site-baseleg.sh (explicit no-key-material boundaries); netbox/*.py (consume NETBOX_TOKEN from env only); creds-audit.sh (metadata only); .claude/hooks/guard-destructive.py:28-42; snapshot_scrub.py:10.
Searched beyond scripts/+runbooks/: bundle.yaml, overlays/, clientdocs/ (incl. scripts/ and tenant-skill/), policies/, skills/, .claude/, creds-manifests/, _handoff-not-committed/. tests/ EXCLUDED as fixtures (tests/tenant-onboard/run-tests.sh:21,86 mints a fake CA/PEM; test_logic.py uses NETBOX_TOKEN="fake-token") -- no real credential originates there. NEVER opened, and excluded from content-printing greps: opentofu/terraform.tfstate*, the 8 *.tfplan, logs/, asbuilt/, docs/audit/, netbox/draft/*.json. opentofu/README.md + variables.tf:6-10 confirm maas_api_key reaches tfstate in plaintext (DOCFIX-175) -- a secret-at-rest location with no mint of its own.