RPF-WP-0015: repair recovery preflight for attest-chain Job pods and current audit-core digest

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 680842@bnt-lap001
Assistant-Session: 54eb9e8e-7d6b-4f0c-b7a6-a6db62075883
This commit is contained in:
codex 2026-10-14 11:12:16 +02:00
parent a86dfe2220
commit 63df822f6c
4 changed files with 49 additions and 4 deletions

View file

@ -141,6 +141,6 @@
"docs/openbao-reboot-snapshot-receipt.example.json": "b95903a3e1aacb607eecc3f0440f08e62480d09da47bd7ab96c3eb8814f2eaee",
"docs/railiance01-coordinated-reboot.md": "9969c2f498587710154e8d9a650fb8a13159e9e7b99918291dbdf577b131b7e7",
"scripts/audit-core-database-lease-recovery.py": "c87fb026c527fc17116067c5742d324340707ce4aecf440d0d4e618db9b872a1",
"scripts/audit-core-recovery-preflight.py": "802f73fa4352f64bc0972d813e3dd874f3ec2f428e162b23158c9d8d6fd8e710"
"scripts/audit-core-recovery-preflight.py": "248c663bad31860dd4f62870db00f6d624143d18c18f86634a39ceda12c7bc66"
}
}

View file

@ -21,7 +21,7 @@ from typing import Any
EXPECTED_IMAGE = (
"forgejo.coulomb.social/coulomb/audit-core@"
"sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6"
"sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615"
)
EXPECTED_DB_KEYS = {"username", "password", "host", "port", "dbname"}
@ -30,6 +30,18 @@ class PreflightError(RuntimeError):
pass
def serving_pods(pod_list: dict[str, Any]) -> list[dict[str, Any]]:
"""Deployment pods only: audit-core attest-chain Job pods share the app label."""
return [
pod
for pod in pod_list.get("items", [])
if not any(
owner.get("kind") == "Job"
for owner in pod.get("metadata", {}).get("ownerReferences", [])
)
]
class Remote:
def __init__(self, host: str) -> None:
self.host = host
@ -204,7 +216,7 @@ def common_state(remote: Remote, now: datetime) -> dict[str, Any]:
["-n", "audit-core", "get", "pods", "-l", "app.kubernetes.io/name=audit-core"],
label="read audit-core pod metadata",
)
pods = pod_list.get("items", [])
pods = serving_pods(pod_list)
if len(pods) != 1:
raise PreflightError(f"expected one audit-core pod, observed {len(pods)}")
pod = pods[0]

View file

@ -19,6 +19,26 @@ SPEC.loader.exec_module(module)
class RecoveryPreflightTests(unittest.TestCase):
def test_serving_pods_ignores_job_owned_attest_chain_pods(self) -> None:
def pod(name: str, kind: str) -> dict:
return {"metadata": {"name": name, "ownerReferences": [{"kind": kind}]}}
pods = {
"items": [
pod("audit-core-66767ddd58-x", "ReplicaSet"),
pod("audit-core-attest-chain-1", "Job"),
pod("audit-core-attest-chain-2", "Job"),
]
}
self.assertEqual(
["audit-core-66767ddd58-x"],
[item["metadata"]["name"] for item in module.serving_pods(pods)],
)
def test_serving_pods_keeps_two_deployment_pods_visible(self) -> None:
pods = {"items": [{"metadata": {"ownerReferences": [{"kind": "ReplicaSet"}]}}] * 2}
self.assertEqual(2, len(module.serving_pods(pods)))
def test_condition_selects_named_condition(self) -> None:
resource = {
"status": {

View file

@ -9,7 +9,7 @@ flavor: residual
owner: codex
topic_slug: railiance
created: "2026-08-22"
updated: "2026-10-03"
updated: "2026-10-14"
related:
- AUDIT-WP-0008
- WH-ENG-20260822-AUDIT-E2-01
@ -365,3 +365,16 @@ T02/T03 remain waiting on fresh attended execution windows, named abort operator
> critical path (identity → authz → secrets → SecurityCase execution). Tasks are unchanged;
> promote back when demand or risk appears. See the-custodian
> `history/20261011-netkingdom-security-progress-assessment.md`.
## Window preparation — 2026-10-14
The founder chose to prepare attended windows for T02 and T03 (D5 `2fd5849c` already
authorizes the outage exercise; each window still needs its own GO). The read-only
preflight had drifted: audit-core's attest-chain CronJob pods share the app label, so
the single-pod check failed, and the reviewed digest pin was stale. The preflight now
ignores Job-owned pods and pins audit-core's current deploy digest `ec15f63d…`
(audit-core `deploy/audit-core.yaml`, descendant of the `abd22fa` expiry prerequisite).
Both preflights then pass every automated check with no secret value observed; only
operator gates remain. The review contract's preflight hash is refreshed, so owner
receipts must be renewed against it. Unrelated pre-existing test failures (credential
change, factory audit delivery, informed_decision import) are unchanged.