Newer
Older
openstack-caracal-dc-dc / creds-matrix-notes.md

creds-matrix-notes.md -- notes-ref targets for creds-matrix.tsv (D-137)

The notes-ref column of creds-matrix.tsv points here. Free text lives HERE and never in the TSV, so the TSV stays logical-keys-only (SEC-004) and greppable. Added by the 12-column amendment 2026-07-26 (docs/D-137-implementation-plan.md).

Parse contract. One ## <key> heading per note; the body is every line until the next heading. scripts/creds-matrix.py --render emits a note as the manifest comment above the rows that reference it -- this is how D-137 ruling 2's "prose must not be dropped" obligation is met for per-row governance text.

No secret values, no paths, no hostnames -- the same discipline as the TSV. Where a note must refer to a location it names the role token and the register row that holds the detail (docs/security-ledger.md remains the custody/exposure register of record).


n-maas-region-admin

The MAAS region admin superuser. Password 28 characters, alphanumeric, NO trailing newline -- that format spec is the verify-by-format basis; never verify by printing. The REGION copy is source-of-record and the jumphost copy is a byte-identical working copy (sha256-verified 2026-07-25). SEC-020 ruling: admin's password was deliberately NOT rotated, so any future rotation MUST update both copies in one operation or the region copy becomes a stale trap. This identity is also the automation identity, which is why it carries both a human and a service row -- see n-maas-cli-profile. That is the ruling-5 violation D-137 exists to make machine-detectable, not a data-entry error.

n-maas-admin-apikey

Deliberately NOT consolidated (SEC-020(i)): trivially regenerable, and a second copy is pure added surface. not-consolidated-ruled records that this is a settled decision, so --remote must not report it as an undeclared find.

n-maas-cli-profile

The installer logs the CLI in as profile admin; 19 maas admin ... call sites across 4 scripts hardcode that profile (SEC-020(b)). ROOT CAUSE of the SEC-020 miss: automation only ever needed the API key, so nobody ever needed the password, so nothing forced it into the folder. Remediation is the operator/admin split begun 2026-07-25 -- migrate those call sites to a service identity, which is what clears the ruling-5 FAIL.

n-maas-region-operator

Minted 2026-07-25 for HUMAN GUI login so a person no longer authenticates as the service identity. 32-char base64, generated on the jumphost, set via stdin -- never argv (createadmin --password would expose it in argv, the shape the installer uses). The first human-only MAAS identity.

n-region-infra-carveout

Region infrastructure, ruled OUT of the consolidation task's scope (SEC-020(i)). Present at a declared location by decision, not by oversight.

n-rack-enroll

Side effect of maas init region+rack; consumed by each DC rack at enrollment via a 0600 copy. Mint and consume are different stages -- minted at region standup, consumed at each DC standup.

n-no-mint-command

Research FINDING 1 -- reproducibility debt. ssh-keygen returns ZERO hits repo-wide; there is likewise no mkpasswd, htpasswd, pwgen, uuidgen, nor any Terraform random_password/tls_private_key. This credential's only provenance record is a manifest comment, so it is not reproducible: if the jumphost is rebuilt, no repo artifact states how to recreate it. That is a Roosevelt-transfer defect, not merely hygiene. mint-ref=operator-terminal rows are the debt to convert, and must be admitted by the parser rather than treated as errors.

n-edge-api

OPNsense REST API key+secret, minted ON the edge via the vendor model (D-113(a2)) into a 0600 temp file that is then wiped, and retrieved to the jumphost creds folder by the bootstrap driver.

n-third-party

Operator-supplied from a third-party control server. Correctly has NO repo mint command -- this is the one FINDING 1 row that is not a defect.

n-netbox-upstream

Minted on the upstream NetBox apex. SEC-006 records this credential as BURNED; the rotation obligation stands at v1 close.

n-netbox-sandbox

The founding SEC-009 miss. Minted on the NetBox VM and never pulled into the creds folder; it went unnoticed until the fidelity re-check needed it. That case is why creds-audit exists at all, and why the source-of-record vs consolidated distinction is modelled: a VM-minted secret has no forcing function to reach the folder.

n-netbox-vm-secrets

Held on the NetBox VM, outside the jumphost creds folder. netbox-secret-key was rotated off netbox-docker's PUBLIC default. The admin password is a human GUI login and is a separate identity from the service SECRET_KEY -- do not merge these ids.

n-tofu-apikey

