Implement SECRETS-WP-0008 unblocked layer-model obligations
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run

Load pep-stance.yaml as the live unreachable-engine gate and record named
stance fields on privileged evidence. Classify evidence, queue load-bearing
records in a local outbox, and add heartbeat/drain commands that never sit
on a mutation path. Publish proposed SSH-CA and secret-use evidence
contracts without adding an OpenBao SSH-CA write.

T02 (access-engine decision records) and T06 (no standing credential) stay
wait on external endpoints.

Assistant: grok
Assistant-Session: 01a04cea-cb33-7c63-bad7-c1b0f9f0076b
This commit is contained in:
tegwick 2026-08-29 12:52:55 +02:00
parent 57f6c4fa65
commit 3cd9955ac9
16 changed files with 1041 additions and 77 deletions

View file

@ -17,6 +17,7 @@ from pathlib import Path
from typing import Any
from secrets_engine.errors import DecisionError, SecretsEngineError
from secrets_engine.evidence_class import KIND_ATTRIBUTIVE, classify
from secrets_engine.redact import looks_secret, redact_text
# Keys that must never carry a value into evidence regardless of nesting.
@ -64,6 +65,21 @@ class EvidenceWriter:
with path.open("a", encoding="utf-8") as fh:
fh.write(json.dumps(record, sort_keys=True) + "\n")
def outbox_dir(self) -> Path:
path = self.evidence_dir / "outbox"
path.mkdir(parents=True, exist_ok=True)
return path
def _queue_outbox(self, record: dict[str, Any]) -> str:
"""Durably queue a load-bearing record. Never talks to audit-core."""
record_id = str(record.get("record_id") or uuid.uuid4())
path = self.outbox_dir() / f"{record_id}.json"
tmp = path.with_suffix(".tmp")
tmp.write_text(json.dumps(record, sort_keys=True) + "\n", encoding="utf-8")
os.chmod(tmp, 0o600)
tmp.replace(path)
return record_id
def record(
self,
action: str,
@ -75,9 +91,18 @@ class EvidenceWriter:
detail: dict[str, Any] | None = None,
hub: bool = True,
) -> dict[str, Any]:
"""Append one non-secret evidence record. Returns the stored record."""
"""Append one non-secret evidence record. Returns the stored record.
Load-bearing records are queued locally first. An audit-core outage
cannot occur here because this method never contacts audit-core.
Completeness is never claimed. Presence or absence of a record is
not consulted as a permission.
"""
record_id = str(uuid.uuid4())
hub_requested = bool(hub and self.hub_url)
evidence_class = classify(action, stage)
hub_requested = bool(
hub and self.hub_url and evidence_class.kind == KIND_ATTRIBUTIVE
)
record = {
"record_id": record_id,
"ts": datetime.now(timezone.utc).isoformat(),
@ -89,7 +114,13 @@ class EvidenceWriter:
"decision_id": decision_id,
"detail": _scrub(detail or {}),
"hub_delivery_requested": hub_requested,
"evidence_kind": evidence_class.kind,
"evidence_rule": evidence_class.rule_id,
"completeness_claimed": False,
}
if evidence_class.queued_locally:
self._queue_outbox(record)
record["outbox_queued"] = True
self._append_local(record)
if hub_requested:
delivery = self._post_hub(
@ -205,6 +236,7 @@ class PrivilegedActionEvidence:
decision_id: str = ""
approval_status: str = "pending"
completed: bool = False
stance: dict[str, Any] = field(default_factory=dict)
def __post_init__(self) -> None:
if not self.approval_required:
@ -218,6 +250,8 @@ class PrivilegedActionEvidence:
"decision_ref": self.decision_ref,
}
)
if self.stance:
merged.update(self.stance)
if extra:
merged.update(extra)
return merged
@ -239,6 +273,17 @@ class PrivilegedActionEvidence:
self.decision_id = str(getattr(decision, "id", ""))
self.approval_status = "approved"
def mark_stance(self, stance: object | None) -> None:
if stance is None:
return
if hasattr(stance, "as_evidence"):
payload = stance.as_evidence()
elif isinstance(stance, dict):
payload = stance
else:
return
self.stance = {key: value for key, value in payload.items() if value != ""}
def finish(
self, result: str, *, detail: dict[str, Any] | None = None
) -> dict[str, Any]:
@ -259,6 +304,8 @@ class PrivilegedActionEvidence:
return False
if isinstance(exc, DecisionError):
self.approval_status = "rejected"
if getattr(exc, "stance", None):
self.mark_stance(exc.stance)
if isinstance(exc, (KeyboardInterrupt, SystemExit)):
result = "interrupted"
elif isinstance(exc, SecretsEngineError):
@ -274,3 +321,76 @@ class PrivilegedActionEvidence:
detail=self._detail({"error_type": type(exc).__name__}),
)
return False
def write_heartbeat(writer: EvidenceWriter, *, stage: str = "prod") -> dict[str, Any]:
"""Positive claim that can go missing. Not a permission and not silence."""
queued = 0
outbox = writer.evidence_dir / "outbox"
if outbox.is_dir():
queued = sum(1 for path in outbox.glob("*.json") if path.is_file())
return writer.record(
"evidence-heartbeat",
result="nothing-to-report",
stage=stage,
detail={
"form": "heartbeat",
"outbox_depth": queued,
"completeness_claimed": False,
},
hub=False,
)
def drain_outbox(
writer: EvidenceWriter,
*,
audit_core_url: str = "",
) -> dict[str, Any]:
"""Best-effort delivery of queued load-bearing records.
Never called from a mutation path. An audit-core outage leaves files in
place and does not raise into a revoke/destroy handler.
"""
outbox = writer.evidence_dir / "outbox"
if not outbox.is_dir():
return {
"queued": 0,
"delivered": 0,
"failed": 0,
"skipped": 0,
"completeness_claimed": False,
}
files = sorted(path for path in outbox.glob("*.json") if path.is_file())
queued = len(files)
if not audit_core_url:
return {
"queued": queued,
"delivered": 0,
"failed": 0,
"skipped": queued,
"completeness_claimed": False,
}
delivered = 0
failed = 0
for path in files:
try:
payload = path.read_text(encoding="utf-8").encode()
req = urllib.request.Request(
audit_core_url.rstrip("/") + "/v1/events",
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req, timeout=3)
path.unlink()
delivered += 1
except (urllib.error.URLError, OSError, ValueError):
failed += 1
return {
"queued": queued,
"delivered": delivered,
"failed": failed,
"skipped": 0,
"completeness_claimed": False,
}