diff --git a/REUSE.md b/REUSE.md index 479daf2..5bb7acc 100644 --- a/REUSE.md +++ b/REUSE.md @@ -132,7 +132,7 @@ | Install-profile gate (shell) | scripts/felhom-host-install.sh `--mode appliance\|byo` (GL-2, v1.10.0) | Mandatory-flag profile (no default), refusals at argv time BEFORE any prompt/step, risky step gated at its CALL SITE (one auditable place — never a branch inside the step), mode persisted to state.json + resume-mismatch refusal, `FELHOM_INSTALL_STATE_DIR` override for harness isolation. Harness: scripts/hostinstall-mode-harness.sh (static refusal matrix + grep-invariants + PVE dry-transcript tier; red-proofs run against a mutated scratch copy). | | Disclosure↔uninstall parity (shell) | scripts/felhom-host-install.sh `_uninstall_statement` + harness GL4-D (v1.11.0) | Every host artifact the byo disclosure names must be removed OR explicitly listed KEPT by `run_uninstall`; the harness greps the parity (token list). New install-time artifact ⇒ add its removal + disclosure line + parity token in the SAME commit. Drive data rule: plain `umount` only, never `-l`/`-f`, never any format op under /mnt/felhom-drives. | | Website deploy (manifest) | manifests/webpage.yaml | git-sync sidecar (sparse-checkout `/website/` + `/scripts/`, `--link=current`) + init container waits for first sync; nginx serves `current/website`; push to main = deployed, no image build. | -| Secret handling (manifest) | manifests/hub.yaml (env, ~L142) | Secrets via `secretKeyRef` to OUT-OF-BAND secrets created per documentation/runbooks/secrets.md — never inline stringData (see §3). `report-api` (the operator bearer, v0.53.0) is deliberately NOT `optional:` — a missing Secret fails Ready instead of booting an unauthenticatable hub. `scripts/manifest_secret_gate.py` (run after ANY manifests/ change) blocks bearer-shaped (64-hex) literals. ERRATA (2026-07-03): `gitea-creds` is COMMITTED in manifests/felhom.secret.yaml AND live-consumed by hub.yaml — rotation + de-git is a pending operator task (spike SPIKE-a1 appendix). | +| Secret handling (manifest) | manifests/hub.yaml (env, ~L142) | Secrets via `secretKeyRef` to OUT-OF-BAND secrets created per documentation/runbooks/secrets.md — never inline stringData (see §3). `report-api` (the operator bearer, v0.53.0) is deliberately NOT `optional:` — a missing Secret fails Ready instead of booting an unauthenticatable hub. `scripts/manifest_bearer_gate.py` (run after ANY manifests/ change) blocks bearer-shaped (64-hex) literals. ERRATA (2026-07-03): `gitea-creds` is COMMITTED in manifests/felhom.secret.yaml AND live-consumed by hub.yaml — rotation + de-git is a pending operator task (spike SPIKE-a1 appendix). | | Hub deploy (GitOps) | manifests/hub.yaml `image:` (~L129) | Pinned explicit tag, bumped in git, deliberate ArgoCD sync (auto-sync OFF). Code push alone deploys nothing. | ## 3. Dangerous lookalikes — do NOT reuse diff --git a/documentation/pilot/RUNBOOK-publish-0.81-0.113-2026-07-11.md b/documentation/pilot/RUNBOOK-publish-0.81-0.113-2026-07-11.md index 3e45387..027a413 100644 --- a/documentation/pilot/RUNBOOK-publish-0.81-0.113-2026-07-11.md +++ b/documentation/pilot/RUNBOOK-publish-0.81-0.113-2026-07-11.md @@ -116,7 +116,7 @@ rollout OK, live env verified 0.113.0, clean hub startup log. Demo: no churn (al exposed in a session screenshot during Phase D. Rotate it + move to a `secretKeyRef` (the Resend-key pattern, documentation/runbooks/secrets.md) as the next operator hygiene task. **DISPOSITION 2026-07-13 (hub v0.53.0):** de-git DONE (`secretKeyRef: report-api/REPORT_API_KEY` - + `scripts/manifest_secret_gate.py`); the value rotation is the supervised procedure in + + `scripts/manifest_bearer_gate.py`); the value rotation is the supervised procedure in documentation/runbooks/secrets.md §"Operator/global bearer key". ## END STATE diff --git a/documentation/pilot/RUNBOOK-publish-0.85-0.120-2026-07-12.md b/documentation/pilot/RUNBOOK-publish-0.85-0.120-2026-07-12.md index 1706474..7f4f5c8 100644 --- a/documentation/pilot/RUNBOOK-publish-0.85-0.120-2026-07-12.md +++ b/documentation/pilot/RUNBOOK-publish-0.85-0.120-2026-07-12.md @@ -96,7 +96,7 @@ floor lands his controller at 0.120 automatically. carried over from the 0.81/0.113 train. Rotate + move to a `secretKeyRef` (Resend-key pattern, documentation/runbooks/secrets.md) as an operator hygiene task.~~ **DISPOSITION 2026-07-13 (hub v0.53.0):** moved to `secretKeyRef: report-api/REPORT_API_KEY` - (literal removed from the manifest; `scripts/manifest_secret_gate.py` blocks reintroduction). + (literal removed from the manifest; `scripts/manifest_bearer_gate.py` blocks reintroduction). The value ROTATION itself is the supervised step in documentation/runbooks/secrets.md §"Operator/global bearer key" — the git-history copy is dead only after it runs. diff --git a/documentation/runbooks/secrets.md b/documentation/runbooks/secrets.md index a9dd3ba..b4ed59a 100644 --- a/documentation/runbooks/secrets.md +++ b/documentation/runbooks/secrets.md @@ -73,7 +73,7 @@ The hub API's global bearer (`api.report_api_key`) — the operator's own key (e … -hub-key`), distinct from the per-customer/per-host keys the hub generates itself. It was COMMITTED in `manifests/hub.yaml` until v0.53.0 (flagged in the 0.81/0.113 and 0.85/0.120 publish runbooks, incl. a Phase-D screenshot exposure); the manifest now carries a -`secretKeyRef` and `scripts/manifest_secret_gate.py` blocks reintroduction. **The git-history +`secretKeyRef` and `scripts/manifest_bearer_gate.py` blocks reintroduction. **The git-history copy stays alive until the value is ROTATED** — de-git alone kills nothing. **What uses it (live consumers of the GLOBAL key):** diff --git a/scripts/manifest_bearer_gate.py b/scripts/manifest_bearer_gate.py new file mode 100644 index 0000000..067796d --- /dev/null +++ b/scripts/manifest_bearer_gate.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +"""Manifest bearer-literal gate (v0.53.0, part of the hub bearer de-git) — no bearer-shaped +literal (64 hex chars, the `openssl rand -hex 32` shape every felhom bearer/API key uses) may +appear ANYWHERE in manifests/, comments included. Secrets ride out-of-band `kubectl create +secret` + secretKeyRef (documentation/runbooks/secrets.md); the manifests carry only +placeholders. The other known committed secrets in felhom.secret.yaml (passwords, non-hex +shapes) are a tracked backlog item (secrets.md) and are NOT matched by this gate — extend the +patterns when they are de-gitted. + +Run from the repo root: python scripts/manifest_bearer_gate.py +Exit 1 on any hit. + +(Named "bearer", not "secret": the repo .gitignore's `*secret*` pattern — which guards real +secret files — would silently un-track a gate with "secret" in its filename.) +""" +import io, os, re, sys + +ROOT = "manifests" + +# 64 hex chars with no hex/word neighbors (so longer blobs and sha256-of-file hexes embedded in +# longer strings still match at 64+, but ordinary short ids never do). +BEARER = re.compile(r"(?