PACT Passport — agent ID. Self-certifying identity, holder-bound capabilities, audit receipts.
Project description
PACT Passport
The agent ID.
Self-certifying identity, holder-bound capabilities, and unilateral audit receipts for agent-to-agent systems. Three message types — REQ, RES, RES_CHUNK. Everything else is built at the edges.
Status: v0.6.1 — three-tier trust gradient (passport / visa / refusal) shipped on top of the v0.5 capability layer. Spec v1.1 → v1.3 codifies the wire changes. Bugs 1–10 from the v0.1 case-study battery + paper-revision experiments closed at the reference-implementation level; cross-machine Stage 2 runs pending.
- V-tier visa machinery + emit-only
protocol_advertisementfield (PACT itself never reads or acts on a received advertisement — see spec §16.5).- Bugs 6 / 7 / 8 / 9 closed in v0.6.0; Bug 10 (Windows-only gap in the Bug 7 stream-partition fix) closed in v0.6.1 after the CI matrix caught it on the v0.6.0 release push.
- 282 tests passing across macOS / Linux / Windows (CI matrix green; some POSIX-only tests skipped on Windows).
- Stage 2 adversarial probe harness (25 pre-registered probes) ready for cross-machine runs.
- Full case-study details in docs/EXPERIMENTS.md Part 2.
Breaking changes (v0.5.2 → v0.6): see CHANGELOG.md for the full v0.2 → v0.5.1 history.
build_req(cap_envelope=...)without an explicitcap_idnow auto-derivescap_idfrom the envelope, or raisesValueErrorif the envelope lacks one (v0.5.2; was silent pass).- REQs with deadlines further than
max_deadline_seconds(default 3600s) in the future are rejected with new fault codedeadline_too_far(v0.5.2; bump the constructor arg for long-running streaming intents).DelegationLinkgains required-from-v1.3action_at_step+caveats_at_stepfields; pre-v1.3 chains verify at K=2 only withDeprecationWarning. v1.4 will drop pre-v1.3 support — re-issue long-lived multi-hop capabilities before then (v0.6.0, Bug 9 / spec §16.1).cancelledreceipt outcome emits on streaming partition; downstream code assuming{completed, failed}only must add acancelledbranch (v0.6.0, #30 / Bug 7).
Overview
PACT Passport is a Python implementation of a minimal trust substrate for agent-to-agent interaction. It sits below orchestration protocols like MCP and A2A as the layer that answers who is this agent, what can they do, and what did they do. Three primitives — self-certifying identity, holder-bound capability tokens, signed messages — plus unilateral audit receipts. No central authority, no shared secrets, no registry.
The reference implementation is ~4,600 LOC in src/pact/. The wire protocol is specified in spec/PACT_v1.md — sufficient for independent implementations. Deterministic test vectors at tests/vectors/pact_v1_vectors.json.
Guarantees
| Property | Mechanism |
|---|---|
| Authenticity | Every message + receipt signed Ed25519 (PyNaCl). verify_message is fail-closed on malformed input. |
| Identity | agent_id = sha256(alg || base64(pubkey)). Self-certifying; no CA. |
| Key rotation | KERI-style pre-rotation via next_key_digest. First post-rotation message proves continuity. |
| Authorization | Holder-bound capability tokens with append-only caveat chain. Stolen tokens require the holder's private key to present (holder_proof). |
| Replay safety | Mandatory idempotency_key per REQ. Durable cache survives process restart. |
| Causal ordering | refs[] field carries sender-asserted back-references; a cooperative sender forms a causal DAG over message history. |
| Liveness | Mandatory deadline with server-side enforcement and configurable upper bound (default 3600s). |
| Audit | Each side writes its own signed receipt unilaterally. outcome ∈ {completed, failed, cancelled} including stream partition. |
| Trust gradient | Three tiers: passport (full identity), visa (session-scoped attenuated capability for passport-less peers), refusal (opaque, no information leak). v0.6+. |
Primitives
| Primitive | What it is | Source |
|---|---|---|
| Identity | Ed25519 keypair with self-certifying agent_id derived from the pubkey, plus a next_key_digest commitment for KERI-style rotation. |
src/pact/identity.py |
| Capability | Signed, holder-bound token. Caveats append-only; verifier re-derives action + caveats at each chain step (Macaroons-style). Multi-hop delegation at any depth verifies inline via cap_envelope. |
src/pact/capability.py |
| Visa | Session-scoped, attenuated capability bound to an ephemeral key. Issued by a gatekeeper to a passport-less counterparty; binds the session, not the identity. | src/pact/visa.py |
| Message | REQ / RES / RES_CHUNK (streaming). Mandatory deadline + idempotency_key. refs[] carries sender-asserted causal back-references. |
src/pact/message.py |
| Receipt | Each side writes its own signed receipt unilaterally — no coordination required. Both receipts share task_ref, so the pair reconstructs a bilateral trace post-hoc. outcome ∈ {completed, failed, cancelled}. |
src/pact/receipt.py |
Non-goals
- Cross-organization capability chains. v0.6 enforces strict
issuer must be self. Cross-org via atrusted_issuersset is post-v0.7. - Application-level caveat enforcement. Caveats validate structurally at issue/attenuate; handler-side enforcement is post-v0.7.
- Cross-machine revocation propagation. Issuer-local only — no CRL, no push-REVOKE.
- Post-quantum signatures. Ed25519 throughout.
crypto.pyis a single-file seam for the eventual migration. - Per-token cost accounting. Rate limits via
max_invocationsonly. Token economics belong one layer up.
Install
pip install pact-passport
Or directly from this repo:
pip install git+https://github.com/bene-art/pact-passport.git
Optional extras:
pip install pact-passport[cbor] # CBOR encoding support
pip install pact-passport[fast] # Async uvicorn server
pip install pact-passport[lak] # local-agent-kit integration
The Python module is pact regardless of the distribution name — from pact import PACTAgent works either way.
Quick Start
CLI
# Terminal 1: Create and serve an agent
pact init alice
pact serve --agent alice --capabilities get_weather
# Terminal 2: Create another agent, discover, and ask
pact init bob
pact discover
pact ask alice get_weather '{"city": "Chicago"}'
pact receipts
Python API
from pact import PACTAgent
agent = PACTAgent("alice", capabilities=["get_weather"])
@agent.handle("get_weather")
def weather(payload):
return {"temp": 72, "condition": "clear"}
agent.serve()
Demo
python examples/demo.py
Runs two agents in-process, exchanges a capability-scoped task, and verifies receipts.
CLI Commands
| Command | Purpose |
|---|---|
pact init <name> |
Create agent identity with pre-rotation key commitment |
pact serve |
Start HTTP server + mDNS broadcast. Flags: --agent NAME, --capabilities a,b,c (comma-separated). |
pact discover |
Find agents on local network |
pact ask <target> <action> [payload] |
Send a task (auto-handshake on first contact) |
pact grant <holder> <action> |
Issue a capability token |
pact revoke <cap_id> |
Revoke a capability |
pact caps |
List issued capabilities |
pact rotate |
Rotate keys using pre-rotation |
pact doctor |
Validate keys, event log, permissions |
pact trace <msg_id> |
Walk the causal message DAG |
pact receipts |
List audit receipts |
pact identity |
Show public identity document |
pact peers |
List known peers |
Architecture
crypto.py All PyNaCl in one file (post-quantum swap = one file change)
identity.py Ed25519 identity, agent_id, key event log, rotation
capability.py Token issue, attenuate, verify, multi-hop delegation chains (Macaroons-style re-derivation)
visa.py V-tier visa machinery: issuance policy, holder-proof binding, peer-network-id rate limits
message.py REQ/RES builder, signer, verifier
receipt.py Unilateral signed audit receipts
store.py Filesystem storage (~/.pact/)
transport/
server.py HTTP server with CBOR content negotiation
async_server.py Optional async server via uvicorn
client.py HTTP client with CBOR support
discovery.py mDNS via zeroconf
agent.py PACTAgent high-level API
cli.py `pact` command (13 subcommands)
contrib/
lak_channel.py local-agent-kit integration
Features by Release
Last 3 releases below; see CHANGELOG.md for v0.1 → v0.5.3 history.
| Release | Feature | Status |
|---|---|---|
| v0.5.4 | Public-surface polish: README agent_id formula corrected, [project.urls] added to pyproject.toml, SECURITY.md added, auto_grant emits DeprecationWarning (scheduled for v1.0 removal). No wire changes. |
Done |
| v0.6.0 | V-tier visa machinery + emit-only protocol_advertisement field + Bugs 6/7/8/9 closed (per-link parent_cap_id at K≥3, cancelled-receipt on stream partition, rate-limit cap_token binding, rogue-delegator chain re-derivation). Spec v1.1.0-draft → v1.3.0-draft. Stage 2 adversarial probe harness staged. Wire changes — see breaking changes above. |
Done |
| v0.6.1 | Bug 10 fix: stream-partition transport handler now catches ConnectionError (parent class), covering ConnectionAbortedError (Windows WinError 10053) in addition to BrokenPipeError / ConnectionResetError (POSIX). Caught by CI matrix on the v0.6.0 release push. README polish + EXPERIMENTS.md Part 2 documenting Bugs 6–10. No wire changes. |
Done |
Tests
pip install -e ".[dev,cbor,fast]"
pytest -v
282 tests, 0 xfails. Coverage:
- Cryptography, identity, key rotation, doctor validation — Ed25519 round-trip, key event log, KERI pre-rotation, store-permission checks.
- Capabilities — issue, attenuate, verify, multi-hop chains at K ∈ {3, 5, 7, 10},
cap_envelope, append-only caveats, chain re-derivation (Macaroons-style). - Messages — REQ / RES / RES_CHUNK, signing, deadline enforcement, idempotency, sender-asserted
refs[]. - Receipts — completed / failed / cancelled, including stream-partition cancellation across POSIX + Windows exception variants.
- Transport — HTTP, CBOR content negotiation, async (uvicorn), mDNS discovery, slow-loris read-timeout.
- Integration — two-agent + three-agent delegation, V-tier visa battery (V1–V7),
protocol_advertisementno-consumption proof, deep-delegation regression. - Stress — 5 race-conditions under concurrent dispatch,
PACT_CHAOS=1chaos mode. - CLI smoke —
init/identity/caps/grant/revoke/receipts/peers/doctor. - End-to-end —
PACTAgent.ask()happy path / unknown target / failed-receipt-on-error.
Stage 2 adversarial probe harness (25 pre-registered probes) runs standalone, not under pytest — see tests/stage2/.
Platform support
| Platform | Status |
|---|---|
| macOS | green (CI matrix: macos-latest × Python 3.11 / 3.12 / 3.13) |
| Linux | green (CI matrix: ubuntu-latest × Python 3.11 / 3.12 / 3.13) |
| Windows 11 | green (CI matrix: windows-latest × Python 3.11 / 3.12 / 3.13; some POSIX-only tests skipped) |
Concurrency stress mode
Set PACT_CHAOS=1 to inject random delays at race-prone code paths. Useful for catching idempotency / rate-limit races that would otherwise surface 1-in-1000:
PACT_CHAOS=1 pytest -v
Specification
- Concept document: docs/PACT_Specification.md
- Formal v1 spec: spec/PACT_v1.md — sufficient for independent implementation
- Test vectors: tests/vectors/pact_v1_vectors.json — deterministic, reproducible
- Case study (v0.1.3 + paper-revision through v0.6.1): docs/EXPERIMENTS.md — Part 1 covers 23 stress experiments and 5 bugs in the v0.1.3 era; Part 2 covers paper-revision experiments and 5 additional bugs (Bugs 6–10), including one caught by CI matrix on the v0.6.0 release push.
Theoretical Foundations
| Paper | Contribution |
|---|---|
| Saltzer, Reed, Clark — End-to-End Arguments (1984) | Push verification to agents, not transport |
| Waldo et al. — A Note on Distributed Computing (1994) | Failure is explicit, never abstracted away |
| Lamport — Time, Clocks, and the Ordering of Events (1978) | Causal ordering via message DAG |
| Birgisson et al. — Macaroons (2014) | Attenuable, context-bound capability tokens |
| Smith — KERI (2019) | Self-certifying identity with pre-rotation |
| Miller — Robust Composition (2006) | Capability discipline, no ambient authority |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pact_passport-0.6.1.tar.gz.
File metadata
- Download URL: pact_passport-0.6.1.tar.gz
- Upload date:
- Size: 78.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b36812ea04b1ae543d2a530468ca4fbf09eba6d7b15c2ff3b025161089b49c9d
|
|
| MD5 |
3e5a48d36123171d9fa67f1b4025663d
|
|
| BLAKE2b-256 |
d41da0ced58c98e69532c1d02bd5eae528041163c2aa1102130ddfcfde1bd9c5
|
Provenance
The following attestation bundles were made for pact_passport-0.6.1.tar.gz:
Publisher:
release.yml on bene-art/pact-passport
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pact_passport-0.6.1.tar.gz -
Subject digest:
b36812ea04b1ae543d2a530468ca4fbf09eba6d7b15c2ff3b025161089b49c9d - Sigstore transparency entry: 1791429511
- Sigstore integration time:
-
Permalink:
bene-art/pact-passport@e608699e6fed1e8da5a2f3f2466d9d02baeb42e7 -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/bene-art
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e608699e6fed1e8da5a2f3f2466d9d02baeb42e7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pact_passport-0.6.1-py3-none-any.whl.
File metadata
- Download URL: pact_passport-0.6.1-py3-none-any.whl
- Upload date:
- Size: 61.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57d454d38e419e60d8eb19513209348dd5a3ac429e4641513b0f20fd8ae6115f
|
|
| MD5 |
3bffc7f48e39bc756f13f185f84aee77
|
|
| BLAKE2b-256 |
96b67a5bc0b5e988a90b0fed51622d514d90a700bf0efd1cb7b6697fb680231f
|
Provenance
The following attestation bundles were made for pact_passport-0.6.1-py3-none-any.whl:
Publisher:
release.yml on bene-art/pact-passport
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pact_passport-0.6.1-py3-none-any.whl -
Subject digest:
57d454d38e419e60d8eb19513209348dd5a3ac429e4641513b0f20fd8ae6115f - Sigstore transparency entry: 1791429715
- Sigstore integration time:
-
Permalink:
bene-art/pact-passport@e608699e6fed1e8da5a2f3f2466d9d02baeb42e7 -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/bene-art
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e608699e6fed1e8da5a2f3f2466d9d02baeb42e7 -
Trigger Event:
push
-
Statement type: