Skip to content

Releases: dcp-ai-protocol/dcp-ai

DCP-AI v2.8.1 — wire canonicalization_profile across the 4 SDKs

27 Apr 02:12

Choose a tag to compare

Summary

v2.8.0 declared the canonicalization_profile field in the bundle manifest schema and described it normatively in spec/CANONICALIZATION_PROFILE.md § 4, but no SDK actually wrote or read the field — the typed BundleManifest structures shipped without it, and verifiers ignored it. v2.8.1 closes that gap end to end in TypeScript, Python, Go, and Rust, and fills the three remaining holes in the spec documents.

SDK wiring

  • TypeScript (@dcp-ai/sdk 2.1.0 → 2.1.1)BundleManifest interface in sdks/typescript/src/types/v2.ts gains canonicalization_profile?: 'dcp-jcs-v1'. BundleBuilderV2.build() in sdks/typescript/src/bundle/builder-v2.ts now emits canonicalization_profile: \"dcp-jcs-v1\" on every produced manifest. verifyV2Bundle in sdks/typescript/src/core/verify-v2.ts accepts an absent field (assumes dcp-jcs-v1), accepts \"dcp-jcs-v1\" explicitly, and rejects any other value with \"Unknown canonicalization_profile: <value>\".

  • Python (dcp-ai 2.8.0 → 2.8.1)BundleManifest in sdks/python/dcp_ai/v2/models.py gains canonicalization_profile: Literal[\"dcp-jcs-v1\"] | None = \"dcp-jcs-v1\". Pydantic enforces unknown-value rejection at parse time; absence triggers the default value.

  • Go (sdks/go/v2.8.0v2.8.1)BundleManifest struct in sdks/go/dcp/v2/types.go gains CanonicalizationProfile string with json:\"canonicalization_profile,omitempty\". BuildBundleV2 in sdks/go/dcp/v2/bundle_builder.go sets it to \"dcp-jcs-v1\" on every produced manifest. VerifySignedBundleV2 in sdks/go/dcp/v2/verify_bundle.go rejects bundles whose manifest carries any other value.

  • Rust (dcp-ai crates.io 2.8.0 → 2.8.1)BundleManifest in sdks/rust/src/v2/types.rs gains pub canonicalization_profile: Option<String> with #[serde(skip_serializing_if = \"Option::is_none\")]. wasm_build_bundle in sdks/rust/src/lib.rs sets \"canonicalization_profile\": \"dcp-jcs-v1\" on the manifest it emits.

Spec & profile document

  • spec/DCP-AI-v2.0.md § 9 (Bundle Manifest) now lists canonicalization_profile alongside the other manifest fields, with an inline note that an absent field MUST be assumed equivalent to \"dcp-jcs-v1\".

  • spec/CANONICALIZATION_PROFILE.md § 2 — Rule 6 (undefined / null handling) is rewritten as a cross-language table that explicitly maps TypeScript, Python, Go, and Rust host values onto the JSON wire format for both object slots and array slots. The previous prose-only rule left Python None, Go nil, and Rust Option::None underspecified for verifiers cross-checking foreign producers.

  • spec/CANONICALIZATION_PROFILE.md § 2 — new Rule 9 makes Unicode normalization (NFC, NFD, NFKC, NFKD) explicitly out of scope: the canonicalizer emits strings byte-for-byte as the host parser delivers them, and a future profile (dcp-jcs-v2 or later) MAY pin a normalization form. RFC 8259 § 8.1 is referenced as the conventional choice (NFC) for application-layer normalisation.

  • Rule 9 also pins the rationale for not shipping NaN / ±Infinity interop fixtures: those values are not part of RFC 8259 wire format, so most parsers reject them before the canonicalizer is reached. Rejection is verified by per-SDK unit tests rather than shared fixtures.

Behavioural compatibility

The new field is additive and optional in serialization:

  • A v2.8.1-produced bundle carries canonicalization_profile: \"dcp-jcs-v1\" on its manifest.
  • A bundle produced by v2.8.0 or earlier has no canonicalization_profile field. v2.8.1 verifiers accept it: per spec/CANONICALIZATION_PROFILE.md § 4, an absent field MUST be assumed equivalent to \"dcp-jcs-v1\".
  • A bundle that carries canonicalization_profile with an unknown value (e.g. \"dcp-jcs-v2\") is rejected by every v2.8.1 SDK. This is the forward-compatibility hook for future profiles: a verifier that has not been updated to a new profile MUST refuse it rather than silently accept a manifest it cannot validate.

The bundle-level signature covers canonical(manifest), so adding the field changes the manifest hash of any newly-produced bundle relative to one that would have been produced under v2.8.0. Bundles produced under v2.8.0 and earlier, and their stored signatures, remain valid under v2.8.1 verifiers.

Tests

  • Conformance (root): `DCP-AI CONFORMANCE PASS (V1 + V2)`
  • TypeScript: 461 / 461
  • Python: 236 / 236
  • Go: 4 packages OK
  • Rust: 181 tests across 10 binaries

Versions bumped

  • `dcp-ai` (PyPI) 2.8.0 → 2.8.1
  • `dcp-ai` (crates.io) 2.8.0 → 2.8.1
  • `github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2` v2.8.0 → v2.8.1
  • `@dcp-ai/sdk` (npm) 2.1.0 → 2.1.1 — first TypeScript code change since 2.1.0 (the 2.8.0 release was a docstring-only edit)

Registries

After publishing the artefacts (PyPI, crates.io, npm) and the Go module proxy picks up the tag, install with:

```
pip install dcp-ai==2.8.1
cargo add dcp-ai@2.8.1
go get github.com/dcp-ai-protocol/dcp-ai/sdks/go/v2@v2.8.1
npm install @dcp-ai/sdk@2.1.1
```

DCP-AI v2.8.0 — canonicalization profile dcp-jcs-v1 (spec)

27 Apr 02:10

Choose a tag to compare

What shipped

  • New normative document spec/CANONICALIZATION_PROFILE.md — formalises profile dcp-jcs-v1 with eight numbered rules, an edge-case acceptance table, an undefined-handling contract documenting the TypeScript-only asymmetry (omit in objects, serialize as null in arrays), and a versioning policy for future profiles.
  • Schema field declared in schemas/v2/bundle_manifest.schema.json: optional canonicalization_profile: "dcp-jcs-v1" on bundle manifests. Per the spec, verifiers that encounter a bundle without the field MUST assume dcp-jcs-v1. Future profiles will make the field required.
  • Spec § 15 rewritten to reference the full profile document.
  • 14 interop edge-case vectors added to tests/interop/v2/interop_vectors.json under canonicalization.edge_cases. Each SDK's interop test loads the same block and asserts byte-identical output (or matching error) for every vector.

SDK changes

  • Python dcp-ai 2.7.1 → 2.8.0assert_no_floats now accepts floats whose value is a finite integer (1.0, 1e2, 1.00); they are normalised to integer form before json.dumps. Non-integer floats (0.1), NaN, and infinities remain rejected.
  • Rust dcp-ai 2.7.0 → 2.8.0assert_no_floats now accepts Number(f64) whose fract() is zero. New format_number helper emits integer-valued floats without decimal point or exponent, matching the other SDKs byte-for-byte.
  • Go sdks/go/v2.7.0v2.8.0 — behaviour already aligned (v != math.Trunc(v) check). Version-bump only.
  • TypeScript @dcp-ai/sdk stays 2.1.0 — behaviour already aligned. Docstring in sdks/typescript/src/core/canonicalize.ts formalises the undefined-vs-null asymmetry.

What is NOT in this release

The schema field is declared but the typed BundleManifest structures in the four SDKs do not expose it, the BundleBuilder types do not emit it by default, and verifiers do not route on it. Code-side wiring of the field across the four SDKs lands in v2.8.1 (see release notes there).

Behavioural compatibility

Bundles produced by previous releases remain valid. The change is additive for SDKs that previously rejected 1.0 / 1.00 / 1e2: inputs that used to throw TypeError / Err now succeed and produce the same canonical output the other SDKs were already producing. No input that previously canonicalised successfully canonicalises differently in 2.8.0.

Registries

DCP-AI Python 2.7.1

23 Apr 17:16

Choose a tag to compare

Fix: add cryptography as required dep for A2A session encryption. Supersedes the broken 2.7.0 Python wheel. Rust and Go unaffected — they stay at 2.7.0.

DCP-AI v2.7.0

23 Apr 17:00

Choose a tag to compare

Cross-SDK behavioral parity across TS, Python, Rust, Go. See CHANGELOG.md for details.

v2.6.0

23 Apr 17:04

Choose a tag to compare

Blinded RPR, multi-party auth, advisory helpers. See CHANGELOG.md.

v2.5.0

23 Apr 17:04

Choose a tag to compare

A2A protocol parity. See CHANGELOG.md.

v2.4.0

23 Apr 17:03

Choose a tag to compare

Production hardening primitives. See CHANGELOG.md.

v2.3.0

23 Apr 17:03

Choose a tag to compare

Dispute + Rights + Delegation parity. See CHANGELOG.md.

v2.2.0

23 Apr 17:03

Choose a tag to compare

Lifecycle + Succession parity. See CHANGELOG.md.

DCP-AI v2.1.0 — observability release

22 Apr 08:17

Choose a tag to compare

DCP-AI v2.1.0 — observability release (all four SDKs)

This release completes the OpenTelemetry integration across the four core DCP-AI SDKs. All four now speak OTLP directly — no hand-rolled bridges, same metric names, same counter names, same span shapes.

What shipped

SDK Package Version OTLP enable-switch
TypeScript @dcp-ai/sdk 2.1.0 exporterType: 'otlp' + install @opentelemetry/* peers
Python dcp-ai 2.1.0 pip install 'dcp-ai[otlp]'
Rust dcp-ai 2.1.0 features = ["otlp"]
Go sdks/go/v2 sdks/go/v2.1.0 go build -tags otlp

Metric surface (identical across all four SDKs)

  • Histograms: dcp.sign.latency_ms, dcp.verify.latency_ms, dcp.kem.latency_ms, dcp.checkpoint.latency_ms, dcp.bundle_verify.latency_ms
  • Counters: dcp.signatures.created, dcp.signatures.verified, dcp.bundles.verified, dcp.a2a.sessions, dcp.a2a.messages, dcp.errors
  • Span lifecycle: dcp.sign, dcp.verify, plus any custom spans you start via the SDK API
  • Resource attributes: service.name, service.version, sdk.language

How to enable (pick any backend — Jaeger, Grafana Cloud, Honeycomb, Datadog, self-hosted Collector)

See docs/OBSERVABILITY.md for the three copy-paste recipes (local Jaeger via Docker, Grafana Cloud OTLP, Honeycomb) and one-page install + wire-in instructions for each SDK.

Backward compatibility

  • Fully backward compatible. Default installs are unchanged.
  • Telemetry is disabled by default in every SDK — the recorders short-circuit and zero memory is consumed until you call init(enabled=True).
  • If you opt into ExporterType::Otlp / exporterType: 'otlp' / exporter_type="otlp" / ExporterOTLP without the optional OTel dependencies installed, every SDK surfaces the missing-deps message as an error event via onEvent / on_event / OnEvent instead of crashing the application.
  • The WASM SDK remains at 2.0.x; browser telemetry has different transport requirements and will ship in a later minor.

Behind the scenes (for future contributors)

Every SDK implements the same primitives:

  1. Disabled-by-default Telemetry singleton with a thread-safe event bus.
  2. Recorder methods (record_sign_latency, record_verify_latency, etc.) that append to in-memory histograms and emit metric / counter events.
  3. on_event(listener) fan-out so custom dashboards and CLI tools can subscribe without needing OTel.
  4. get_metrics_summary() for synchronous p50/p95/p99 snapshots.
  5. Lazy OTLP bridge loaded only when the exporter is enabled and its optional peer deps are present.

Test coverage for the observability layer:

  • TypeScript: 460/460 vitest cases pass
  • Python: 15 new pytest cases (145/145 total)
  • Rust: 10 unit tests (cargo test green with and without --features otlp)
  • Go: 10 unit + 2 integration tests (go test ./... and go test -tags otlp ./... both green)