continuity-receipt 0.3.3

Independent Rust verifier for continuity-receipt/0.1-0.3 bundles (the Python implementation remains the reference)
Documentation

continuity-receipt (Rust)

Second, independent implementation of the Continuity Receipt verifier for continuity-receipt/0.1-0.3 bundles and verification receipts (companion v1). The Python implementation in ../continuity_receipt/ remains the reference; this crate exists for the two-independent-implementations bar, native embedding, and single-binary deployment.

It re-implements the pinned JCS subset (canon.rs), did:key Ed25519 verification (didkey.rs), and the full verification algorithm with the same error codes, verdict precedence, and JSON output shape as continuity_receipt.verify (verify.rs) and continuity_receipt.verification (verification.rs).

Install

cargo add continuity-receipt        # library
cargo install continuity-receipt    # CLIs: verify, disclose, anchor, verify-receipt

The crate tracks continuity-receipt/0.1-0.3 bundles; the Python implementation in this repository remains the reference. Publication runbook: PUBLISH.md.

Run

cargo run --bin continuity-receipt-verify -- ../vectors/02_happy_full.json
cargo run --bin continuity-receipt-verify -- ../vectors/10b_anchor_missing.json --require-anchor

The binary prints the same JSON shape as the Python CLI (verdict, errors, provisional_reasons, insufficient_reasons, summary) and exits 0 iff the verdict is TRUSTED, else 1.

Selective disclosure

continuity-receipt-disclose mirrors continuity_receipt/disclose.py (redact / verify / reveal / check):

cargo run --bin continuity-receipt-disclose -- redact \
  --bundle ../vectors/02_happy_full.json \
  --path receipts[3].body.spec_ref \
  --out /tmp/redacted.json --map /tmp/map.json --gate-key /tmp/gate.key

cargo run --bin continuity-receipt-disclose -- verify \
  --bundle /tmp/redacted.json --map /tmp/map.json

cargo run --bin continuity-receipt-disclose -- reveal \
  --bundle /tmp/redacted.json --map /tmp/map.json \
  --path receipts[3].body.spec_ref --out /tmp/package.json

cargo run --bin continuity-receipt-disclose -- check \
  --salt <hex> --value '["quality-ok"]' --commit sha256:...

The gate-key file is the raw 32-byte Ed25519 seed. Redaction re-signs the modified receipt and every receipt after it, so the signer must be the chain's issuer; redacting a cross-checked field (for example spend_cap) fails closed to UNTRUSTED because a commitment cannot prove the claim.

Verification receipts

continuity-receipt-verify-receipt mirrors continuity_receipt/verification.py: shape and consistency checks (error_codes equals the codes in errors; the verdict equals the class implied by the errors/reasons), the Ed25519 signature over the canonical view minus sig, optional bundle-digest and issuer-revocation checks.

cargo run --bin continuity-receipt-verify-receipt -- \
  ../vectors/verification/01_valid.json --bundle ../vectors/verification/bundle.json
cargo run --bin continuity-receipt-verify-receipt -- \
  ../vectors/verification/12_revoked_issuer.json \
  --bundle ../vectors/verification/bundle.json \
  --revocations ../vectors/verification/12_revoked_issuer.revocations.json
cargo run --bin continuity-receipt-verify-receipt -- \
  ../vectors/verification/01_valid.json --canonical /tmp/receipt.canonical   # prints the anchor digest

Exit 0 iff the receipt is valid. --revocations reads a local revocation document (the Python CLI also accepts URLs).

OpenTimestamps anchors

continuity-receipt-anchor mirrors continuity_receipt/anchor.py: replay a detached .ots proof from the file digest and verify a Bitcoin attestation against a supplied 80-byte block header by exact merkle-root equality.

cargo run --bin continuity-receipt-anchor -- verify proof.ots \
  --digest sha256:<hex> [--header <80-byte hex> --height <n>] [--json]

cargo run --bin continuity-receipt-anchor -- verify proof.ots \
  --bundle ../vectors/08_redacted_disclosed.json --target <receipt_id> --json

Statuses verified / unverified / mismatch / invalid with the same machine codes as Python; exit 0 iff verified. Header chain validation is out of scope by design (../ANCHORING.md): the header is trusted input, not a substitute for proof-of-work or confirmation checks.

Tests

cargo test

The vector runner reads ../vectors/manifest.json and checks all 26 bundle vectors against their expected verdict and error code; tests/verification.rs does the same for the 20 verification-receipt vectors (../vectors/verification/manifest.json), including consistency violations and receipt digests. Malformed-bundle smoke cases (empty object, non-object, missing receipts) are included so the verifier returns structured errors instead of panicking. tests/disclose.rs covers redaction, tail re-signing, refusal cases, and commitment recomputation against the frozen vectors. tests/fuzz_corpus.rs mutates every vector (deterministic seed) plus synthetic malformed shapes and byte truncations: every case must yield a structured verdict with coded errors and never panic. Set CR_FUZZ_CORPUS_DIR=<dir> to write the generated corpus to disk.

tests/anchor.rs covers synthetic proofs (LEB128 varuints, reverse/prepend, truncation, keccak refusal) and the five real .ots fixtures, including the published keccak negative case.

The Python↔Rust differentials run in CI after cargo build:

python3 tools/differential_vectors.py               # verdict + error-code parity
python3 tools/differential_disclose.py              # maps, signatures, cross-verification
python3 tools/differential_anchor.py                # status/code/confirmed parity
python3 tools/differential_verification_receipts.py # validity/codes/digest parity

Intentional, documented differences from Python

  • Floats and canonicalization failures produce a structured malformed (or the relevant check's) error instead of a Python exception/traceback.
  • Invalid JSON input to the CLI prints a structured malformed result and exits 1 instead of raising.
  • Non-list anchors/revocations values and other deeply malformed shapes are failed closed with the corresponding structured error where Python would raise.
  • disclose redact additionally accepts repeatable --salt <path>=<hex> for reproducible commitments; the Python CLI always randomizes salts.
  • disclose verify does not accept --revocations until the Rust revocation-list loader lands; external lists remain Python-only. The verification-receipt CLI accepts local revocation documents (no URLs).
  • anchor verify human (non---json) output prints attestations as compact JSON rather than Python dict reprs; the --json shape is identical.