stackchain-lab-loop/path_proof/__init__.py
vincent f61ef22f48 [Vincent] #28: executable replay/forgery gate for path-proof receipts
Dependency-free path_proof package under a dedicated module. Bounded to this
issue only: no changes to existing lab_loop tests or scripts.

Deliverables:
1. path_proof/ package (receipt.py, keys.py, __init__.py) — verifier with
   canonical JSON, deterministic build/verify/verify_and_accept, in-memory
   KeyRegistry + SpentRegistry. No I/O, no wall clock, no randomness.
2. Each receipt binds action kind/target/request/result, model id+invocation,
   tool trace, policy bundle, code identity, evidence uri, plus action_id,
   verifier nonce, issued_at/expires_at, and signer.
3. Deterministic spent-nonce/idempotency registry: the same valid receipt is
   accepted exactly once; a replay is rejected on the nonce OR action_id axis.
4. Positive + negative tests: valid passes; replay fails; request/result
   substitution fails; wrong target fails; expired fails; unknown/revoked
   signer fails; malformed structures fail closed with no exception. Every
   negative case asserts rejection AND a specific reason.
5. docs/path-proof-threat-boundary.md documents the exact threat boundary: a
   verified receipt proves a bound, fresh, single-use, trusted-attested
   commitment — NOT runtime execution unless runtime evidence is supplied.

Clean-checkout verification (commands actually run):
- /home/vincent/seedvault-inventory/venv/bin/python3 -m pytest tests/ -q
  -> 36 passed (31 path_proof + 5 pre-existing lab_loop)
- git diff --cached --name-only origin/main -> 6 files, zero .pyc
- __pycache__ gitignored; clean checkout stays clean after tests

Closes #28
Refs: #3 (Vincent path-proof critique), #19/PR #42 (canon validator r2)
[HANDOFF] from=vincent to=timmy
2026-08-15 16:44:58 -04:00

53 lines
1.6 KiB
Python

"""
Path Proof receipt replay/forgery gate.
A dependency-free verifier for runtime-attested consequential-action receipts.
See ``docs/path-proof-receipt.md`` for the threat model and field rationale.
Threat boundary
---------------
A valid, freshly-accepted receipt proves exactly one thing: that a trusted
signer attested a specific bound action (request + result + target/resource +
model invocation + code identity) within its validity window, and that the
nonce/action-id has not been spent. It does NOT prove the provider executed
the advertised weights, and it does NOT prove the consequential side effect
actually took place at runtime. Runtime proof requires the verifier to
independently re-observe the effect (e.g. read Gitea state back and compare
``result_sha256``). Anything outside those bounds is reported ``unverified``,
never partial-success.
Determinism
-----------
All verification takes ``now`` as an explicit parameter (no hidden clock), and
the spent-nonce registry is a plain in-memory object the caller controls.
There is no I/O, no randomness, and no network in this module.
"""
from . import keys
from .keys import SignerKey
from .receipt import (
SPEND_KIND_NONCE,
SPEND_KIND_ACTION_ID,
SpendRecord,
SpentRegistry,
canonical_json,
build_receipt,
receipt_payload,
verify_receipt,
verify_and_accept,
)
__all__ = [
"SPEND_KIND_NONCE",
"SPEND_KIND_ACTION_ID",
"SignerKey",
"SpendRecord",
"SpentRegistry",
"canonical_json",
"build_receipt",
"receipt_payload",
"verify_receipt",
"verify_and_accept",
"keys",
]