From ae916174ed745defcd7083ce08e837ab361cd1bc Mon Sep 17 00:00:00 2001 From: Tymofii Pidlisnyi Date: Thu, 20 Aug 2026 08:56:21 -0700 Subject: [PATCH] chore(release): 2.11.0 RFC 8785 integer domain alignment in canonicalize_jcs and the unsafe-integer write policy at signing and new-write boundaries (#6). Canonical JCS bytes move for integers whose decimal spelling differs from the binary64 serialization of the same value, which is why the minor version moves. Signed-off-by: Tymofii Pidlisnyi --- CHANGELOG.md | 11 +++++++++++ README.md | 4 ++-- pyproject.toml | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4278c98..77ae212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 2.11.0 (2026-08-20) + +### Fixed / Security + +- **`canonicalize_jcs` serializes `int` through the RFC 8785 number domain, so canonical bytes agree with the TypeScript and Go SDKs.** RFC 8785 section 3.2.2.3 defines the JCS number domain as IEEE 754 binary64 serialized under ECMAScript `Number::toString`. Python's `int` is arbitrary precision and the previous code emitted it verbatim, keeping a decimal spelling the double does not have: 2^60 emitted as `1152921504606846976` where the binary64 serialization is `1152921504606847000`. Where those two spellings differ, a digest or signature computed over `canonicalize_jcs` output disagreed with the same object canonicalized by the TypeScript or Go SDK, both of which already emitted the binary64 form, so such an artifact verified in this SDK and failed for a peer that recomputed the bytes through the RFC 8785 number domain. The `int` branch now widens to binary64 first and takes the same path a `float` takes. An integer beyond the binary64 range raises `JCSCanonicalizationError` with reason `number_out_of_double_range`, since RFC 8785 defines no representation for it. The generic `canonicalize` is untouched. + +### Behavior change + +- **Canonical JCS bytes move for integers whose decimal spelling differs from the binary64 serialization of the same value, which is why the minor version moves rather than the patch.** Not every large integer is affected. `9007199254740992` and `9007199254740994` are unchanged, while `9007199254740993` now emits `9007199254740992`, 2^60 emits `1152921504606847000` and 2^68 emits `295147905179352830000`. A signature made by 2.10.0 or earlier over an affected value does not verify against bytes recomputed by 2.11.0. Those artifacts were already unverifiable outside Python for the reason above, so this release makes the failure visible in one place instead of leaving it to the peer. The pinned canonicalization baselines are unchanged and the generic `canonicalize` keeps its previous output. +- **Signing and new-write boundaries refuse integer-valued numbers outside the interoperable IEEE 754 range.** RFC 7493 section 2.2 says an I-JSON sender cannot expect a receiver to treat an integer whose absolute value exceeds 9007199254740991 as an exact value, and recommends encoding such a value as a JSON string. A new-write value carrying such an integer now raises `UnsafeIntegerError`, a `ValueError` subclass carrying the JSON path of the offending member. Only integer-valued numbers are bounded. Verification and recompute paths keep calling the unrestricted canonicalizer, so this rule refuses nothing on the verification side: where a pre-2.11.0 artifact stops verifying, the cause is the canonicalization change above and not this rule. The guard is internal: no write-policy name is exported from `agent_passport`, and `write_policy.py` ships in the wheel for internal use. One limit worth knowing at the call site: a documented set of exported helpers both mint and re-derive a value through the same function and stay unrestricted, so that re-derivation of a value minted before the rule keeps working. Minting an unsafe integer through one of those helpers is not covered. Scope, the call-site inventory and the proofs are in #6. + ## 2.10.0 (2026-07-26) ### Added diff --git a/README.md b/README.md index c63ae81..dd62675 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ pip install agent-passport-system ``` -> **Current stable**: `2.10.0`, installed by default with `pip install agent-passport-system`. The 2.4 line adds the Wave 1 accountability primitives (ActionReceipt, AuthorityBoundaryReceipt, CustodyReceipt, ContestabilityReceipt, APSBundle), Cognitive Attestation (Paper 4), and Instruction Provenance Receipts (v0.2). It also adds evidentiary type safety: a claim and evidence registry, a claim verifier, and a contestation cascade. All primitives are tested for byte-identical canonical JSON against the TypeScript SDK npm 3.1.0 fixtures. +> **Current stable**: `2.11.0`, installed by default with `pip install agent-passport-system`. The 2.4 line adds the Wave 1 accountability primitives (ActionReceipt, AuthorityBoundaryReceipt, CustodyReceipt, ContestabilityReceipt, APSBundle), Cognitive Attestation (Paper 4), and Instruction Provenance Receipts (v0.2). It also adds evidentiary type safety: a claim and evidence registry, a claim verifier, and a contestation cascade. All primitives are tested for byte-identical canonical JSON against the TypeScript SDK npm 3.1.0 fixtures. ## Quick Start @@ -110,7 +110,7 @@ This Python SDK implements all 8 Agent Passport Protocol layers: 7. **Integration Wiring**: Cross-layer bridges (commerce+intent, coordination+agora) 8. **Agentic Commerce**: 4-gate checkout, human approval, spend limits -Cross-language parity with the [TypeScript SDK](https://www.npmjs.com/package/agent-passport-system) at npm v4.3.0. Python SDK 2.10.0 ships the full Wave 1 surface: ActionReceipt, AuthorityBoundaryReceipt, CustodyReceipt, ContestabilityReceipt, APSBundle (with balanced Merkle commitment), Cognitive Attestation (Paper 4: three-stage verification, typed dispute primitives), and Instruction Provenance Receipts v0.2 (path canonicalization, context-root binding, action-time recompute). The four evidentiary type safety primitives also ship in 2.10.0. All surfaces are tested against TS-issued fixtures for byte-identical canonical JSON. Cross-language signature verification covers every signed primitive in the SDK. Also available via the [MCP server](https://mcp.aeoess.com/sse). +Cross-language parity with the [TypeScript SDK](https://www.npmjs.com/package/agent-passport-system) at npm v4.4.0. Python SDK 2.11.0 ships the full Wave 1 surface: ActionReceipt, AuthorityBoundaryReceipt, CustodyReceipt, ContestabilityReceipt, APSBundle (with balanced Merkle commitment), Cognitive Attestation (Paper 4: three-stage verification, typed dispute primitives), and Instruction Provenance Receipts v0.2 (path canonicalization, context-root binding, action-time recompute). The four evidentiary type safety primitives also ship in 2.11.0. All surfaces are tested against TS-issued fixtures for byte-identical canonical JSON. Cross-language signature verification covers every signed primitive in the SDK. Also available via the [MCP server](https://mcp.aeoess.com/sse). ## Links diff --git a/pyproject.toml b/pyproject.toml index 144a5dc..ec1b258 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "agent-passport-system" -version = "2.10.0" +version = "2.11.0" description = "Python SDK for the Agent Passport System. Identity, delegation, governance, data source registration, training attribution, per-period attribution settlement, mutual authentication, evidentiary type safety, Wave 1 accountability primitives (action, authority-boundary, custody, contestability, bundle), Cognitive Attestation, Instruction Provenance Receipts. Cross-language parity with agent-passport-system npm v3.3.0 tested against byte-identical canonical JSON fixtures." readme = "README.md" license = "Apache-2.0"