Office1 MAAS API key consumed by OpenTofu as TF_VAR_maas_api_key.

n-tfstate-plaintext

DOCFIX-175: the same API key reaches terraform.tfstate in PLAINTEXT. A secret-at-rest location with no mint of its own -- it exists only as a second copy, which is exactly what the (credential, location) row shape is for. Any rotation of n-tofu-apikey must account for this copy.

n-office1-env

Environment file carrying the edge root-password hash and an SSH key path. One of the three files whose loose, 0664 placement in the home directory WAS SEC-009.

n-readme

NOT a credential -- the human index of the creds folder, declared in the manifest so the folder's undeclared-file check does not fire on it. principal is - deliberately; the ruling-5 invariant must skip rows with no principal rather than fault them.

n-edge-key

Edge service SSH keypair for the D-112(c) console bootstrap. The runbook describes the bootstrap step but does NOT contain the key generation itself -- see n-no-mint-command.

n-dc0-edge-api-absent

SEC-021, tier-1 detectable. dc1 declares an edge API credential and dc0 does not, so this expected row has no manifest row to satisfy it. This is an EXPECTED-BUT-ABSENT failure, not a matrix error: do not delete the row to make the checker green.

n-dc0-power-key-divergence

SEC-021, per-DC asymmetry. dc1's power key is consolidated on the jumphost under a DC-qualified name; dc0's equivalent exists under a DIFFERENT name and on a DIFFERENT host-role (the headend shadow store, SEC-022). Both the name and the location diverge from the per-DC pattern, so the expected jumphost rows fail EXPECTED-BUT-ABSENT while the divergent copy shows up at a declared remote location.

n-snap-key-fragility

The private half is installed into the region MAAS snap so MAAS's own power operations can dial the rack. The snap path is per-revision and may not survive a snap refresh -- it must be re-asserted after any refresh (a standing DC-standup item). Per-DC isolation is mandatory here: NEVER a cross-DC power key (SEC-012 dc0, SEC-016 dc1).

n-dc1-power-key

Dedicated dc1 MAAS->libvirt power key, minted under the SEC-016 ruling "Mint a dedicated dc1 power key" (per-DC isolation). Roosevelt analog: per-DC IPMI/BMC credentials.

n-dc-maas-apikey

Per-DC MAAS admin API key backing the Juju cloud credential. Generated FROM the jumphost so it never persisted on the region -- which is why its provenance is local rather than a VM path. Verified by FORMAT ONLY (parts=3, 70 chars); never printed, echoed, or placed in argv. GRANT is admin-scoped MAAS API. The owning MAAS superuser's password is random and unstored by ruling -- see n-juju-user-nopassword.

n-juju-cred-store

A runtime copy lives in the Juju client credential store on the HEADEND, a consequence of running the Juju client there per D-128. The jumphost file stays canonical. Recorded off-manifest in the manifests today (SEC-018/-019); this row is what makes it visible to the checker instead.

n-juju-user-nopassword

createadmin is invoked with NO --password, so the value is stored NOWHERE. Kept random and unstored by operator decision recorded in SEC-018/SEC-019 (2026-07-24): the API key is what is load-bearing, and the operator identity removes any need to log in as this user. filename is - because there is no host artifact -- existence checks must skip such rows rather than fail them.

n-headend-shadow-store

SEC-022 -- a scope gap in the control itself, not a one-off miss. An entire parallel *-creds/ store exists on the headend holding real key material. creds-audit.sh:29-31 resolves the folder against $HOME on whatever host it runs on, and the script contains no ssh/scp at all, so these stores are structurally invisible to every check that exists today. The D-128 operating model puts Plane-2 execution on the headend, so headend-resident credentials are EXPECTED to keep appearing -- this is why ruling 3's declared-locations list must include the headend, and why adding a location row is DoD for any new mint site.

n-outside-convention

Research FINDING 2. A credential directory outside the SEC-009 *-creds/ convention and outside creds-audit entirely -- the audit checks the per-site folders plus a loose FILE glob in the home directory, and never inspects these DIRECTORIES. Named in CLAUDE.md as a secret location, so it is KNOWN; it is simply outside the control. Materially extends SEC-023. The Vault directory holds the cloud's entire root of trust and its mint is an irreversible one-shot.

n-overlay-in-clone

Contains base64 CA cert/key blobs PLUS the issuing-CA passphrase in PLAINTEXT, written inside the repo clone and gitignored. Given SEC-004 (this repo is currently PUBLIC), that gitignore is the only thing standing between a CA private key and publication.

