Skip to main content

pipelock-verify

PyPI Python CI CodeQL OpenSSF Scorecard License: Apache-2.0

Python verifier for Pipelock receipts. Supports ActionReceipt v1 chains and individual EvidenceReceipt v2 envelopes for contract-aware lifecycle events. Verifies Ed25519 signatures, v1 chain linkage, v2 payload schemas, key-purpose authority, and flight-recorder wrapping.

Mirrors the Go reference implementation byte-for-byte. The conformance golden files in tests/conformance/ are generated by Pipelock's Go code and verified identically by both sides.

Install · Usage · EvidenceReceipt v2 · Well-known directory · What gets verified · Spec · Go reference

Install

pip install pipelock-verify

Only one runtime dependency: cryptography for the Ed25519 primitives.

Usage

Single receipt (auto-detects v1 vs v2)

import pipelock_verify

with open("receipt.json", "rb") as f:
    result = pipelock_verify.verify(f.read())

if not result.valid:
    raise SystemExit(f"bad receipt: {result.error}")

print(f"OK: {result.action_id} {result.verdict} {result.target}")

The verify() function automatically routes to the correct verification path based on the record_type field:

  • No record_type or "action_receipt_v1" routes to ActionReceipt v1.
  • "evidence_receipt_v2" routes to EvidenceReceipt v2.
  • Unknown record_type is rejected with a clear error.

Pin a signing key via the well-known directory

import pipelock_verify

# Fetch the signing keyset from the Pipelock instance.
directory = pipelock_verify.fetch_directory("pipelab.org")
key_hex = directory.public_key_hex()

result = pipelock_verify.verify(receipt_bytes, public_key_hex=key_hex)

ActionReceipt v1 chain

Pass a flight-recorder JSONL path:

chain = pipelock_verify.verify_chain("evidence-proxy-0.jsonl")

if not chain.valid:
    raise SystemExit(f"chain broken at seq {chain.broken_at_seq}: {chain.error}")

print(f"CHAIN VALID: {chain.receipt_count} receipts, root {chain.root_hash}")

When no trust anchor is supplied, the first receipt's signer_key becomes the expected key for the rest of the chain. This matches the signer- consistency check in Go's receipt.VerifyChain.

verify_chain() intentionally fails closed when the chain contains EvidenceReceipt v2 entries. v0.2.0 verifies v2 receipts one at a time with verify() or verify_evidence(); v2 chain verification is reserved for a follow-up release once the cross-version chain-linking rules are specified.

CLI

python -m pipelock_verify receipt.json
python -m pipelock_verify evidence.jsonl
python -m pipelock_verify evidence.jsonl --key 70b991eb77816fc4...

Exit codes match pipelock verify-receipt: 0 on success, 1 on failure.

EvidenceReceipt v2

EvidenceReceipt v2 is the contract-aware receipt envelope introduced in Pipelock v2.4. It sits alongside ActionReceipt v1 (which remains unchanged for backward compatibility).

Direct v2 verification

For fine-grained control over v2-specific checks (key purpose enforcement, signer key ID pinning):

from pipelock_verify import verify_evidence

result = verify_evidence(
    receipt_dict,
    public_key_hex="...",
    expected_signer_key_id="receipt-key-prod",
    expected_key_purpose="receipt-signing",
)

if not result.valid:
    raise SystemExit(f"v2 receipt failed: {result.error}")

print(f"Event: {result.event_id}, Kind: {result.payload_kind}")

13 payload kinds

Payload kind Signing purpose
proxy_decision receipt-signing
contract_ratified receipt-signing
contract_promote_intent contract-activation-signing
contract_promote_committed receipt-signing
contract_rollback_authorized contract-activation-signing
contract_rollback_committed receipt-signing
contract_demoted receipt-signing
contract_expired receipt-signing
contract_drift receipt-signing
shadow_delta receipt-signing
opportunity_missing receipt-signing
key_rotation contract-activation-signing
contract_redaction_request contract-activation-signing

The authority matrix is enforced automatically. A valid signature from the wrong key purpose is rejected.

v2 canonicalization

EvidenceReceipt v2 uses RFC 8785 JSON Canonicalization Scheme (JCS) for signable preimages, not Go's encoding/json byte order (which is what ActionReceipt v1 uses). JCS rules:

  • Object keys sorted lexicographically by Unicode codepoint.
  • Strings NFC-normalized.
  • Floats rejected (use decimal strings).
  • No whitespace between tokens.

The signature field is zeroed before computing the preimage.

Well-known directory

Pipelock instances serve their signing keys at /.well-known/http-message-signatures-directory (RFC 9421). Use the built-in fetch helper to retrieve and parse the keyset:

from pipelock_verify import fetch_directory, parse_directory

