Skip to content

Releases: bene-art/pact-passport

v0.8.2

Choose a tag to compare

@github-actions github-actions released this 21 Jun 19:34

[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 _dispatch by
    message type, before intent dispatch.
  • _handle_initiator_ack dispatch handler (agent.py). Verifies the
    outer envelope sig, parses the {task_ref, initiator_ack_signature}
    payload, looks up the unilateral receipt by task_ref, verifies the
    ack signature against the receipt's canonical bytes minus the
    initiator_ack_signature field (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 inline identity_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_ACK message type (spec §18.6). 4 of the remaining 9 pact_*
    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

Choose a tag to compare

@github-actions github-actions released this 21 Jun 19:36

[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_req plumbed into _handle_task dispatch pipeline (spec §18.2).
    New _step_audit_req runs after _step_verify_sender and before
    _step_verify_capability — cheap-first ordering. Failures short-circuit
    with the matching v1.4 pact_* fault code.
  • audit_req plumbed into _handle_visa_request (symmetric to task flow).
    Same migration-window semantics.
  • HTTP status mapping at transport layer (spec §18.3). _status_for_response
    in transport/server.py reads fault.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_strict constructor kwarg on PACTAgent. Default False
    during the §18.7 90-day migration window — a missing audit_context is
    accepted with DeprecationWarning so v0.7 senders interoperate. Present-
    but-malformed audit_context is 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_*_caveat factories, 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] — runs audit_receipt on a
      stored receipt (looked up by id or task_ref prefix) or stdin JSON.
      Prints AuditResult and exits non-zero on failure.
    • pact receipts --bilateral-only — filters out unilateral receipts.
    • pact ask --audit-purpose <tag> — sets audit_context.purpose on
      outgoing REQ (default: "task").
  • PACTAgent.ask(..., audit_purpose: str = "task") kwarg flows the CLI
    flag through to build_req.

Changed

  • audit.py: audience_hint MAY be empty (visa-request broadcast form
    where the sender doesn't know the gatekeeper's agent_id pre-visa). Other
    required audit_context keys MUST still be non-empty strings.
  • tests/integration/test_v_tier_v1_v7.py helper _build_task_with_visa
    updated to include audit_context (was bypassing build_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_req enforcement (REQ + V-tier),
    audit-side pact_* fault codes + HTTP mapping, policy + audit public
    API exposure, CLI v0.8 surface. 4 of 5 dispatch gaps closed.
  • v0.8.2 (planned) — bilateral receipt wire round-trip via new
    INITIATOR_ACK message 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

Choose a tag to compare

@github-actions github-actions released this 21 Jun 12:53

[0.8.0]

See CHANGELOG.md for details.

v0.7.1

Choose a tag to compare

@github-actions github-actions released this 12 Jun 15:49

[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 Xfrom pact_passport import X).
  • Overview LOC line: src/pact/src/pact_passport/.
  • Primitives table Source column: 5 rows updated from src/pact/X.py to src/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

Choose a tag to compare

@github-actions github-actions released this 12 Jun 14:20

[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: pactpact_passport. The previous import path silently shadowed pact-python — the widely-used Pact Foundation contract-testing library, which also installs as the pact module. Any environment with both packages installed had unpredictable behavior depending on sys.path order. 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-passport works as before).
    • CLI binary: pact (no conflict — pact-python ships pact-broker, pact-stub-service, etc., not a bare pact).
    • 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.

Changed

  • Post-quantum claim softened in README. Architecture box and Non-goals previously said crypto.py was "a single-file seam for the eventual migration" / "post-quantum swap = one file change". Both overclaimed — a real PQ migration changes agent_id derivation (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

Choose a tag to compare

@github-actions github-actions released this 11 Jun 16:43

[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 at transport/server.py:171 enumerated POSIX exception types only: (BrokenPipeError, ConnectionResetError). On Windows, consumer disconnect raises ConnectionAbortedError (WinError 10053), which escaped the catch and bypassed chunks_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 failed test_c3_stage1_partition_writes_cancelled_receipt with expected exactly 1 cancelled receipt; got 0). Fixed by widening the catch to the parent class ConnectionError, which covers BrokenPipeError + ConnectionResetError + ConnectionAbortedError and any future platform-specific subclass. Regression test in tests/test_server.py::test_send_stream_catches_all_connection_error_subclasses asserts 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 + --capabilities flags to CLI Commands table; added visa.py to 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

Choose a tag to compare

@bene-art bene-art released this 04 May 01:58
3f4d012

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

Choose a tag to compare

@bene-art bene-art released this 03 May 19:53
f739a4d

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

Choose a tag to compare

@bene-art bene-art released this 03 May 18:47
5f3febf

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:

  1. Verifies the envelope's `cap_id` matches `msg.cap_id`
  2. Verifies the cap was issued by us (root issuer check)
  3. Gathers required pubkeys from peer cache (delegators in chain + holder)
  4. Calls `verify_capability` with full `known_keys` (issue #8 fail-closed applies)
  5. 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)

Choose a tag to compare

@bene-art bene-art released this 03 May 18:38
9afedac

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
```

Remaining backlog

  • #10 cap envelope inline — wire-format change, makes delegation actually propagate (v0.4.0)
  • #11 streaming RES_CHUNK design — deferred to v0.5+