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 RULING (2026-07-25): 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.


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.


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.