Skip to content

Releases

v0.3.2 - review fixes, hashing scheme V3, and the fresh-build fix

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 23 Oct 16:24
5caaebc

Version 0.3.2: the review fixes for #7–#12, and the fix for 0.3.1 failing a fresh build. Additive to the public API; cargo update brings it to every project on 0.3.

The review findings filed against 0.3.1 by the C#/.NET SDK's maintainer
(#7–#12), fixed. Two behaviour changes are listed first because they can turn
a call that succeeded into one that is refused. Both refuse what should not
have been accepted, and the public API only grows, so they ship as a patch:
cargo update brings them, and the fix for 0.3.1's broken fresh build, to
every project on 0.3.

Changed — behaviour

  • A created event must name its package to decode (#7).
    Template::from_created_event and from_json_created_event now compare the
    event's package name with the template's, as well as its module and
    entity; the package id is still not compared, so an upgraded version
    decodes. An event from another package that defines the same Module:Entity
    with a payload of the same shape used to decode as this template; it is now
    refused. An event with no template id, or with a package id and no package
    name, is refused rather than decoded on trust. Events a participant returns
    carry both; a hand-built event in a test may need package_name set.
  • A prepared transaction hashed under a scheme the SDK cannot check is
    refused unless the caller opts in
    (#10). Under V2 and V3 the hash is
    recomputed and held to the transaction; any other scheme (V4) used to be
    signed as the participant returned it with only a warning, and now needs
    Prepare::trust_participant_hash().

Added

  • Hashing scheme V3 (#10): canton_ledger::hashing::hash_prepared_transaction_v3
    and hash_prepared_transaction(prepared, version), byte for byte as Canton
    3.5.17 builds V3 (no encoding-version bytes; keys on create, fetch and
    exercise nodes; the by-key flag; lookup-by-key nodes; the metadata's maximum
    record time), checked against Canton's own V3 vectors. Interactive submission
    recomputes the hash under V3 as it does under V2, so a transaction that uses
    contract keys is no longer signed on the participant's word. Live: an
    external party creates a keyed contract and exercises it by key under V3.
  • Point reads and pruning points on both transports (#12):
    update_by_offset, update_by_id (and _with forms taking an
    UpdatesRequest's format) and latest_pruned_offsets on CantonClient and
    JsonClient. "No such update" is Ok(None). Error::pruned_earliest_offset
    names the earliest offset the participant can still serve when a read starts
    below its pruning point; a resumable stream yields that refusal rather than
    moving forward on its own.
  • Updates as SDK types (#11): canton_ledger::update, the update envelope
    (Update, Transaction, created, archived and exercised events,
    Reassignment, TopologyTransaction, OffsetCheckpoint) built from either
    transport, with payloads decoded by the generated type
    (CreatedEvent::decode) and the wire form still reachable.
    CantonClient::updates_typed_with, updates_resumable_typed_with and
    JsonClient::updates_typed_with return them. At 1.0 these take the plain
    names and the proto-typed reads move to *_raw (ADR-0010 §4).
  • A keyed Daml-LF 2.3 DAR under CI (#8): testdata/contract-keys, pinned
    by commit and checksum. The LF oracle runs on it, the codegen compile test
    builds bindings from it and an exercise-by-key command from them, and the
    commands__contract_keys conformance row generates from it.
  • v2_allocate runs the receiver's side of a leg (CANTON_TOKEN_SIDE=receiver),
    so a V2 settlement between three different parties runs from the examples.
  • The live suites run nightly and on demand against a disposable LocalNet with
    OIDC (#9, contributed by the C#/.NET SDK's maintainer).

Fixed

  • canton-lf 0.3.x does not build on a fresh install. zip 2.6, inside the
    2 range the crate accepts, added a type parameter to ZipFile, which
    canton-lf named; the workspace lockfile held zip at 2.4, so CI stayed green
    while cargo add canton-lf, cargo add canton-codegen and
    cargo install canton-codegen-cli (without --locked) failed to compile.
    The entry is now read through std::io::Read, which builds on every 2.x, the
    lockfile moves to zip 2.6.1, and a new CI job builds the workspace with every
    dependency at its newest compatible version.
  • The lockfile, which canton-codegen-cli installs with under --locked,
    moves past the RustSec advisories published since 0.3.1: rustls 0.23.46,
    rustls-webpki 0.103.16, webpki-roots 1.0.11, tokio 1.54.1, ctutils 0.4.5.
    Three advisories against zip (RUSTSEC-2026-0367, -0368, -0369) are fixed
    only in zip 9 and concern its extraction API, which canton-lf does not
    call: it reads each DAR entry into memory under a size cap. Moving to zip 9
    changes the error type canton_lf::DarError::Zip carries, so it waits for
    0.4; deny.toml records the three with that reason.
  • The Prepared and Prepare::with_hashing_scheme_version docs still said
    the SDK did not recompute the hash and that the scheme defaulted to the
    participant's; both were stale since 0.3.1 (#10).

v0.3.1 - pre-audit security fixes and Daml-LF 2.3

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 26 Sep 22:16
a32f6f3

Version 0.3.1: the fixes from an internal security review of 0.3.0, run ahead of the independent audit, plus Daml-LF 2.3.

Fixes from an internal security review of 0.3.0, run ahead of the independent
audit. No new features; every change closes a way for a hostile participant,
registry, token endpoint or DAR to make the SDK do something its caller did
not ask for. Additive to the public API; behaviour changes are listed.

Fixed — signing and onboarding

  • canton-ledger: the hash of a prepared transaction is recomputed, not
    taken from the participant.
    Under hashing scheme V2 (the default, and now
    requested explicitly) the new canton_ledger::hashing module rebuilds the
    hash from the returned transaction and metadata, byte for byte as Canton
    does (checked against Canton's own test vectors), and prepare_submission
    refuses a response whose hash is of something else. A response under an
    unknown scheme, a scheme other than the one requested, or with a hash that
    is not 32 bytes is refused too. A caller that asks for another scheme
    explicitly signs the participant's hash as returned, with a warning.
  • canton-admin: the external-party onboarding set is read before it is
    signed for.
    The multi-hash is recomputed over the returned topology
    transactions, each transaction is decoded, and only the key's own namespace
    delegation, a key mapping to that key, and hosting with confirmation or
    observation rights are accepted; a participant that slips in a second host
    with submission rights, a delegation to another key, or any other mapping
    is refused. allocate_external_party refuses a participant that allocated a
    party other than the one signed for.

Fixed — transport and retries

  • canton-auth, canton-ledger, canton-token: HTTP redirects are no
    longer followed.
    The token client refuses a 3xx from the token endpoint
    instead of replaying the client secret at the redirect target; the JSON
    Ledger API client and the registry client answer a 3xx as the status it is,
    unretried, so a bearer token or a command body is never re-sent elsewhere.
  • HTTP response bodies are read only up to a bound on all three lanes: the
    JSON client up to its decoding limit (with_max_decoding_message_size), the
    token client up to 64 KiB, the registry client up to 64 MiB; error bodies
    are kept up to 64 KiB. The bound holds without a Content-Length.
  • canton-core: a server-recommended retry delay is capped at
    MAX_SERVER_DELAY (60 s, logged when capped), a spelled retryInfo is
    clamped after parsing, and the jitter arithmetic can no longer panic on a
    hostile value. A RetryInfo detail on its own no longer makes a permanent
    gRPC code or a permanent HTTP status retriable.
  • canton-ledger: a retried JSON submission counts as de-duplicated only
    on DUPLICATE_COMMAND or ALREADY_EXISTS
    , not on any HTTP 409 (which is
    ABORTED as often as not on Canton's JSON lane) or on a body that merely
    mentions the word. Two backpressure refusals no longer come back as Ok.
  • canton-ledger, canton-admin: a party listing that cycles page tokens
    or never ends is reported as incomplete
    (MAX_LIST_PAGES) instead of
    followed until memory runs out.

Fixed — codegen

  • canton-codegen: a Daml name cannot inject into generated docs. Lowering
    refuses a choice, field or package name carrying a control character or
    */, and the emitter keeps every doc attribute on one line, so a DAR cannot
    plant a Markdown fence (and with it a doctest that cargo test would run)
    in the generated crate.

Added — Daml-LF 2.3

  • canton-lf decodes LF 2.3, the version Canton 3.5.x SDKs emit and a
    DevNet participant already vets (Digital Asset's utility apps,
    daml-stdlib 3.5.1). The vendored archive schema is now Canton 3.5.17's,
    trimmed as before (expression bodies stay opaque), and the conformance
    oracle runs against the 3.5.17 JVM reader (daml-lf-archive_2.13).
    Bindings generated from a 290-package DevNet DAR at LF 2.3 compile; the
    committed bindings are unchanged. In the generated types pb::daml_lf_dev
    is now pb::daml_lf and a Type referencing an interned type spells it
    InternedType, following the schema. canton_lf::pb is now
    #[doc(hidden)]: generated schema types, exempt from SemVer as
    canton-proto's are (ADR-0002), reachable but not part of the documented
    surface; CI's semver guard skips canton-lf for this one release and takes
    it back with 0.3.1 as the baseline.
  • fetch_packages skips a package in an LF version the build does not read
    and continues, instead of stopping at it.

Changed

  • Prepare asks for HASHING_SCHEME_VERSION_V2 explicitly rather than
    leaving the choice to the participant.
  • canton-proto's smoke_version example speaks TLS to an https://
    participant, so it runs against a DevNet validator as well as a LocalNet.
  • Two live-suite assumptions that held only on a LocalNet are gone: the
    participant id is checked for its shape rather than a participant::
    prefix, and the PQS suite expects a ledger effective time only from a store
    that read the ledger from the beginning (SCRIBE_PIPELINE_LEDGER_START is
    the hint).
  • tools/pqs/wait-ready.sh waits for Scribe's ingestion to settle, not only
    for its schema to exist.
  • docs/security/audit-scope.md names 0.3.1 as the commit under review and
    states the interactive-submission guarantee as the code now provides it.

v0.3.0 - Milestone 3

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 23 Sep 14:32

Milestone 3 of the Canton dev-fund grant (#407): the token standard in both versions (CIP-56 V1 and CIP-0112 V2), interactive submission with a pluggable signer, a typed PQS client, the conformance suite mapped to the Ledger Client Standard with its compatibility matrix, and the remaining canton-splice-* crates. All 31 crates are on crates.io at 0.3.0.

New crates: canton-token, canton-signer, canton-pqs, canton-daml-stdlib, the thirteen canton-splice-api-token-* crates (one per Splice token-standard package, V1 and V2) and canton-splice-api-featured-app-v1.

Two breaking changes for 0.2.x users, with the migration in the changelog: the token-standard packages are crates of their own now (a path such as canton_splice_amulet::splice_api_token_holding_v1::… becomes canton_splice_api_token_holding_v1::…, with that crate added to Cargo.toml), and Error::Http gained a url field.

Verified on a cn-quickstart LocalNet, on a Canton Builder Tool LocalNet, and on the Canton Network DevNet (Canton 3.5.17, Splice 0.8.1): the V1 and V2 transfers, a V2 allocation withdrawn and another settled by its executor, every DevNet update id read back from the participant. The runs are on record in docs/verification/token-standard-live-runs.md.

The complete list of changes is in CHANGELOG.md.

v0.2.3 — retry a dropped connection, and the coverage behind it

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 25 Aug 10:36

A correctness fix for streaming/submit resilience, plus the test coverage that led to it. Additive, no breaking changes; upgrading from 0.2.x needs nothing.

Fixed

A gRPC stream or submit did not retry a connection that died in flight. tonic surfaces a dropped connection mid-call as a Status with code Unknown ("transport error" / "h2 protocol error"), not as a transport::Error — that variant is only for connection establishment. So Error::is_retriable() treated the most ordinary failure there is as terminal: updates_resumable gave up on the first blip and run_with_retry never fired.

Data integrity was never at risk — a real drop came back as an error, never as silent loss or a double commit, and the change-ID recovery still recovered the truth — but the resilience did not engage. The fix distinguishes the two Unknowns by their source chain: a client-side transport failure carries a source (tonic built the status from the underlying error), a genuine server Unknown decoded from response trailers does not. So Unknown/Internal with a source is a dropped connection and retriable; the same code bare stays terminal.

Found by killing a real TCP connection every few seconds through a chaos proxy — in-process mocks always emitted Status::unavailable, which is why it did not show earlier. Verified live: a resumable stream survived 15 real kills and observed all 20 submitted updates, offsets strictly ascending. The WebSocket resumable lane was already correct.

Also: Template::from_json_created_event rejected the events-by-contract-id response, which nests the created event as {"created": {"createdEvent": …}}; it now unwraps both layers.

Tests

A mutation-testing pass over canton-core surfaced retry-pipeline behaviours the tests did not actually prove — backoff doubling (every test had pinned initial == max), the max-attempts stop (broken only surfaced as a hang), and the day/microsecond/nanosecond retry-delay units. Not defects; gaps. All now covered.

v0.2.2 — API polish and coverage from continued review

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 24 Aug 20:14

Additive polish from the review that did not stop when the milestone shipped. No breaking changes; upgrading from 0.2.x needs nothing.

API, from an independent Rust API Guidelines audit

Sixteen observations, none blocking. Landed here: ChangeId (and Store, TransactionShape, ClientAuth) derive Hash — the change ID is the key applications track submissions by, and it could not be a HashMap key; canton-ledger/canton-admin re-export Auth, ErrorInfo, ResourceInfo so the types their own APIs return can be named without a canton-core dependency; ErrorCategory gains TryFrom<i32>/From<ErrorCategory> for i32; #[must_use] and docs.rs feature badges where siblings had them and these did not.

The audit's two breaking findings are recorded rather than rushed: ADR-0010 commits the *ParseError renames and the removal of Party's unvalidated From<&str> to 1.0.

Tests

Coverage measurement found what four review passes reading code had not: the three TopologyClient list reads and both recovery handles' recover were exercised only by the env-gated live suites, which CI never runs. All hermetically covered now — 363 tests without a node, hand-written code at 87.7% line coverage, cargo udeps clean, and a compile-time proof that every public future is Send.

Adversarial probes against a live Canton 3.5.7 participant, run with the published crates, established the duplicate-submission semantics the mocks simplify: fire-and-forget Submit reports a duplicate through the completion stream (where recover reads it), only the waiting lane rejects synchronously, and the JSON lane's answer is exactly the HTTP 409 + DUPLICATE_COMMAND the SDK's predicate matches. OIDC refresh was driven through a real token expiry against Keycloak.

v0.2.1 — docs.rs build fix, and the gates that missed it

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 24 Aug 14:16

A patch over v0.2.0. The crates themselves were sound — every one installs and builds as published — but canton-ledger 0.2.0 shows its documentation as failed to build on docs.rs, and that cannot be fixed for a version already out.

What broke

Two #[cfg_attr(docsrs, doc(cfg(feature = "ws")))] attributes went in without the crate declaring #![cfg_attr(docsrs, feature(doc_cfg))], which that attribute requires. cargo doc never sets docsrs, so the attributes compiled out and every gate stayed green everywhere. docs.rs does set it, on nightly, and is therefore the first place the code is built as written.

The gates that let it through

A docsrs job now builds the workspace the way docs.rs will.

Adding it surfaced a second problem: it failed on stable while asking for nightly. rust-toolchain.toml pins the channel for anything run inside the checkout and overrides the toolchain CI installs — rustup says so in its own log. The MSRV job had the same defect and always had, so rust-version = "1.88" had never actually been checked. The claim holds, but nothing was establishing it. Both jobs now set RUSTUP_TOOLCHAIN, the override that beats the file.

cargo-semver-checks also grew from 5 crates to 11, now that the M2 crates have a published baseline to compare against.

Also

The README's compatibility table still called 0.2 unreleased when 0.2.0 was published, and the README is frozen into each crate's page at publish time — so it says so on crates.io for 0.2.0 and is corrected here. RELEASING.md gained the post-publish verification the pre-publish gate cannot do: cargo package runs --no-verify because verifying needs the siblings already on crates.io, which means the packaged artefact is first built by whoever installs it.

No API changes. Upgrading from 0.2.0 needs nothing.

v0.2.0 — Milestone 2: DAR codegen, dpm component, Splice bindings

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 24 Aug 13:23

Milestone 2 of the Canton dev-fund grant (#407): typed Rust codegen from DARs, the dpm codegen-rust component, and the first prebuilt canton-splice-* crates — together with every finding from Equilibrium's independent review of Milestone 1.

Codegen

canton-codegen generates typed Rust from a DAR: templates, choices, interfaces and contract keys, with serde for the JSON lane and ToValue/FromValue for gRPC on every generated type. It decodes daml-lf-archive natively, so nothing downstream needs a JVM, and a conformance oracle in CI holds the decoder to the official reader.

cargo install canton-codegen-cli
dpm-codegen-rust --dar my-app-1.0.0.dar --out my-app-bindings

Prebuilt bindings ship as canton-splice-amulet, canton-splice-wallet and canton-splice-wallet-payments. The Daml-LF → Rust mapping is specified in docs/daml-lf-type-mapping.md, and what an SCU version bump does to generated code is measured in docs/scu-regeneration.md.

From the Milestone 1 review

Equilibrium reviewed M1 independently and published a regression branch stating the behaviour each finding requires. All of it is fixed here; their suite passes against this release.

The one worth naming: a submission whose response was lost is retried with the same change ID, the participant refuses the retry as a duplicate — and the SDK reported failure over a command that had committed. It now reports success, because that rejection is the participant saying the earlier attempt landed. CantonClient::submission hands back the change ID before anything is sent, so an ambiguous outcome is recoverable over either transport.

Also: a lossless ACS read (AcsEntry) that no longer drops incomplete reassignments; resumable streams that resume from the participant's own checkpoint and report the failure that stopped them; the JSON lane at parity on submission, recovery and event lookup; Auth0 and Okta presets that produce their providers' actual requests; stream-lifetime telemetry, WebSocket trace propagation and a supported OTLP metrics path; and two credential leaks through Debug closed.

Breaking, for code on 0.1.x

Two signatures change, both because the old one could not express a correct call — OidcConfig::auth0 now takes an audience, and await_completion takes a ChangeId rather than a command id and a party list. CHANGELOG.md has the migration.

Verified

356 tests without a node, and against a Canton 3.5.7 participant 33 ledger + 5 admin live tests with CANTON_TEST_REQUIRE_LIVE=1 so a missing environment fails rather than skipping quietly. CI is green on Linux, macOS and Windows, at MSRV 1.88, across the feature powerset, on aarch64 and musl.

v0.1.4 — documentation fixes from the Foundation review

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 05 Aug 18:50

Documentation and developer-experience fixes from the Canton Foundation review
of the M1 submission. No library code changes; 0.1.3 and 0.1.4 are the same
SDK.

Fixed

  • Examples: version_and_health and submit_and_read read the same
    CANTON_TEST_* variables as the live tests (the unprefixed CANTON_*
    spellings still work and take precedence). They previously required a second,
    undocumented set of six names, so following the README's own setup and then
    running the example it links to failed on set CANTON_TOKEN_URL.
  • Live tests: the ACS token-paging test no longer caps the walk at 500
    pages. At page size 1 that bounded the ledger rather than the loop, so on a
    participant with a few hundred contracts a healthy walk reported "paging did
    not terminate". It now fails when the page token stops advancing, which is
    the condition it was meant to catch.

Documentation

  • README: the live-test variables are documented under their real names
    (every one is CANTON_TEST_-prefixed) in a table that says what each gates,
    with a copy-pasteable export block. CANTON_TEST_JSON_ENDPOINT (the JSON and
    WebSocket tests) and CANTON_TEST_SYNC_ID were previously undocumented, and
    six names were listed without their prefix.
  • README: node setup lists the
    Canton Builder Tool
    and Splice LocalNet
    alongside cn-quickstart, with what each one actually provides: Splice
    LocalNet is unauthenticated, so the OIDC-gated tests skip there as well as the
    command-submission ones; the Builder Tool's canton builder deploy uploads a
    DAR, which is how to get CANTON_TEST_LICENSING_PKG without cn-quickstart.

All six crates release in lockstep at 0.1.4 (canton, canton-core, canton-proto, canton-auth, canton-ledger, canton-admin) — mix only equal versions.

cargo add canton@0.1.4

v0.1.3 — feature-powerset fix

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 05 Aug 18:50

Fixed

  • canton-ledger: CompletionsRequest's JSON body builder is now gated on
    the ws feature, which is the only thing that reads it — the JSON Ledger API
    has no POST endpoint for completions. Without the gate the method was dead
    code in any build without ws, which failed the workspace's own
    -D warnings on the feature powerset. Consumers of 0.1.2 are unaffected:
    Cargo caps lints in registry dependencies, so a default-features = false
    build of the published crate compiles either way.

All six crates release in lockstep at 0.1.3 (canton, canton-core, canton-proto, canton-auth, canton-ledger, canton-admin) — mix only equal versions.

cargo add canton@0.1.3

v0.1.2 — request builders and Canton-native retry

Choose a tag to compare

@dmitryk-cf dmitryk-cf released this 05 Aug 18:50

Additive (semver-compatible) API growth from external M1 review feedback:
request builders for the read streams, and Canton-native error classification.

Added

  • canton-ledger (request builders): UpdatesRequest,
    ActiveContractsRequest, and CompletionsRequest (module request,
    re-exported at the root) open up the request surface the plain methods
    hard-code — on every read path and both transports: gRPC
    (updates_with, updates_resumable_with, updates_page_with,
    active_contracts_with, active_contracts_page_with,
    active_contracts_resumable_with, completions_with), JSON POST
    (JsonClient::updates_with, active_contracts_with), and WebSocket
    (ws_updates_with, ws_active_contracts_with, ws_completions_with):

    • bounded streams (until(end_inclusive)) for catch-up/sync reads that
      terminate at an offset instead of staying live;
    • template and interface filters (for_templates / for_interfaces,
      accepting package:Module:Entity ids with #package-name references)
      instead of the wildcard filter;
    • transaction shape selection (TransactionShape::AcsDelta /
      LedgerEffects), created-event blobs, topology events, dropping
      reassignments, and non-verbose records;
    • UpdatesRequest::descending — bounded newest-first reads on the stream
      and page paths, both transports (rejected up front on the resumable
      stream, whose position tracking assumes ascending order);
    • for_any_party (updates and ACS) — the filters_for_any_party /
      filtersForAnyParty full-ledger ingestion form, for tokens with
      wildcard read authorization;
    • CompletionsRequest::with_user_id to scope the completion stream to a
      submitting user id (pairs with Submit::with_user_id) — required for
      tokens that do not carry a user id.

    Defaults match the plain methods exactly (updates_with(UpdatesRequest:: new(p, o)) ≡ updates(p, o)), whose signatures are unchanged — the plain
    methods now delegate to the builders, so there is a single body/request
    producer per query on each transport.

  • canton-ledger (submit): Submit::with_transaction_shape selects the
    shape of the transaction returned by submit_and_wait_for_transaction
    (ledger effects — the default, as before — or ACS delta), removing the last
    hard-coded TransactionShape.

  • canton-ledger (submit, full Commands surface): every remaining
    Commands field is now settable on both transports —
    Submit/JsonCommands gain with_submission_id, add_disclosed_contract
    (the explicit-disclosure counterpart to the created-event blobs the read
    builders expose), with_package_id_selection_preference (the SCU upgrade
    pin), and with_min_ledger_time_abs/with_min_ledger_time_rel; Submit
    additionally gains with_prefetch_contract_keys and with_taps_max_passes,
    and JsonCommands gains with_deduplication_period (raw JSON, mirroring
    Submit::with_deduplication_duration/_offset).

  • canton-ledger (JSON retries): JsonClient::with_retry(RetryConfig) —
    the same opt-in retry pipeline as the gRPC client (category-first
    classification of the participant's error body, exponential backoff,
    server-recommended delays honoured; de-duplication-safe for submits since
    the command id stays fixed across attempts).

  • canton-core (error classification): errors now expose the precise
    retry semantics the Ledger API encodes, beyond the transport status code —
    on both transports:

    • ErrorCategory — Canton's error categories (from
      ErrorInfo.metadata["category"] on gRPC statuses, or the
      errorCategory field of JSON API error bodies) with the retryability the
      error-code docs define; surfaced as Error::category().
    • Error::retry_delay() — the server-recommended pause, from the
      google.rpc.RetryInfo detail (gRPC) or the retryInfo field (JSON).
    • Error::correlation_id() — from the google.rpc.RequestInfo detail
      (gRPC) or correlationId/traceId (JSON), for quoting to the
      participant operator.
    • Error::is_retriable is now category-first for both Error::Status
      and Error::Http: the category's verdict wins over the gRPC/HTTP status
      code (e.g. a category-12 "seek after ledger end" on HTTP 400 is
      retryable; a category-10 "resource exists" on ABORTED is not), a bare
      RetryInfo counts as retryable, and the previous code-based
      classification remains as the fallback for non-Canton errors.
      Behaviour change under opt-in retry: HTTP errors that Canton marks
      retryable now retry where they previously failed fast — including the
      413 JSON_API_MAXIMUM_LIST_ELEMENTS_NUMBER_EXCEEDED (category 2 with a
      1-second retryInfo), even though shrinking the request limit is the
      actual remedy. The SDK follows the server's verdict rather than
      special-casing codes; callers that want to fail fast on it can match on
      Error::Http { status: 413, .. } or handle it before retrying.
    • retry::run_with_retry honours the server's retry_delay when it
      exceeds the local backoff.

Fixed

  • canton-ledger (WebSocket error frames): a JsCantonError frame on a
    WS stream is now surfaced as Error::Http (with the gRPC code's canonical
    HTTP status and the full error body), instead of Error::CommandRejected.
    The frame is the same error object the JSON API returns in HTTP error
    bodies, so it now carries its category()/retry_delay()/
    correlation_id() — and, behaviour change, ws_updates_resumable now
    correctly reconnects on transient participant errors (categories 1/2/…),
    which the never-retriable CommandRejected mapping previously turned into
    a dead stream.

All six crates release in lockstep at 0.1.2 (canton, canton-core, canton-proto, canton-auth, canton-ledger, canton-admin) — mix only equal versions.

cargo add canton@0.1.2