Prepare immutable caller rollout and independent Scaleway recovery custody
Some checks failed
Transactional mail acceptance / mail (push) Failing after 32s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11fd9-caf6-7a63-b578-9daaaa134ff2
This commit is contained in:
tegwick 2026-10-09 19:49:24 +02:00
parent 3e18f11afe
commit 9a2cf213d1
19 changed files with 1543 additions and 4 deletions

View file

@ -5,6 +5,7 @@ from __future__ import annotations
import argparse
import json
import re
import secrets
import subprocess
import time
@ -13,6 +14,7 @@ from pathlib import Path
from ledger_archive import (
Refused,
checkpoint,
digest,
private_directory,
private_file,
validate_capture,
@ -89,7 +91,26 @@ print(json.dumps({'status':'isolated_ledger_verified','ledger_fingerprint':befor
"""
def rehearse(recovered, receipt_path):
def recovery_image(offline_binding=None):
if offline_binding is None:
return IMAGE
private_file(offline_binding)
binding = json.loads(offline_binding.read_text())
image_id = binding.get("image_id", "")
if (binding.get("schema") != "email-connect.ledger-recovery-kit.v1"
or binding.get("image") != IMAGE
or not isinstance(image_id, str)
or not re.fullmatch(r"sha256:[a-f0-9]{64}", image_id)):
raise Refused("offline_image_binding_required")
archive = offline_binding.parent / "receiver-image.tar"
private_file(archive)
expected = binding["files"]["receiver-image.tar"]
if archive.stat().st_size != expected["bytes"] or digest(archive) != expected["sha256"]:
raise Refused("offline_image_archive_changed")
return image_id
def rehearse(recovered, receipt_path, offline_binding=None):
private_directory(recovered)
private_directory(receipt_path.parent)
if receipt_path.exists() or receipt_path.is_symlink():
@ -104,8 +125,9 @@ def rehearse(recovered, receipt_path):
):
raise Refused("pinned_recovered_ledger_required")
# Never pull a recovery image from the production forge during this rehearsal.
selected_image = recovery_image(offline_binding)
inspected = subprocess.run(
["docker", "image", "inspect", IMAGE], capture_output=True, timeout=30
["docker", "image", "inspect", selected_image], capture_output=True, timeout=30
)
if inspected.returncode:
raise Refused("cached_pinned_recovery_image_required")
@ -119,6 +141,7 @@ def rehearse(recovered, receipt_path):
"docker",
"run",
"--rm",
"--pull=never",
"--name",
name,
"-i",
@ -145,7 +168,7 @@ def rehearse(recovered, receipt_path):
"PYTHONDONTWRITEBYTECODE=1",
"--entrypoint",
"python",
IMAGE,
selected_image,
"-c",
CODE,
],
@ -171,6 +194,7 @@ def rehearse(recovered, receipt_path):
uid=verification["uid"],
mail_sent=False,
evidence_ceiling="isolated_ledger_rehearsal",
offline_image_binding_verified=offline_binding is not None,
elapsed_seconds=round(time.monotonic() - started, 2),
)
checkpoint(receipt_path, receipt)
@ -182,6 +206,7 @@ def main():
parser.add_argument("--recovered", required=True, type=Path)
parser.add_argument("--receipt", required=True, type=Path)
parser.add_argument("--execute", action="store_true")
parser.add_argument("--offline-image-binding", type=Path)
args = parser.parse_args()
if not args.execute:
print(
@ -190,7 +215,7 @@ def main():
)
)
return 0
print(json.dumps(rehearse(args.recovered, args.receipt)))
print(json.dumps(rehearse(args.recovered, args.receipt, args.offline_image_binding)))
return 0