Round-10 review (railiance-platform) found that ContainedOperator inherited an ssh-only kubectl, so an E2 would write OpenBao and then fail its sync, restart and rollback. Also: a bad AUDIT_KUBECONFIG now leaves window-error evidence, and the receipt script validates bodies and lets a later request-changes withdraw an approval. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 236506@bnt-lap001 Assistant-Session: 09087018-91e1-48c5-ab2f-acfbb62c92e4
99 lines
4.5 KiB
Python
Executable file
99 lines
4.5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Write the RPF-WP-0015-T03 synthetic E2 approval receipt for one founder window.
|
|
|
|
Usage: scripts/rpf-wp-0015-e2-receipt.py OUTPUT.json [--start-in-min N]
|
|
|
|
The window is 15 minutes and starts now, or N minutes from now. Writing the
|
|
receipt and running the window is the founder GO for that window; D5
|
|
(2fd5849c) authorizes the exercise class only.
|
|
|
|
The owner acknowledgements are the owners' State Hub receipts for the
|
|
*current* review contract
|
|
(interfaces/RAILIANCE-WP-0024-owner-reviews.json). They are found by subject
|
|
`WP0024-OWNER-RECEIPT/v1/<owner>/<decision>/<contract digest>`, each body
|
|
validated by the review module. An owner's latest receipt must be `approve`.
|
|
The script refuses when any pinned artifact differs from the contract, or when
|
|
a required owner has no current approval for this digest. The receipt holds no
|
|
credential values, and the script never overwrites an existing file.
|
|
"""
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.parse
|
|
import urllib.request
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
OWNERS = ("audit-core", "rapp-postgres", "railiance-platform")
|
|
|
|
|
|
def review_module():
|
|
spec = importlib.util.spec_from_file_location("wp0024_review", ROOT / "scripts/wp0024-owner-review.py")
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def approvals(review, contract, api_base):
|
|
digest = review.contract_digest(contract)
|
|
query = urllib.parse.urlencode({"to_agent": "railiance-platform", "limit": 1000})
|
|
with urllib.request.urlopen(f"{api_base}/messages/?{query}", timeout=20) as response:
|
|
messages = json.load(response)
|
|
found = {}
|
|
for owner in OWNERS:
|
|
# Validate each body with the review module's own parser (subject, sender,
|
|
# schema). The latest receipt for this digest decides: a request-changes
|
|
# after an approval withdraws it.
|
|
receipts = []
|
|
for message in messages:
|
|
receipt = review.parse_receipt_message(message)
|
|
if receipt and receipt["owner"] == owner and receipt["contract_digest"] == digest:
|
|
receipts.append((receipt["created_at"], receipt["decision"], message["id"]))
|
|
if not receipts or max(receipts)[1] != "approve":
|
|
sys.exit(f"no current {owner} approval for contract {digest[:8]}; run the owner round first")
|
|
found[owner] = {"acknowledged": True, "message_id": max(receipts)[2]}
|
|
return digest, found
|
|
|
|
|
|
def main(argv):
|
|
if len(argv) < 2:
|
|
sys.exit(__doc__)
|
|
review = review_module()
|
|
contract = review.load_contract()
|
|
drift = [p for p, h in contract["artifact_sha256"].items() if review.file_sha256(ROOT / p) != h]
|
|
if drift:
|
|
sys.exit("reviewed artifacts differ from the owner-review contract: " + ", ".join(drift))
|
|
digest, owners = approvals(review, contract, os.environ.get("STATE_HUB_URL", "http://127.0.0.1:8000"))
|
|
out = argv[1]
|
|
delay = int(argv[argv.index("--start-in-min") + 1]) if "--start-in-min" in argv else 0
|
|
start = datetime.now(timezone.utc).replace(second=0, microsecond=0) + timedelta(minutes=delay)
|
|
end = start + timedelta(minutes=15)
|
|
tag = start.strftime("%Y%m%dt%H%M")
|
|
z = lambda t: t.strftime("%Y-%m-%dT%H:%M:%SZ") # noqa: E731
|
|
receipt = {
|
|
"procedure": "audit-core-database-lease-recovery",
|
|
"task_id": "RAILIANCE-WP-0024-T03",
|
|
"status": "approved",
|
|
"approval_id": f"rpf-wp-0015-t03-e2-{tag}z-founder-go",
|
|
"window": {"start": z(start), "end": z(end)},
|
|
"abort_operator": "founder (platform-root), attended",
|
|
"owners": owners,
|
|
"synthetic_load": {"contract_id": "RAILIANCE-WP-0024-T02-SYNTHETIC-01",
|
|
"driver_revision": "sha256:941ba251f638869626b06e9cbf430c70188c0bdf4715e3eff9c7610366f68662"},
|
|
"synthetic_sender": {"identity": f"t03-e2-synthetic-sender-{start:%Y%m%d}",
|
|
"source": f"t03-e2-synthetic-{start:%Y%m%d}",
|
|
"tenant": f"tenant:trial:t03-e2-synthetic-{start:%Y%m%d}",
|
|
"expires_at": z(end)},
|
|
}
|
|
with open(out, "x") as handle:
|
|
json.dump(receipt, handle, indent=2)
|
|
handle.write("\n")
|
|
print(json.dumps({"approval_id": receipt["approval_id"], "window": receipt["window"],
|
|
"contract_digest": digest,
|
|
"confirm": f"audit-core-synthetic-sender:{receipt['approval_id']}:attended"}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv)
|