# Fetch from a live instance.
directory = fetch_directory("pipelab.org")

# Or parse from a pre-fetched JSON blob.
directory = parse_directory(json_bytes)

# Look up a specific key.
key = directory.get_key("pipelock-mediation-prod")
if key:
    print(f"Key: {key.public_key}, Use: {key.use}")

What gets verified

On a single ActionReceipt v1:

  • Envelope version (rejects anything other than v1).
  • Action record version (rejects anything other than v1).
  • Required action record fields (action_id, action_type, timestamp, target, verdict, transport).
  • Signature format (ed25519:<hex> prefix, 64-byte length).
  • Signer key format (32-byte hex).
  • Optional trust anchor match (public_key_hex argument).
  • Ed25519 signature over SHA-256(canonical action record).

On a single EvidenceReceipt v2:

  • Envelope record_type and receipt_version.
  • Strict unknown-field rejection (envelope, signature proof, and payload).
  • Required envelope fields (event_id, timestamp, payload_kind).
  • Payload schema validation for all 13 payload kinds.
  • Key purpose authority matrix enforcement.
  • Signature proof structure (signer_key_id, key_purpose, algorithm).
  • Ed25519 PureEdDSA signature over JCS(receipt_without_signature).
  • Optional trust anchors: public_key_hex, expected_signer_key_id, expected_key_purpose.

On an ActionReceipt v1 chain:

  • Every individual ActionReceipt v1 above.
  • Signer consistency across the chain.
  • Monotonic chain_seq starting at 0.
  • chain_prev_hash linkage via SHA-256 of canonical envelopes.
  • First receipt's chain_prev_hash equals "genesis".

EvidenceReceipt v2 entries are rejected in chain mode with an explicit unsupported-v2-chain error. Verify them individually in v0.2.0.

Input formats

verify_chain() accepts JSONL in two shapes:

  1. Flight-recorder entries -- the format Pipelock actually writes to disk. Each line is a recorder.Entry object with type == "action_receipt" and the receipt nested in detail. Non-receipt entries (checkpoints etc.) are skipped, not rejected.
  2. Bare receipts -- one receipt object per line, no wrapping. Used by the conformance suite and handy for ad-hoc testing. ActionReceipt v1 bare receipts are verified as a chain. EvidenceReceipt v2 bare receipts are rejected in chain mode and should be verified individually.

verify() accepts:

  • A JSON string or UTF-8 bytes.
  • A pre-parsed dict (for callers that already have the receipt loaded).
  • A flight-recorder entry dict (transparently unwrapped).

Relationship to the Go reference

Both implementations verify the same single-receipt v2 golden files. For chain root hashes, v0.2.0 parity is limited to ActionReceipt v1 chains.

Development

git clone https://github.com/luckyPipewrench/pipelock-verify-python
cd pipelock-verify-python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest

Maintainers: see RELEASING.md for the OIDC-based publish flow.

To refresh the conformance fixtures from a local Pipelock checkout:

cd /path/to/pipelock
go test ./sdk/conformance/ -run TestGenerateGoldenFiles -update
cp sdk/conformance/testdata/*.{json,jsonl} \
   /path/to/pipelock-verify-python/tests/conformance/
pytest

License

Apache 2.0. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pipelock_verify-0.3.0.tar.gz (59.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pipelock_verify-0.3.0-py3-none-any.whl (37.7 kB view details)

Uploaded Python 3

File details

Details for the file pipelock_verify-0.3.0.tar.gz.

File metadata

  • Download URL: pipelock_verify-0.3.0.tar.gz
  • Upload date:
  • Size: 59.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pipelock_verify-0.3.0.tar.gz
Algorithm Hash digest
SHA256 eb25cfa9633d8a1396edb94be4770a102108eff61bc27a90d64c9a23a76b6c25
MD5 5b6f95134c5670277fafd5c60dfd948b
BLAKE2b-256 bfd9a809a325328b03d044080bf5ff58e65c9148eadb8600905f8680b7f26a26

See more details on using hashes here.

Provenance

The following attestation bundles were made for pipelock_verify-0.3.0.tar.gz:

Publisher: release.yml on luckyPipewrench/pipelock-verify-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pipelock_verify-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: pipelock_verify-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 37.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pipelock_verify-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7da048803f21849d4c91526d3da9d8dd1d999d67b0e8e23c9ddf40f881c7303f
MD5 6d0154adf54e3bd6736178cca6520b5c
BLAKE2b-256 dba51fec6adcbe15b9585057e1ae379ff65dfeb87dfa605b8e6be051920bfbd4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pipelock_verify-0.3.0-py3-none-any.whl:

Publisher: release.yml on luckyPipewrench/pipelock-verify-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.0

2 files

This release

0.3.0 This release

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page