Releases: bene-art/pact-passport
Release list
v0.8.2
[0.8.2] — 2026-06-20
Closes the bilateral-receipt round-trip from spec §18.6 by adding a fourth
PACT message type, INITIATOR_ACK, and a server-side dispatch handler that
promotes stored unilateral receipts to bilateral when the initiator's ack
signature verifies. Wire-compatible with v0.8.0 and v0.8.1 — old servers
that see type="INITIATOR_ACK" route through the existing dispatch and
fall through with no fatal error; they simply do not perform the merge.
Pre-registered at tag v0.8.2-pre-registration (commit local). 477 dynamic
tests pass (+9 from v0.8.1's 468); 9/9 formal lemmas unchanged.
Added
type="INITIATOR_ACK"PACT message type (spec §18.6). Same wire route
(/pact/v1/messages), same Ed25519 signing envelope, same canonical-JSON
encoding — only the discriminator changes. Routed inside_dispatchby
message type, before intent dispatch._handle_initiator_ackdispatch handler (agent.py). Verifies the
outer envelope sig, parses the{task_ref, initiator_ack_signature}
payload, looks up the unilateral receipt bytask_ref, verifies the
ack signature against the receipt's canonical bytes minus the
initiator_ack_signaturefield (spec §18.6 binding), and merges the
ack into the stored receipt. The receipt is then bilateral.- Idempotent ack semantics — sending the same INITIATOR_ACK twice
returns{"bilateral": true, "idempotent": true}without re-mutating
the receipt. - Fault codes emitted (spec §18.3):
pact_signature_invalid(envelope
or ack sig invalid),pact_token_malformed(missing payload fields),
pact_token_missing(no receipt for the referenced task_ref),
pact_identity_unresolvable(unknown sender with no inlineidentity_doc).
Changed
- Capability dispatch ergonomics unchanged; the bilateral-receipt
path is purely additive. v0.8.1 unilateral receipts continue to verify
as before; v0.8.2 enables (but does not require) the bilateral upgrade
via INITIATOR_ACK.
Tests
468 → 477 dynamic (+9). New tests/v1_4/dispatch/test_initiator_ack.py
covers the happy path (receipt mutated to bilateral, audit_receipt then
passes), idempotent re-ack, 4 fault classes (malformed payload, missing
receipt, bad ack signature, unknown sender + no identity_doc), malformed
base64, and end-to-end audit_receipt verification before-and-after.
Library-complete vs dispatch-integrated (Option D split, COMPLETE)
- v0.8.0 — library modules + spec + tests + formal-verification freeze.
- v0.8.1 — plumbed audit_req (REQ + V-tier), audit-side
pact_*fault
codes + HTTP mapping, policy + audit public-API exposure, CLI v0.8 surface. - v0.8.2 (this release) — bilateral receipt wire round-trip via new
INITIATOR_ACKmessage type (spec §18.6). 4 of the remaining 9pact_*
fault codes (pact_signature_invalid,pact_token_malformed,
pact_token_missing,pact_identity_unresolvable) are now wired at the
new dispatch site. The remaining 5 (pact_key_revoked,pact_scope_insufficient,
pact_budget_exceeded,pact_depth_exceeded,pact_revocation_observed)
ship as part of the library API and Simple-policy evaluation; their
full dispatch-site migration tracks into v0.9 alongside capability +
replay + rate + bind dispatch refactoring.
Pre-registration evidence (across the v0.8.x series)
- v0.8.0-pre-registration (
f29b56c) — formal verification freeze. 9/9
Tamarin lemmas + 1 ProVerif. D4 + D5 + D6 evidence stack frozen at this
tag. - v0.8.1-pre-registration (
8cd540d) — audit + policy plumbing freeze.
Phase ε.1 confirmatory will re-run Phase A + Phase B at this tag. - v0.8.2-pre-registration (local) — bilateral round-trip freeze.
Phase ε.2 confirmatory will re-run Phase A + Phase B at this tag.
v0.8.1
[0.8.1] — 2026-06-19
Enforcement-side plumbing of the v0.8.0 library modules into the dispatch
pipeline (per Option D split). Wire-compatible with v0.8.0. Closes 4 of the
5 dispatch integration gaps surfaced in the v0.8.0 status review; the 5th
(bilateral receipt round-trip via INITIATOR_ACK + remaining 9 of 13
fault codes) ships in v0.8.2.
Pre-registered at tag v0.8.1-pre-registration (commit local). 468 dynamic
tests pass (+56 from v0.8.0's 412); 9/9 formal lemmas unchanged.
Added
audit_reqplumbed into_handle_taskdispatch pipeline (spec §18.2).
New_step_audit_reqruns after_step_verify_senderand before
_step_verify_capability— cheap-first ordering. Failures short-circuit
with the matching v1.4pact_*fault code.audit_reqplumbed into_handle_visa_request(symmetric to task flow).
Same migration-window semantics.- HTTP status mapping at transport layer (spec §18.3).
_status_for_response
intransport/server.pyreadsfault.code, maps to 401 / 403 / 410 via
FAULT_HTTP_STATUS. Wired into both sync (ThreadingHTTPServer) and async
(uvicorn) servers. Audit-side fault codes map to spec §18.3 statuses;
legacy fault codes keep v0.7 behavior of HTTP 200 with fault in body until
v0.8.2 completes the roll-out. audit_context_strictconstructor kwarg onPACTAgent. Default False
during the §18.7 90-day migration window — a missingaudit_contextis
accepted withDeprecationWarningso v0.7 senders interoperate. Present-
but-malformedaudit_contextis always rejected. Default flips to True
in v0.9.- Public-API surface for v0.8 library modules.
pact_passport.__init__
re-exports the policy module (PolicyProfile,evaluate_caveats,
make_*_caveatfactories,classify_*_profile), the audit module
(audit_req,audit_receipt,make_bilateral_receipt,
sign_initiator_ack,AuditResult), and the fault taxonomy
(ALL_FAULT_CODES,FAULT_HTTP_STATUS,http_status_for_fault). - CLI v0.8 surface (gap 6):
pact audit <receipt_id|-> [--show-receipt]— runsaudit_receipton a
stored receipt (looked up by id or task_ref prefix) or stdin JSON.
PrintsAuditResultand exits non-zero on failure.pact receipts --bilateral-only— filters out unilateral receipts.pact ask --audit-purpose <tag>— setsaudit_context.purposeon
outgoing REQ (default:"task").
PACTAgent.ask(..., audit_purpose: str = "task")kwarg flows the CLI
flag through tobuild_req.
Changed
audit.py:audience_hintMAY be empty (visa-request broadcast form
where the sender doesn't know the gatekeeper'sagent_idpre-visa). Other
requiredaudit_contextkeys MUST still be non-empty strings.tests/integration/test_v_tier_v1_v7.pyhelper_build_task_with_visa
updated to includeaudit_context(was bypassingbuild_req).
Library-complete vs dispatch-integrated (Option D split)
- v0.8.0 — library modules + spec + tests + formal-verification freeze.
- v0.8.1 (this release) — plumbs
audit_reqenforcement (REQ + V-tier),
audit-sidepact_*fault codes + HTTP mapping,policy+auditpublic
API exposure, CLI v0.8 surface. 4 of 5 dispatch gaps closed. - v0.8.2 (planned) — bilateral receipt wire round-trip via new
INITIATOR_ACKmessage type (spec §18.6), completion of the 9 remaining
pact_*fault codes across capability + replay + rate + bind dispatch sites.
Tests
416 → 468 dynamic (+52). Conformance unit tests (tests/v1_4/) unchanged
at 81. New dispatch integration tests under tests/v1_4/dispatch/ (44 tests)
and CLI tests under tests/v1_4/cli/ (8 tests). Total 469 collected with
1 strict-xfail (B0_TLS).
Pre-registration evidence (v0.8.0 frozen baseline)
Tag v0.8.0-pre-registration (commit f29b56c) remains the load-bearing
frozen artifact for the paper. v0.8.1 is an enforcement-side release; the
substrate's H1-H5 verdicts and §12 attribution diagonal are confirmed by
re-running Phase A on the v0.8.1 tag (see D4_SUMMARY.md §6 once Phase ε.1
completes).
v0.8.0
[0.8.0]
See CHANGELOG.md for details.
v0.7.1
[0.7.1] — 2026-06-12
README-only patch. v0.7.0's README didn't reflect v0.7.0's own rename — shipped to PyPI with stale src/pact/ paths and a Status line still saying v0.6.1. No code change.
Fixed (README only)
- Status block updated from "v0.6.1" to "v0.7.x" and now describes the rename + PQ soften + Node 24 actions. Includes a one-line migration note (
from pact import X→from pact_passport import X). - Overview LOC line:
src/pact/→src/pact_passport/. - Primitives table Source column: 5 rows updated from
src/pact/X.pytosrc/pact_passport/X.py(identity, capability, visa, message, receipt). - Features-by-Release table dropped v0.5.4 (oldest), added v0.7.0 + v0.7.1 rows. Intro updated from "last 3" to "last 4".
Why a release for README-only
PyPI displays the README from the wheel METADATA, frozen per release. The v0.7.0 wheel had a stale README baked in. Fixing the GitHub README would leave PyPI showing inconsistent paths until the next substantive release. v0.7.1 ships a corrected README via the same OIDC publish workflow.
v0.7.0
[0.7.0] — 2026-06-11
Module rename for namespace hygiene + post-quantum claim softened. No wire / on-disk / behavior changes. Both fixes prompted by external review of v0.6.1.
Breaking changes
-
Python module renamed:
pact→pact_passport. The previous import path silently shadowedpact-python— the widely-used Pact Foundation contract-testing library, which also installs as thepactmodule. Any environment with both packages installed had unpredictable behavior depending onsys.pathorder. After v0.7.0, both packages coexist without conflict.Migration (every import path):
# Before (v0.6.x and earlier): from pact import PACTAgent from pact.capability import issue_capability, Caveat from pact.message import build_req, verify_message # After (v0.7.0+): from pact_passport import PACTAgent from pact_passport.capability import issue_capability, Caveat from pact_passport.message import build_req, verify_message
Unchanged:
- PyPI package name:
pact-passport(pip install pact-passportworks as before). - CLI binary:
pact(no conflict — pact-python shipspact-broker,pact-stub-service, etc., not a barepact). - Wire protocol, on-disk format, capability tokens, receipts, test vectors, spec — no changes. v0.6.1 receipts/caps remain valid; only the import path moves.
- PyPI package name:
Changed
- Post-quantum claim softened in README. Architecture box and Non-goals previously said
crypto.pywas "a single-file seam for the eventual migration" / "post-quantum swap = one file change". Both overclaimed — a real PQ migration changesagent_idderivation (PQ pubkeys are 1–4KB vs Ed25519's 32B), signature/token sizes, the spec, and test vectors. Updated to acknowledge the migration is non-trivial; the architectural point ("crypto isolated to one module") is preserved.
Tests
282 tests, all passing. No count change — every test file's imports moved from pact to pact_passport; no test behavior changes.
v0.6.1
[0.6.1] — 2026-06-11
Bug 10 fix + documentation pass. No wire changes.
Fixed
- Bug 10 — stream-partition transport handler missed the Windows exception variant. The Bug 7 fix to
_run_streaming_handler(v0.6.0) was correct, but the transport-layer catch attransport/server.py:171enumerated POSIX exception types only:(BrokenPipeError, ConnectionResetError). On Windows, consumer disconnect raisesConnectionAbortedError(WinError 10053), which escaped the catch and bypassedchunks_iter.close()cleanup. Mac and Linux passed locally; only the CI matrix on the v0.6.0 release push surfaced the gap (Windows × Python 3.11 / 3.12 / 3.13 all failedtest_c3_stage1_partition_writes_cancelled_receiptwithexpected exactly 1 cancelled receipt; got 0). Fixed by widening the catch to the parent classConnectionError, which coversBrokenPipeError+ConnectionResetError+ConnectionAbortedErrorand any future platform-specific subclass. Regression test intests/test_server.py::test_send_stream_catches_all_connection_error_subclassesasserts the exception hierarchy our fix depends on and that the source code uses the parent class, not the narrower tuple.
Documentation
- README polish (26 changes): compressed run-on Status paragraph to one-sentence headline + bullets; trimmed Breaking-changes list to v0.5.2 → v0.6 + link to CHANGELOG; updated Overview LOC count (~3,750 → ~4,600); softened
refs[]causal-ordering claim to acknowledge sender-assertion; added Trust-gradient row to Guarantees table (passport / visa / refusal); updated Capability + Receipt rows in Primitives table; added Visa row; updated Non-goals version references (post-v0.6 → post-v0.7); added--agent+--capabilitiesflags to CLI Commands table; addedvisa.pyto Architecture diagram; trimmed Features-by-Release table to last 3 releases; replaced 281-test paragraph with bulleted coverage; updated Platform-support table with actual CI matrix status; reconciled 5-vs-9 bug count by pointing to EXPERIMENTS.md Part 2; removed internal jargon (NUC-bridge, C-tier, B1/B3 labels, "load-bearing"); dropped v0.5.5 row (subsumed into v0.6.0, never released standalone). - EXPERIMENTS.md Part 2 added covering v0.5.5 → v0.6.1 paper-revision experiments. Bugs 6 / 7 / 8 / 9 / 10 documented with same narrative style as v0.1.3's Part 1. Stage 2 cross-machine probe harness section. Updated "What I learned" lessons (#5: the CI matrix is an adversary too). Updated "Where this leaves PACT" for v0.6.1 state. Part 1 (v0.1.3 case study) preserved intact as historical record.
Tests
281 → 282 (+1 Bug 10 regression test). Coverage unchanged. CI matrix green across macOS / Linux / Windows × Python 3.11 / 3.12 / 3.13.
v0.5.1 — polish: 4 bugs fixed, async-server parity, 3-platform CI verified
Summary
Post-v0.5.0 audit pass. Four real bugs fixed, async-server brought up to v0.5 parity, docs caught up to current behavior, test vectors expanded, CI matrix verifies all three platforms on every push. No new protocol features. No breaking changes.
Bugs fixed
Streaming detection too permissive
`agent.py` was detecting streaming via `hasattr(result, 'iter') and not isinstance(result, dict)`. That matches lists, tuples, strings, dataclasses, etc. A handler returning `['a','b']` was silently streamed. Narrowed to `inspect.isgenerator()`. Regression test added.
`async_server.py` significantly behind sync
The async path lacked all v0.2-v0.5 hardening: no size cap, no read timeout, no streaming RES_CHUNK support — would crash if any generator-returning dispatch hit it. Brought to parity. Same constructor args, same behavior, ASGI flavor.
`lak_channel.py` wiring bug
Bridge set `self._pact._server_dispatch = bridged_dispatch` but `PACTAgent.serve()` reads `self._dispatch`. The bridge was never actually wired. Fixed. Module docstring updated with known limitations.
Dead `_request_auto_grant` stub
Always returned `None`. Was the only consumer of `self.auto_grant`. Removed. Constructor parameter kept for back-compat (now silent no-op).
Public API expanded
`init.py` now exports the full surface (19 symbols) including `PACTMessage`, `build_req`, `build_res_chunk`, `send_message_streaming`, `verify_capability`. Pre-v0.5.1, streaming users had to reach into submodules.
Documentation caught up
| Doc | Update |
|---|---|
| README | Status v0.2.0 → v0.5.x. Two types → three. Test count → 141. Phase table → release table. Platform support table refreshed. |
| `spec/PACT_v1.md` | New §12 addendum covering all v0.2-v0.5 additions (RES_CHUNK, identity_doc, cap_envelope, stream, new fault codes, fail-closed verification, rotation continuity, cap envelope inline rules, durable state). Original §1-§11 untouched. |
| `docs/PACT_Specification.md` | "Two types" → "three types". v0.5 update note. |
| `pyproject.toml` | Development Status: 3-Alpha → 4-Beta. Python 3.11/3.12/3.13 classifiers. Crypto + Networking topic classifiers. |
Test vectors expanded
`tests/vectors/pact_v1_vectors.json`: 214 lines → 619 lines. Six new vector categories with deterministic bytes:
- Rotation event (sequence=1, signed by pre-rotated key)
- Attenuated capability (delegation chain length 1)
- REQ with `identity_doc` (TOFU handshake)
- REQ with `cap_envelope` (cross-machine A→B→C delegation)
- RES_CHUNK stream (3 chunks, last marked final)
- Error RES with v0.2+ standard fault code
Anyone implementing PACT in another language now has reproducible bytes for every wire-format addition. All 24 existing vector tests still pass.
CI matrix actually verifies platform claim
`.github/workflows/test.yml`: `ubuntu-latest` only → `ubuntu + macos + windows`. 9 jobs total (3 OS × 3 Python). `fail-fast: false` so per-platform regressions are visible.
This v0.5.1 PR was the first time CI ran on macOS or Windows. All 9 jobs passed on the first try:
| 3.11 | 3.12 | 3.13 | |
|---|---|---|---|
| Linux | ✅ 39s | ✅ 40s | ✅ 40s |
| Windows | ✅ 1m4s | ✅ 1m45s | ✅ 1m4s |
| macOS | ✅ 8m14s | ✅ 8m15s | ✅ 8m19s |
The README's three-platform claim is no longer asserted — it's verified on every push.
Test results
| v0.5.0 | v0.5.1 | |
|---|---|---|
| Tests passing | 140 | 141 |
| Documented xfails | 0 | 0 |
| CI platforms | Linux | Linux + macOS + Windows |
| Public exports | 6 | 19 |
| Test vector categories | 6 | 12 |
| Spec coverage | v0.1 | v0.1 + v0.2-v0.5 §12 addendum |
Install
```bash
pip install --upgrade pact-passport
```
Stats arc — full session, v0.1.3 → v0.5.1
| v0.1.3 (case study era) | v0.5.1 | |
|---|---|---|
| Tests | 118 + 1 xfail | 141 |
| Critical auth bypasses | 3 | 0 |
| Durability gaps | 1 | 0 |
| Wire-level delegation | broken | works |
| Streaming | not supported | supported |
| Open issues | 7 | 0 |
| Spec coverage | v0.1 only | v0.1 + addendum |
| Test vectors | 6 categories | 12 categories |
| CI platforms | 1 | 3 |
| Public exports | 6 | 19 |
| Releases | 1 | 8 (v0.1.3, v0.1.4, v0.2.0, v0.2.1, v0.3.0, v0.3.1, v0.4.0, v0.5.0, v0.5.1) |
v0.5.0 — streaming RES_CHUNK responses
Summary
Closes #11. PACT now supports streaming responses: handlers `yield` instead of `return`, dispatcher signs each yielded payload as a `RES_CHUNK`, HTTP layer streams them as NDJSON over chunked transfer encoding.
This was the last open issue. Zero open issues, zero documented xfails.
Quick example
```python
Server side
@agent.handle("ask_llm_stream")
def ask_llm_stream(payload):
for token in ollama_stream(payload["prompt"]):
yield {"chunk": token}
Client side
req = build_req(..., stream=True)
for chunk in send_message_streaming(url, req):
print(chunk["payload"]["chunk"], end="")
```
Wire format additions
`PACTMessage` gains three new optional fields:
| Field | Where | Purpose |
|---|---|---|
| `stream: bool` | REQ | Opt-in to streaming response |
| `chunk_seq: int` | RES_CHUNK | Monotonic sequence (0, 1, 2, ...) |
| `chunk_final: bool` | RES_CHUNK | Marks the terminal chunk |
Each chunk is a fully-formed signed PACTMessage with `type="RES_CHUNK"`. Tampering with any field — including chunk_seq or chunk_final — invalidates that chunk's signature without affecting others.
Idempotency under streaming
Cached value is now polymorphic:
- Dict → one-shot replay (existing)
- List of chunk dicts → streaming replay (new)
A retried streaming REQ returns the cached chunks WITHOUT re-running the handler. Test: `test_streaming_replay_returns_same_chunks`.
Receipts
One receipt per stream, written after the final chunk. `refs` include the original REQ id plus every chunk id. If the consumer disconnects mid-stream, the receipt records `outcome="failed"` with whatever chunks did make it.
Test results
| v0.4.0 | v0.5.0 | |
|---|---|---|
| Tests passing | 135 | 140 |
| Documented xfails | 0 | 0 |
| Open issues | 1 | 0 |
| CI | 3.11 / 3.12 / 3.13 green | 3.11 / 3.12 / 3.13 green |
5 new tests in `tests/integration/test_streaming.py`:
- Round-trip stream
- Per-chunk signature verification
- Tampered chunk detection
- Idempotent replay returns same chunks
- One-shot handler under stream=true still works
No breaking changes
Streaming is opt-in on both sides:
- Client requests it via `stream=True` on `build_req`
- Server delivers it when the handler yields (generator return)
- Either side opting out → falls back to one-shot RES (existing behavior)
Stats arc — full session
The complete v0.1.3 → v0.5.0 progression in two days:
| v0.1.3 (case study era) | v0.5.0 | |
|---|---|---|
| Tests passing | 118 + 1 xfail | 140 |
| Critical auth bypasses | 3 | 0 |
| Durability gaps | 1 | 0 |
| Wire-level delegation | broken | works |
| Streaming | not supported | supported |
| Documented xfails | 1 | 0 |
| Open issues | 7 | 0 |
Install
```bash
pip install --upgrade pact-passport
```
What's next
The actionable v0.x backlog is empty. Possible next moves:
- Patrick integration — wire the local agent stack onto PACT (was out of scope; now safe and unblocked)
- Mini-cluster integration — replace the Mac→NUC SSH path with PACT for the actual Monte Carlo flow
- Spec doc audit — `spec/PACT_v1.md` was written for v0.1; needs updating to reflect v0.2-v0.5 additions (identity_doc, cap_envelope, RES_CHUNK)
v0.4.0 — cap envelope inline, three-agent delegation works over the wire
Summary
Closes #10. The Macaroons-style attenuation that PACT advertised in v0.1 finally works end-to-end across the wire. A→B→C delegation: A issues to B, B attenuates and grants to C, C presents to A. A verifies the chain.
What changed
`PACTMessage` gains an optional `cap_envelope: dict | None` field. When the receiver doesn't have the cap_id locally and `cap_envelope` is present, the receiver:
- Verifies the envelope's `cap_id` matches `msg.cap_id`
- Verifies the cap was issued by us (root issuer check)
- Gathers required pubkeys from peer cache (delegators in chain + holder)
- Calls `verify_capability` with full `known_keys` (issue #8 fail-closed applies)
- Caches the cap locally for future use
`build_req` accepts an optional `cap_envelope` parameter. Passing the full `cap.to_dict()` is the one-line client-side change.
Behavior change
Stricter: `cap_id` claimed but neither in local store nor provided as envelope = explicit `cap_unknown` error.
Pre-v0.4.0: silently fell through to action-name dispatch without auth (a residual bypass path that combined with the v0.1 auto-grant problem).
Tests
| v0.3.1 | v0.4.0 | |
|---|---|---|
| Tests passing | 131 | 135 |
| Documented xfails | 0 | 0 |
| CI | 3.11 / 3.12 / 3.13 green | 3.11 / 3.12 / 3.13 green |
4 new tests in `tests/integration/test_cap_envelope.py`:
- `test_three_agent_delegation_over_the_wire` — the killer scenario
- `test_unknown_cap_without_envelope_rejected` — closes the silent fallthrough
- `test_envelope_with_mismatched_cap_id_rejected` — anti-confusion
- `test_envelope_with_wrong_issuer_rejected` — anti-impersonation
Stats arc since the v0.1.3 case study
| v0.1.3 | v0.4.0 | |
|---|---|---|
| Tests passing | 118 + 1 xfail | 135 |
| Critical auth bypasses | 3 | 0 |
| Durability gaps | 1 | 0 |
| Wire-level delegation | broken | works |
| Documented xfails | 1 | 0 |
Install
```bash
pip install --upgrade pact-passport
```
What's left
- #11 streaming RES_CHUNK design — deferred. Not a bug; a feature, ~16 hours of protocol-level work for v0.5+.
Every other open issue is closed.
v0.3.1 — rotation peer refresh (XFAIL killed)
Summary
Closes #4. Communication now survives key rotation. The XFAIL test that's been the most prominent unfixed bug in the case study is gone.
What changed
When `verify_message` fails AND the REQ includes a fresh `identity_doc`, the receiver now attempts a KERI-style continuity check:
```
hash(new_doc.public_key) == cached_doc.next_key_digest
```
If the proof holds, the cache is refreshed and verification retried. If it doesn't, rejected as `invalid_signature` (treat as attack — only the original key holder could have committed to the right `next_key_digest`).
Client contract
The rotated party MUST include their fresh `identity_doc` on the first post-rotation REQ:
```python
req = build_req(
from_private_key=identity._private_key,
from_id=identity.agent_id,
to_id=peer_id,
intent="task",
payload={"action": "ping"},
identity_doc=identity.to_identity_document(), # required after rotation
)
```
Subsequent REQs to the same peer don't need `identity_doc` — the cache is now fresh.
Test results
| v0.3.0 | v0.3.1 | |
|---|---|---|
| Tests passing | 129 | 131 |
| Documented xfails | 1 | 0 |
| CI | 3.11 / 3.12 / 3.13 green | 3.11 / 3.12 / 3.13 green |
Stats arc since v0.1.3
| v0.1.3 (case study era) | v0.3.1 | |
|---|---|---|
| Tests | 118 + 1 xfail | 131 |
| Critical auth bypasses | 3 | 0 |
| Durability gaps | 1 | 0 |
| Documented xfails | 1 | 0 |
Install
```bash
pip install --upgrade pact-passport
```