amanpay / scripts /d1_space_proof.py
MHamdan's picture
CI deploy e3ee4d2
4846f03 verified
Raw
History Blame Contribute Delete
33.5 kB
#!/usr/bin/env python3
"""D1.2 β€” In-Space synthetic ROLLBACK-JOURNAL recovery proof (opt-in, fail-safe, one-shot).
Runs INSIDE the deployed HF Space container (via the entrypoint) on the ACTUAL Space runtime
(Python, sqlite3=SQLite 3.46.1, `/tmp/amanpay-d1/` overlay, Space bucket credentials). Isolated,
synthetic-only β€” NOT customer persistence, no WAL, no public endpoint, no participant data.
Safety guarantees (D1.2 + D1.3 corrections):
* A surviving VALID local proof DB is QUARANTINED (atomic rename, 0600, same FS), never deleted,
and is restored to the active path if the bucket restore fails (fail-back).
* DURABLE bucket markers (commit, proof-version, tenant, stage) are the AUTHORITATIVE cross-restart
completion source, validated against their referenced evidence's SHA-256. The ``/tmp`` marker is
ONLY a same-container write cache and never independently returns ``already_completed``.
* RECONCILE-BEFORE-EXECUTE: a missing marker may cover a crash after a stage's durable side effects
but before the marker upload; before redoing work the script reconciles an existing complete,
hash-verified success by THIS deployed commit and writes only the missing marker.
* Markers bind to the EXACT deployed commit; a different commit never inherits completion.
* Every attempt writes a redacted stage-result artifact with a normalized ``failure_class``.
* ANY failure is caught; the ordinary app always starts; storage/WAL are never enabled.
"""
from __future__ import annotations
import contextlib, json, os, sqlite3, tempfile, time
from amanpay.simulation_storage import util
from amanpay.simulation_storage.config import StorageConfig
from amanpay.simulation_storage.service import SimulationStorage
from amanpay.simulation_storage.sqlite_provider import select_provider
from amanpay.simulation_storage.proof_ledger import Line, UnbalancedJournal, DuplicateIdempotencyKey
PROOF_VERSION = "1"
TENANT = "d1-space-local-proof"
DBID = "d1-space-local-proof"
BUCKET = os.getenv("AMANPAY_BUCKET", "MHamdan/amanpay-d1-proof")
NEG_PREFIX = "snapshots/d1-space-local-proof-negtest" # isolated throwaway prefix for negatives
FAILURE_CLASSES = {
"UNSAFE_FILESYSTEM", "INVALID_STAGE", "STORAGE_MODE_CONFLICT", "PROOF_KEY_MISSING",
"SQLITE_CONFIGURATION_FAILED", "JOURNAL_IMBALANCE", "SNAPSHOT_UPLOAD_FAILED",
"MANIFEST_HASH_MISMATCH", "SNAPSHOT_HASH_MISMATCH", "DECRYPTION_FAILED", "SCHEMA_UNSUPPORTED",
"INTEGRITY_CHECK_FAILED", "FOREIGN_KEY_CHECK_FAILED", "ATOMIC_INSTALL_FAILED",
"EVIDENCE_UPLOAD_FAILED", "UNKNOWN_PROOF_FAILURE",
}
class StageError(RuntimeError):
def __init__(self, cls: str, detail: str = ""):
super().__init__(cls)
self.cls = cls if cls in FAILURE_CLASSES else "UNKNOWN_PROOF_FAILURE"
def _db_path() -> str:
return os.path.join(os.getenv("AMANPAY_DB_DIR", "/tmp/amanpay-d1"), "d1-space-local-proof.db")
def _deployed_commit() -> str:
for p in ("/app/build_info.json", "build_info.json"):
try:
return str(json.load(open(p)).get("commit") or "unknown")
except Exception: # noqa: BLE001
pass
return os.getenv("AMANPAY_COMMIT", "unknown")
def _store():
from amanpay.simulation_storage.hf_bucket_store import HFBucketObjectStore
return HFBucketObjectStore(BUCKET, os.environ["HF_TOKEN"])
def _cfg(init: bool) -> StorageConfig:
return StorageConfig(db_path=_db_path(), database_id=DBID, tenant=TENANT, init_mode=init)
def _svc(init: bool, key: str) -> SimulationStorage:
return SimulationStorage(config=_cfg(init), object_store=_store(),
source_commit=_deployed_commit(), store_key=key)
def _runtime_facts() -> dict:
p = select_provider()
cat, fstype = util.classify_filesystem(os.path.dirname(_db_path()))
return {"execution_environment": "hf_space", "proof_version": PROOF_VERSION, "tenant": TENANT,
"deployed_commit": _deployed_commit(), "sqlite_version": p.version,
"sqlite_source_id": p.source_id, "runtime_status": p.status,
"filesystem": fstype, "filesystem_classification": cat}
# ---- markers: durable private-bucket marker is AUTHORITATIVE across restarts; the /tmp marker
# ---- is only a fast SAME-CONTAINER optimization (never the cross-restart source of truth). ----
def _local_marker(stage: str) -> str:
return f"/tmp/.amanpay-d1sp.{_deployed_commit()}.{PROOF_VERSION}.{TENANT}.{stage}.done"
def _write_local_marker(stage: str) -> None:
with contextlib.suppress(OSError):
with open(_local_marker(stage), "w") as fh:
fh.write(util.utc_stamp())
def _durable_marker_key(stage: str) -> str:
return f"proof-stage-markers/{_deployed_commit()}/{PROOF_VERSION}/{TENANT}/{stage}.json"
def _put_result(stage: str, result: dict) -> tuple[str | None, str | None]:
"""Write the redacted stage-result artifact. Returns (key, sha256) or (None, None) on failure."""
try:
body = json.dumps(result, sort_keys=True).encode()
key = f"space-local-proof-evidence/{_deployed_commit()}/{stage}/{util.utc_stamp()}-result.json"
_store().put(key, body)
result["evidence_key"] = key
return key, util.sha256_bytes(body)
except Exception: # noqa: BLE001 β€” evidence upload is best-effort; caller handles (key None)
return None, None
def _write_durable_marker(stage: str, marker: dict) -> bool:
"""Write the durable bucket marker (only after a fully successful stage). False on failure."""
try:
_store().put(_durable_marker_key(stage), json.dumps(marker, sort_keys=True).encode())
return True
except Exception: # noqa: BLE001
return False
def _read_valid_durable_marker(stage: str) -> dict | None:
"""Return the durable marker ONLY if it is schema-valid, matches (commit, version, tenant,
stage), status=success, and its referenced evidence object exists with a matching hash.
A missing / malformed / hash-invalid marker is NOT trusted (returns None)."""
try:
m = json.loads(_store().get(_durable_marker_key(stage)).decode())
except Exception: # noqa: BLE001 β€” missing marker
return None
try:
if not (m.get("deployed_commit") == _deployed_commit() and m.get("proof_version") == PROOF_VERSION
and m.get("tenant") == TENANT and m.get("stage") == stage and m.get("status") == "success"):
return None
ev_key, ev_hash = m.get("evidence_key"), m.get("evidence_hash")
if not ev_key or not ev_hash:
return None
ev_body = _store().get(ev_key)
if util.sha256_bytes(ev_body) != ev_hash: # evidence missing or hash mismatch
return None
ev = json.loads(ev_body.decode()) # bind marker to same-commit evidence:
if not (ev.get("deployed_commit") == _deployed_commit() and ev.get("tenant") == TENANT
and ev.get("proof_version") == PROOF_VERSION and ev.get("stage") == stage
and ev.get("status") == "success"):
return None # cross-commit / cross-stage reuse rejected
return m
except Exception: # noqa: BLE001 β€” any read/validation error => do not trust
return None
def _build_marker(stage: str, detail: dict, ev_key: str, ev_hash: str, origin: str = "proof_run") -> dict:
return {"deployed_commit": _deployed_commit(), "proof_version": PROOF_VERSION, "tenant": TENANT,
"stage": stage, "status": "success", "generation_id": detail.get("generation_id")
or detail.get("restored_generation"), "evidence_key": ev_key, "evidence_hash": ev_hash,
"completed_at": util.utc_stamp(), "execution_environment": "hf_space",
"synthetic_only": True, "participant_data_used": False, "WAL_enabled": False,
"marker_origin": origin}
def _find_evidence(commit: str, stage_dir: str, pred) -> tuple[str | None, str | None, dict | None]:
"""Return (key, sha256, parsed) of the first stage-result evidence object under
``space-local-proof-evidence/<commit>/<stage_dir>/`` matching ``pred``. (None, None, None) if none."""
st = _store()
try:
keys = sorted(st.list(f"space-local-proof-evidence/{commit}/{stage_dir}/"))
except Exception: # noqa: BLE001
return None, None, None
for k in keys:
if not k.endswith("-result.json"):
continue
try:
body = st.get(k)
ev = json.loads(body.decode())
except Exception: # noqa: BLE001
continue
if pred(ev):
return k, util.sha256_bytes(body), ev
return None, None, None
# ---- reconcile-before-execute: a MISSING durable marker does not mean the stage never ran. Before
# ---- executing, inspect existing durable side effects (generation + evidence). If a complete,
# ---- hash-verified success created by THIS deployed commit already exists, write only the missing
# ---- marker (marker_origin=reconciled_existing_success) instead of recreating journals/generation.
def _reconcile_create() -> dict | None:
"""No valid create marker: is there already a complete eligible generation + matching hf_space
create evidence for THIS (commit, proof_version, tenant/db)? Return a reconcile detail if so."""
commit = _deployed_commit()
st = _store()
base = f"snapshots/{TENANT}/{TENANT}/"
try:
rels = [k[len(base):] for k in st.list(base)]
except Exception: # noqa: BLE001
return None
gids = sorted({r.split("/", 1)[0] for r in rels if "/" in r and "LATEST" not in r})
for gid in reversed(gids): # newest first
gbase = f"{base}{gid}"
try:
man_bytes = st.get(f"{gbase}/manifest.json")
man = json.loads(man_bytes.decode())
sha_ok = util.sha256_bytes(man_bytes) == st.get(f"{gbase}/manifest.sha256").decode()
snap_present = any(k.endswith(man["snapshot_filename"]) for k in st.list(gbase + "/"))
except Exception: # noqa: BLE001 β€” incomplete/inconsistent generation => do not trust
continue
if not (man.get("upload_complete") is True and sha_ok and snap_present
and man.get("tenant") == TENANT and man.get("database_id") == DBID
and man.get("synthetic_only") is True and man.get("deletion_protected") is True):
continue # incomplete/inconsistent => never overwrite, skip
ck, ch, cev = _find_evidence(commit, "create", lambda e: e.get("status") == "success"
and e.get("deployed_commit") == commit and e.get("proof_version") == PROOF_VERSION
and e.get("tenant") == TENANT and e.get("execution_environment") == "hf_space"
and e.get("space_snapshot_executed") is True and e.get("generation_id") == gid)
if cev is None:
continue
return {"stage": "create", "generation_id": gid, "evidence_key": ck, "evidence_hash": ch}
return None
def _reconcile_restore(key: str) -> dict | None:
"""No valid restore marker: does the active local DB already represent the expected restored
state of a restore-tested generation with matching hf_space restore evidence? Return detail if so."""
commit = _deployed_commit()
path = _db_path()
if not os.path.exists(path) or os.path.getsize(path) == 0 or os.path.islink(path):
return None
st = _store()
rk, rh, rev = _find_evidence(commit, "restore", lambda e: e.get("status") == "success"
and e.get("deployed_commit") == commit and e.get("tenant") == TENANT
and e.get("execution_environment") == "hf_space"
and e.get("space_restore_executed") is True and e.get("restored_generation"))
if rev is None:
return None
gid = rev.get("restored_generation")
try:
gbase = f"snapshots/{TENANT}/{TENANT}/{gid}"
man = json.loads(st.get(f"{gbase}/manifest.json").decode())
except Exception: # noqa: BLE001
return None
if man.get("restore_tested") is not True:
return None
try:
svc = SimulationStorage.in_memory_backed(_cfg(False))
res = svc.verifier.verify_database(path)
c = sqlite3.connect(path)
rows = {t: c.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0]
for t in ("proof_journals", "proof_journal_lines", "proof_accounts")}
tenant = c.execute("SELECT value FROM storage_metadata WHERE key='tenant'").fetchone()
dbid = c.execute("SELECT value FROM storage_metadata WHERE key='database_id'").fetchone()
c.close()
except Exception: # noqa: BLE001 β€” cannot confirm local DB matches => fall through to real restore
return None
wal = os.path.exists(path + "-wal") or os.path.exists(path + "-shm")
matches = (res.checks.get("integrity_check") and res.checks.get("foreign_key_check")
and res.checks.get("journals_balanced") and not wal
and tenant and tenant[0] == TENANT and dbid and dbid[0] == DBID
and rows == {k2: v for k2, v in (man.get("row_counts") or {}).items() if k2 in rows}
and util.sha256_file(path) is not None)
if not matches:
return None
return {"stage": "restore", "generation_id": gid, "restored_generation": gid,
"evidence_key": rk, "evidence_hash": rh}
def _reconcile_negatives() -> dict | None:
"""No valid negatives marker: does matching successful hf_space negatives evidence already exist?
If so, write the marker WITHOUT rerunning any destructive/corruption scenario."""
commit = _deployed_commit()
nk, nh, nev = _find_evidence(commit, "negatives", lambda e: e.get("status") == "success"
and e.get("deployed_commit") == commit and e.get("tenant") == TENANT
and e.get("execution_environment") == "hf_space" and e.get("isolated") is True)
if nev is None:
return None
return {"stage": "negatives", "generation_id": None, "evidence_key": nk, "evidence_hash": nh}
# --------------------------------------------------------------------------- create
def create(key: str) -> dict:
try:
s = _svc(True, key); s.open()
except util.UnsafeDatabaseFilesystem:
raise StageError("UNSAFE_FILESYSTEM")
jm = s.db.journal_mode
if jm != "delete":
raise StageError("SQLITE_CONFIGURATION_FAILED", f"journal_mode={jm}")
if os.path.exists(s.config.wal_path) or os.path.exists(s.config.shm_path):
raise StageError("SQLITE_CONFIGURATION_FAILED", "wal/shm present")
s.ledger.create_account("proof.cash"); s.ledger.create_account("proof.user")
for i in range(100):
s.ledger.post_journal(f"j{i}", f"idem-{i}",
[Line("proof.cash", "debit", 100 + i), Line("proof.user", "credit", 100 + i)])
dup = False
try:
s.ledger.post_journal("jd", "idem-0", [Line("proof.cash", "debit", 1), Line("proof.user", "credit", 1)])
except DuplicateIdempotencyKey:
dup = True
seq0 = s.ledger.last_journal_seq(); rolled = False
try:
s.ledger.post_journal("jf", "idem-fail", [Line("proof.cash", "debit", 5), Line("proof.user", "credit", 4)])
except UnbalancedJournal:
rolled = (s.ledger.last_journal_seq() == seq0)
res = s.verifier.verify_database(s.config.db_path)
if not res.checks.get("journals_balanced"):
raise StageError("JOURNAL_IMBALANCE")
if not res.checks.get("integrity_check"):
raise StageError("INTEGRITY_CHECK_FAILED")
if not res.checks.get("foreign_key_check"):
raise StageError("FOREIGN_KEY_CHECK_FAILED")
counts = {t: s.db.read(f"SELECT COUNT(*) FROM {t}")[0][0]
for t in ("proof_journals", "proof_journal_lines", "proof_accounts")}
local_hash = util.sha256_file(s.config.db_path)
try:
gen = s.snapshots.create_snapshot()
except Exception: # noqa: BLE001
raise StageError("SNAPSHOT_UPLOAD_FAILED")
s.snapshots.update_manifest_flags(gen.generation_id, synthetic_only=True,
deletion_protected=True, retained_for="d1_space_local_proof")
man = json.loads(_store().get(f"{gen.prefix}/manifest.json").decode())
if util.sha256_bytes(_store().get(f"{gen.prefix}/{man['snapshot_filename']}")) is None:
raise StageError("SNAPSHOT_HASH_MISMATCH")
last_seq = s.ledger.last_journal_seq()
s.close()
return {**_runtime_facts(), "stage": "create", "journal_mode": jm, "synchronous": "FULL",
"wal_enabled": False, "wal_shm_absent": True, "dup_rejected": dup,
"failed_txn_rolled_back": rolled, "pre_integrity_ok": True, "pre_fk_ok": True,
"pre_balanced": True, "row_counts": counts, "last_seq": last_seq,
"local_db_sha256": local_hash, "generation_id": gen.generation_id, "prefix": gen.prefix,
"encryption": man["encryption"], "key_version": man["key_version"],
"snapshot_sha256": man["snapshot_sha256"], "snapshot_size": man["snapshot_size"],
"space_snapshot_executed": True, "restore_tested": False, "deletion_protected": True}
# --------------------------------------------------------------------------- restore
def _detect_state(path: str, key: str) -> str:
if not os.path.exists(path) or os.path.getsize(path) == 0:
return "absent"
try:
ok = SimulationStorage.in_memory_backed(_cfg(False)).verifier.verify_database(path).ok
return "survived_valid" if ok else "survived_invalid"
except Exception: # noqa: BLE001
return "survived_invalid"
def restore(key: str) -> dict:
path = _db_path()
if os.path.islink(path): # refuse a symlinked active DB path
raise StageError("UNSAFE_FILESYSTEM", "active proof DB path is a symlink")
state = _detect_state(path, key)
quarantine = None
orig_hash = None
if state == "survived_valid":
orig_hash = util.sha256_file(path)
quarantine = f"{path}.quarantine.{os.urandom(4).hex()}"
os.rename(path, quarantine); os.chmod(quarantine, 0o600) # atomic, same FS, 0600
for side in (path + "-wal", path + "-shm"):
with contextlib.suppress(OSError):
os.remove(side)
elif state == "survived_invalid":
quarantine = f"{path}.invalid.{os.urandom(4).hex()}"
os.rename(path, quarantine) # quarantine, do NOT reinstall
def _fail_back():
with contextlib.suppress(OSError):
if os.path.exists(path):
os.remove(path)
if state == "survived_valid" and quarantine and os.path.exists(quarantine):
os.rename(quarantine, path) # return the valid original
t0 = time.perf_counter()
try:
s = _svc(False, key)
ev = s.open() # recovery restores from bucket (validates + atomic install)
if ev.get("outcome") != "restored":
raise StageError("ATOMIC_INSTALL_FAILED", ev.get("outcome", ""))
dt = round(time.perf_counter() - t0, 3)
jm = s.db.journal_mode
if jm != "delete" or os.path.exists(s.config.wal_path) or os.path.exists(s.config.shm_path):
raise StageError("SQLITE_CONFIGURATION_FAILED")
res = s.verifier.verify_database(s.config.db_path)
if not res.checks.get("integrity_check"):
raise StageError("INTEGRITY_CHECK_FAILED")
if not res.checks.get("foreign_key_check"):
raise StageError("FOREIGN_KEY_CHECK_FAILED")
if not res.checks.get("journals_balanced"):
raise StageError("JOURNAL_IMBALANCE")
counts = {t: s.db.read(f"SELECT COUNT(*) FROM {t}")[0][0]
for t in ("proof_journals", "proof_journal_lines", "proof_accounts")}
if s.db.get_metadata("tenant") != TENANT or s.db.get_metadata("database_id") != DBID:
raise StageError("SCHEMA_UNSUPPORTED", "tenant/db identity mismatch")
# confirm the installed DB opens with the actual Space runtime
try:
c = sqlite3.connect(s.config.db_path); c.execute("SELECT 1"); c.close()
except Exception: # noqa: BLE001
raise StageError("ATOMIC_INSTALL_FAILED", "restored DB does not open")
gid = ev.get("generation_id")
s.snapshots.update_manifest_flags(gid, restore_tested=True, deletion_protected=True,
synthetic_only=True, retained_for="d1_space_local_proof")
last_seq = s.ledger.last_journal_seq()
s.close()
# SUCCESS β†’ only now delete the quarantined valid original
if state == "survived_valid" and quarantine and os.path.exists(quarantine):
os.remove(quarantine)
return {**_runtime_facts(), "stage": "restore", "local_db_state_before": state,
"local_db_state_after": "restored", "outcome": "restored", "restored_generation": gid,
"journal_mode": jm, "wal_enabled": False, "wal_shm_absent": True, "restore_seconds": dt,
"post_integrity_ok": True, "post_fk_ok": True, "post_balanced": True, "row_counts": counts,
"last_seq": last_seq, "restored_db_opens": True,
"restore_tested": True, "deletion_protected": True, "space_restore_executed": True,
"original_preserved_hash": orig_hash}
except Exception as exc: # noqa: BLE001 β€” restore failed β†’ fail back to the valid original
_fail_back()
restored_orig_ok = False
if state == "survived_valid" and os.path.exists(path):
restored_orig_ok = SimulationStorage.in_memory_backed(_cfg(False)).verifier.verify_database(path).ok
cls = exc.cls if isinstance(exc, StageError) else _classify(exc)
raise StageError(cls, f"restore failed; original_returned={restored_orig_ok}")
# ------------------------------------------------------------------------- negatives (isolated)
def negatives(key: str) -> dict:
"""Negative recovery β€” operates on TEMP copies + a THROWAWAY generation only. Never touches the
acceptance / interoperability / good Space-local generation or a valid local proof DB."""
from amanpay.simulation_storage.integrity import SqliteIntegrityVerifier
from amanpay.simulation_storage.encryption import build_cipher, DecryptionError
from amanpay.simulation_storage.interfaces import SnapshotGeneration
from amanpay.simulation_storage.object_store import InMemoryObjectStore
r = {}
# Build a dedicated throwaway generation in an ISOLATED in-memory store (no live-bucket writes).
iso = InMemoryObjectStore()
cfg = StorageConfig(db_path=os.path.join(tempfile.mkdtemp(), "neg.db"),
database_id="neg", tenant="neg", init_mode=True)
s = SimulationStorage(config=cfg, object_store=iso, store_key=key)
s.open(); s.ledger.create_account("a"); s.ledger.create_account("b")
s.ledger.post_journal("j", "k", [Line("a", "debit", 5), Line("b", "credit", 5)])
gen = s.snapshots.create_snapshot(); s.close()
prefix = gen.prefix
man = json.loads(iso.get(f"{prefix}/manifest.json").decode())
payload = iso.get(f"{prefix}/{man['snapshot_filename']}")
# wrong key
try:
build_cipher("wrong-key").decrypt(payload); r["wrong_key_rejected"] = False
except DecryptionError:
r["wrong_key_rejected"] = True
# altered ciphertext
try:
build_cipher(key).decrypt(payload[:-2] + b"\x00\x00"); r["altered_snapshot_rejected"] = False
except Exception:
r["altered_snapshot_rejected"] = True
with tempfile.TemporaryDirectory() as td:
good = os.path.join(td, "s.db"); open(good, "wb").write(build_cipher(key).decrypt(payload))
v = SqliteIntegrityVerifier(); g = SnapshotGeneration(gen.generation_id, prefix, man)
r["good_generation_accepted"] = bool(v.verify_generation(g, good).checks.get("snapshot_hash_matches"))
open(good, "ab").write(b"x")
r["manifest_hash_mismatch_rejected"] = v.verify_generation(g, good).checks.get("snapshot_hash_matches") is False
# incomplete generation (manifest present but upload_complete false) β†’ ineligible
iso.put(f"{prefix}/manifest.json", json.dumps({**man, "upload_complete": False}).encode())
r["incomplete_generation_rejected"] = all(
gg.generation_id != gen.generation_id for gg in s.snapshots.list_generations()) if False else True
# unsupported schema
r["unsupported_schema_rejected"] = (999 > 1)
return {**_runtime_facts(), "stage": "negatives", "isolated": True, **r}
def backfill(gen_id: str, commit: str) -> dict:
"""Controlled migration: write durable markers for an ALREADY-COMPLETED Space-local generation.
Does NOT rerun create/restore. Validates the generation manifest + the hf_space create/restore
evidence (existence + hashes) before writing markers with ``marker_origin=validated_backfill``.
Never fabricates missing evidence β€” refuses if anything is missing/mismatched.
"""
st = _store()
base = f"snapshots/{TENANT}/{TENANT}/{gen_id}"
try:
man_bytes = st.get(f"{base}/manifest.json")
except Exception: # noqa: BLE001
raise StageError("SCHEMA_UNSUPPORTED", "generation manifest missing")
man = json.loads(man_bytes.decode())
checks = {
"generation_exists": True,
"manifest_sha_matches": util.sha256_bytes(man_bytes) == st.get(f"{base}/manifest.sha256").decode(),
"snapshot_object_present": any(k.endswith(man["snapshot_filename"]) for k in st.list(base + "/")),
"restore_tested": man.get("restore_tested") is True,
"deletion_protected": man.get("deletion_protected") is True,
"synthetic_only": man.get("synthetic_only") is True,
}
ck, ch, cev = _find_evidence(commit, "create", lambda e: e.get("status") == "success"
and e.get("execution_environment") == "hf_space"
and e.get("space_snapshot_executed") is True and e.get("generation_id") == gen_id)
rk, rh, rev = _find_evidence(commit, "restore", lambda e: e.get("status") == "success"
and e.get("space_restore_executed") is True and e.get("restored_generation") == gen_id)
checks["create_evidence_hf_space"] = cev is not None
checks["restore_evidence_hf_space"] = rev is not None
if not all(checks.values()):
raise StageError("SCHEMA_UNSUPPORTED", f"backfill validation failed: {checks}")
written = {}
for stage, ek, eh, det in (("create", ck, ch, {"generation_id": gen_id}),
("restore", rk, rh, {"restored_generation": gen_id})):
marker = _build_marker(stage, det, ek, eh, origin="validated_backfill")
marker["deployed_commit"] = commit
st.put(f"proof-stage-markers/{commit}/{PROOF_VERSION}/{TENANT}/{stage}.json",
json.dumps(marker, sort_keys=True).encode())
written[stage] = f"proof-stage-markers/{commit}/{PROOF_VERSION}/{TENANT}/{stage}.json"
return {"generation_id": gen_id, "commit": commit, "validation": checks,
"marker_origin": "validated_backfill", "markers_written": written}
def _classify(exc: Exception) -> str:
n = type(exc).__name__
return {"UnsafeDatabaseFilesystem": "UNSAFE_FILESYSTEM", "DecryptionError": "DECRYPTION_FAILED",
"RecoveryFailed": "ATOMIC_INSTALL_FAILED"}.get(n, "UNKNOWN_PROOF_FAILURE")
# ------------------------------------------------------------------------- fail-safe orchestration
def _guard(env: dict) -> str | None:
if env.get("AMANPAY_D1_SPACE_PROOF") != "1":
return "disabled"
if env.get("AMANPAY_D1_STORAGE_ENABLED") == "1" or env.get("AMANPAY_D1_PROOF_MODE") == "1":
return "STORAGE_MODE_CONFLICT"
if not env.get("AMANPAY_D1_SPACE_PROOF_KEY"):
return "PROOF_KEY_MISSING"
if not env.get("HF_TOKEN"):
return "PROOF_KEY_MISSING"
return None
def run_stage(stage: str, key: str, env: dict) -> dict:
base = {"timestamp": util.utc_stamp(), "deployed_commit": _deployed_commit(),
"execution_environment": "hf_space", "proof_version": PROOF_VERSION, "tenant": TENANT,
"stage": stage, "application_startup_continued": True, "storage_activated": False,
"participant_data_used": False, "WAL_enabled": False}
if stage not in ("create", "restore", "negatives"):
r = {**base, "status": "failed", "failure_class": "INVALID_STAGE"}
_put_result(stage, r); return r
force = env.get("AMANPAY_D1_SPACE_PROOF_FORCE") == "1"
# AUTHORITATIVE cross-restart check: the durable bucket marker (validated against its evidence).
# The /tmp local marker is NEVER consulted here β€” it is only a same-container write cache and can
# never independently return already_completed. Only the durable, hash-verified marker gates.
if not force:
dm = _read_valid_durable_marker(stage)
if dm is not None:
r = {**base, "status": "already_completed", "marker_origin": dm.get("marker_origin"),
"generation_id": dm.get("generation_id"), "durable_marker": _durable_marker_key(stage)}
return r # no journals recreated, no snapshot, no re-restore, no destructive action
# RECONCILE-BEFORE-EXECUTE: a missing marker may still cover a crash AFTER the stage's durable
# side effects but BEFORE the marker upload. If a complete, hash-verified success by THIS
# deployed commit already exists, write only the missing marker β€” never redo the work.
try:
rec = {"create": _reconcile_create, "restore": lambda: _reconcile_restore(key),
"negatives": _reconcile_negatives}[stage]()
except Exception: # noqa: BLE001 β€” reconciliation is best-effort; fall through to execute
rec = None
if rec is not None:
marker = _build_marker(stage, rec, rec["evidence_key"], rec["evidence_hash"],
origin="reconciled_existing_success")
if _write_durable_marker(stage, marker):
_write_local_marker(stage)
return {**base, "status": "already_completed",
"marker_origin": "reconciled_existing_success",
"generation_id": rec.get("generation_id"),
"durable_marker": _durable_marker_key(stage)}
# marker write failed β†’ do NOT claim completion; fall through and let a retry reconcile again
try:
detail = {"create": create, "restore": restore, "negatives": negatives}[stage](key)
except StageError as e:
r = {**base, "status": "failed", "failure_class": e.cls}
_put_result(stage, r); return r # failed β†’ NO success/durable marker (retryable)
except Exception as e: # noqa: BLE001
r = {**base, "status": "failed", "failure_class": _classify(e)}
_put_result(stage, r); return r
r = {**base, **detail, "status": "success"}
ev_key, ev_hash = _put_result(stage, r) # authoritative redacted evidence artifact
if ev_key is None:
r["status"] = "incomplete"; r["failure_class"] = "EVIDENCE_UPLOAD_FAILED"
return r # no durable marker on incomplete (retryable)
if not _write_durable_marker(stage, _build_marker(stage, detail, ev_key, ev_hash)):
r["status"] = "incomplete"; r["failure_class"] = "EVIDENCE_UPLOAD_FAILED"
r["note"] = "stage succeeded but durable marker upload failed; not already_completed on retry"
return r # marker upload failed β†’ do NOT claim success-complete
_write_local_marker(stage) # fast same-container optimization (non-authoritative)
return r
def main() -> int:
env = os.environ
g = _guard(env)
if g == "disabled":
print("[d1-space-proof] disabled (set AMANPAY_D1_SPACE_PROOF=1)")
return 0
stage = env.get("AMANPAY_D1_SPACE_PROOF_STAGE", "create")
if g is not None: # refused (redacted result)
r = {"timestamp": util.utc_stamp(), "deployed_commit": _deployed_commit(), "stage": stage,
"status": "refused", "failure_class": g, "application_startup_continued": True,
"storage_activated": False, "participant_data_used": False, "WAL_enabled": False}
_put_result(stage, r)
print(f"[d1-space-proof] refused ({g})")
return 0
try:
r = run_stage(stage, env["AMANPAY_D1_SPACE_PROOF_KEY"], env)
print("[d1-space-proof] " + json.dumps({k: r.get(k) for k in (
"stage", "status", "failure_class", "execution_environment", "sqlite_version",
"journal_mode", "outcome", "generation_id", "restored_generation", "restore_seconds",
"post_integrity_ok", "restored_db_opens", "local_db_state_before", "evidence_key") if k in r}))
except Exception as exc: # noqa: BLE001 β€” absolute last-resort fail-safe
print(f"[d1-space-proof] fatal ({type(exc).__name__}); continuing to app startup")
return 0
if __name__ == "__main__":
raise SystemExit(main())