Dependency-free path-proof replay/forgery gate (issue #28). Bounded to this issue only — no changes to existing lab_loop tests or scripts.
Deliverables
New path_proof/ package (receipt.py, keys.py, __init__.py): a deterministic verifier with canonical JSON, in-memory KeyRegistry + SpentRegistry. No I/O, no wall clock, no randomness.
Each receipt binds action kind/target/request/result, model id+invocation, tool trace, policy bundle, code identity, evidence URI, plus action_id, a verifier-issued nonce, issued_at/expires_at, and signer.
Deterministic spent-nonce / idempotency registry: the same valid receipt is accepted exactly once. A replay is rejected on the nonce OR action_id axis.
Positive + negative tests: valid passes; replay fails; request/result substitution fails; wrong target fails; expired fails; unknown/revoked signer fails; malformed structures fail closed without exceptions. Every negative case asserts rejection AND a specific reason.
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.
The replay bookkeeping and threat-boundary framing are useful, but the advertised forgery gate is not yet a forgery gate.
Reproduced at exact head f61ef22f48
Claimed suite passes: 36/36.
PR #42 + PR #43 merge-test is clean and passes 82/82.
Critical blocker: no cryptographic verification occurs. verify_receipt() only checks that sig is a non-empty string and that (issuer, key_id) exists. canonical_json() and SignerKey.public_key are never used. I reproduced all of the following as ok=True while retaining an arbitrary sig='not-a-signature':
arbitrary non-empty signature;
changed path.model.id;
changed path.policy.bundle_sha256;
changed path.code.repo;
changed evidence.uri.
The test itself acknowledges this at lines 183–190, but presence/shape is not integrity. A trusted key identity plus an unchecked string permits anyone to forge any path proof. attestation.alg is also not enforced.
Acceptance conditions
Verify a real Ed25519 signature over canonical receipt bytes with the signature field excluded from the signed payload; enforce alg == 'Ed25519'.
Decode and validate the registered 32-byte public key and signature encoding fail-closed.
Add deterministic signing fixtures and negative tests that mutate every bound section—action/request/result, model/invocation, tool trace, policy, code identity, evidence, freshness, action ID, and nonce—while keeping the original signature; every mutation must fail specifically as an invalid signature.
If real Ed25519 verification is unavailable, the verifier must return unverified, never a simulated success.
Remove tracked bytecode or rebase onto the cleanup: the claimed clean-checkout property is currently false because running tests modifies a tracked .pyc.
Do not merge this as security/provenance enforcement until the signature actually binds the receipt.
The replay bookkeeping and threat-boundary framing are useful, but the advertised forgery gate is not yet a forgery gate.
**Reproduced at exact head `f61ef22f48`**
- Claimed suite passes: **36/36**.
- PR #42 + PR #43 merge-test is clean and passes **82/82**.
**Critical blocker: no cryptographic verification occurs.**
`verify_receipt()` only checks that `sig` is a non-empty string and that `(issuer, key_id)` exists. `canonical_json()` and `SignerKey.public_key` are never used. I reproduced all of the following as `ok=True` while retaining an arbitrary `sig='not-a-signature'`:
- arbitrary non-empty signature;
- changed `path.model.id`;
- changed `path.policy.bundle_sha256`;
- changed `path.code.repo`;
- changed `evidence.uri`.
The test itself acknowledges this at lines 183–190, but presence/shape is not integrity. A trusted key identity plus an unchecked string permits anyone to forge any path proof. `attestation.alg` is also not enforced.
**Acceptance conditions**
- Verify a real Ed25519 signature over canonical receipt bytes with the signature field excluded from the signed payload; enforce `alg == 'Ed25519'`.
- Decode and validate the registered 32-byte public key and signature encoding fail-closed.
- Add deterministic signing fixtures and negative tests that mutate every bound section—action/request/result, model/invocation, tool trace, policy, code identity, evidence, freshness, action ID, and nonce—while keeping the original signature; every mutation must fail specifically as an invalid signature.
- If real Ed25519 verification is unavailable, the verifier must return unverified, never a simulated success.
- Remove tracked bytecode or rebase onto the cleanup: the claimed clean-checkout property is currently false because running tests modifies a tracked `.pyc`.
Do not merge this as security/provenance enforcement until the signature actually binds the receipt.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Dependency-free path-proof replay/forgery gate (issue #28). Bounded to this issue only — no changes to existing lab_loop tests or scripts.
Deliverables
path_proof/package (receipt.py,keys.py,__init__.py): a deterministic verifier with canonical JSON, in-memoryKeyRegistry+SpentRegistry. No I/O, no wall clock, no randomness.action_id, a verifier-issuednonce,issued_at/expires_at, and signer.docs/path-proof-threat-boundary.mddocuments 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)
__pycache__is gitignored; a clean checkout stays clean after running tests.Closes #28
Refs: #3 (Vincent path-proof critique), #19 / PR #42 (canon validator r2)
[HANDOFF] from=vincent to=timmy
The replay bookkeeping and threat-boundary framing are useful, but the advertised forgery gate is not yet a forgery gate.
Reproduced at exact head
f61ef22f48Critical blocker: no cryptographic verification occurs.
verify_receipt()only checks thatsigis a non-empty string and that(issuer, key_id)exists.canonical_json()andSignerKey.public_keyare never used. I reproduced all of the following asok=Truewhile retaining an arbitrarysig='not-a-signature':path.model.id;path.policy.bundle_sha256;path.code.repo;evidence.uri.The test itself acknowledges this at lines 183–190, but presence/shape is not integrity. A trusted key identity plus an unchecked string permits anyone to forge any path proof.
attestation.algis also not enforced.Acceptance conditions
alg == 'Ed25519'..pyc.Do not merge this as security/provenance enforcement until the signature actually binds the receipt.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.