n-tenant-dir

Per-tenant credential directory outside the *-creds/ convention (research FINDING 2). Hard tenant isolation applies: D-069 keeps custody off-repo, and D-137 ruling 3 bounds --remote to declared locations precisely so no tenant surface is ever walked. The runbook tenant-onboarding-v2-DRAFT.md documents the SAME credential set as the onboard script -- two creation locations, ONE set; do not double-count them into extra rows.

n-charm-minted

Charm-minted at bundle apply with no repo mint site, then MATERIALIZED to a file by the operator. In matrix scope per the operator-ruled 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) -- a generation-based test would have missed it entirely.

n-predicted-openrc

PREDICTED, not present (SEC-023). The script that writes it runs at a stage this deployment has not reached, so the checker must report this row as not-yet-expected via an explicit skip with an [ok], never as a silent pass. Both readings of the acceptance test apply: this row exercises the not-yet-expected path, and the harness separately proves the widened sprawl glob CATCHES the file once it appears.

n-cloud-only

Lives in the cloud or on a unit rather than in a host file, but is consumed out-of-band (captured by an operator command at a documented step), so the materialization test puts it in scope. filename may be -; existence checks skip such rows.

n-ephemeral-unit

Written to a 0600 temp file ON the unit during a stage step. In scope because it is written to a file, but it is short-lived by design -- absence outside its step is normal, so this row must never drive an existence failure.

n-libvirt-power-pw

The libvirt SSH password for MAAS virsh power control. Read INTERACTIVELY (never a CLI arg, never echoed, never logged, never in the repo) and injected into MAAS power_pass. reenroll-hosts.sh:22-25 records that it was exposed in plaintext on 2026-06-26 -- maas machine power-parameters echoes power_pass -- with a standing instruction to ROTATE it. It is in scope by the materialization test (presented by a human, consumed out-of-band) and had NO row until the 2026-07-26 committee found it: a credential with a live rotation obligation that the credential-lifecycle authority could not see.


Checker behaviour worth knowing before you "fix" it

S5 compares mint-stage and mint-ref as part of the per-DC shape, deliberately. Today both DCs agree on both, so it does not fire on them. A future DC that legitimately mints the same credential at a DIFFERENT stage -- one standing up after Stage 5, say -- would trip S5 on a non-defect. That strictness is INTENDED: a per-DC credential appearing at a different stage or from a different mint site is exactly the drift the register exists to surface, and it should be argued about rather than absorbed. If a future DC makes it genuinely wrong, narrow it with a recorded reason -- do not silently drop the two fields from the comparison.

S3 is a partial gate on ruling 2, not a full one. It compares parsed row fields for rows present on both sides; it does not compare manifest header prose, and --render emits only the consolidated jumphost rows. Byte-equality becomes enforceable once the manifests are actually replaced by rendered output, which is operator-gated because it changes what creds-audit demands at three sites.

n-colocated-nonsecret

NOT a credential -- a config or known-hosts artifact that happens to sit in a declared credential location. Declared so the undeclared-file check stays meaningful instead of crying wolf on every config file next to a key. principal is -, so ruling 5 skips it.


Deliberate EXCLUSIONS (recorded so the decision is not silent)

These credentials appear in the research capture but are NOT matrix rows, because they fail the materialization test -- never written to a file, never presented to a human, never consumed out-of-band:

  • Per-service DB / RabbitMQ / Ceph keyring credentials -- charm relation data (the capture confirms 56 apps, zero hardcoded secret options in the bundle).
  • Vault-issued per-service TLS certificates -- they cascade from the charm-pki-local root; the verify script checks them, nothing mints them.
  • The charm-pki-local root CA itself -- created inside Vault and never materialized.
  • The short-lived (10-minute TTL) child Vault token and the charm authorization token -- printed once, deliberately so the root token never reaches juju logs.
  • CAPI/CAPO per-cluster CAs and bootstrap tokens, and per-cluster trustee users/trusts -- in-cluster Kubernetes Secrets and cloud-side objects, minted at every cluster create.
  • Throwaway acceptance-run credentials -- created and deleted within the same run.
  • Tenant end-user passwords and client-minted CI application credentials -- these live on CLIENT infrastructure, outside this cloud's custody entirely.

If any of these later becomes materialized to a file, it acquires a row at that moment -- adding one is DoD for the change that materializes it.