Repository navigation
Releases
v0.3.2 - review fixes, hashing scheme V3, and the fresh-build fix
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_eventandfrom_json_created_eventnow 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 sameModule: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 needpackage_nameset. - 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
andhash_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_withforms taking an
UpdatesRequest's format) andlatest_pruned_offsetsonCantonClientand
JsonClient. "No such update" isOk(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_withand
JsonClient::updates_typed_withreturn 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_keysconformance row generates from it. v2_allocateruns 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-lf0.3.x does not build on a fresh install. zip 2.6, inside the
2range the crate accepts, added a type parameter toZipFile, which
canton-lfnamed; the workspace lockfile held zip at 2.4, so CI stayed green
whilecargo add canton-lf,cargo add canton-codegenand
cargo install canton-codegen-cli(without--locked) failed to compile.
The entry is now read throughstd::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-cliinstalls 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, whichcanton-lfdoes not
call: it reads each DAR entry into memory under a size cap. Moving to zip 9
changes the error typecanton_lf::DarError::Zipcarries, so it waits for
0.4;deny.tomlrecords the three with that reason. - The
PreparedandPrepare::with_hashing_scheme_versiondocs 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
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 newcanton_ledger::hashingmodule rebuilds the
hash from the returned transaction and metadata, byte for byte as Canton
does (checked against Canton's own test vectors), andprepare_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_partyrefuses 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 aContent-Length. canton-core: a server-recommended retry delay is capped at
MAX_SERVER_DELAY(60 s, logged when capped), a spelledretryInfois
clamped after parsing, and the jitter arithmetic can no longer panic on a
hostile value. ARetryInfodetail 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
onDUPLICATE_COMMANDorALREADY_EXISTS, not on any HTTP 409 (which is
ABORTEDas often as not on Canton's JSON lane) or on a body that merely
mentions the word. Two backpressure refusals no longer come back asOk.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 thatcargo testwould run)
in the generated crate.
Added — Daml-LF 2.3
canton-lfdecodes LF 2.3, the version Canton 3.5.x SDKs emit and a
DevNet participant already vets (Digital Asset's utility apps,
daml-stdlib3.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 typespb::daml_lf_dev
is nowpb::daml_lfand aTypereferencing an interned type spells it
InternedType, following the schema.canton_lf::pbis 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 skipscanton-lffor this one release and takes
it back with 0.3.1 as the baseline.fetch_packagesskips a package in an LF version the build does not read
and continues, instead of stopping at it.
Changed
Prepareasks forHASHING_SCHEME_VERSION_V2explicitly rather than
leaving the choice to the participant.canton-proto'ssmoke_versionexample speaks TLS to anhttps://
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 aparticipant::
prefix, and the PQS suite expects a ledger effective time only from a store
that read the ledger from the beginning (SCRIBE_PIPELINE_LEDGER_STARTis
the hint). tools/pqs/wait-ready.shwaits for Scribe's ingestion to settle, not only
for its schema to exist.docs/security/audit-scope.mdnames 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
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
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
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
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
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
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_healthandsubmit_and_readread the same
CANTON_TEST_*variables as the live tests (the unprefixedCANTON_*
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 onset 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 isCANTON_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) andCANTON_TEST_SYNC_IDwere previously undocumented, and
six names were listed without their prefix. - README: node setup lists the
Canton Builder Tool
and Splice LocalNet
alongsidecn-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'scanton builder deployuploads a
DAR, which is how to getCANTON_TEST_LICENSING_PKGwithout 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.4v0.1.3 — feature-powerset fix
Fixed
canton-ledger:CompletionsRequest's JSON body builder is now gated on
thewsfeature, 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 withoutws, which failed the workspace's own
-D warningson the feature powerset. Consumers of 0.1.2 are unaffected:
Cargo caps lints in registry dependencies, so adefault-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.3v0.1.2 — request builders and Canton-native retry
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, andCompletionsRequest(modulerequest,
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,
acceptingpackage:Module:Entityids with#package-namereferences)
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) — thefilters_for_any_party/
filtersForAnyPartyfull-ledger ingestion form, for tokens with
wildcard read authorization;CompletionsRequest::with_user_idto scope the completion stream to a
submitting user id (pairs withSubmit::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. - bounded streams (
-
canton-ledger(submit):Submit::with_transaction_shapeselects the
shape of the transaction returned bysubmit_and_wait_for_transaction
(ledger effects — the default, as before — or ACS delta), removing the last
hard-codedTransactionShape. -
canton-ledger(submit, fullCommandssurface): every remaining
Commandsfield is now settable on both transports —
Submit/JsonCommandsgainwith_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), andwith_min_ledger_time_abs/with_min_ledger_time_rel;Submit
additionally gainswith_prefetch_contract_keysandwith_taps_max_passes,
andJsonCommandsgainswith_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
errorCategoryfield of JSON API error bodies) with the retryability the
error-code docs define; surfaced asError::category().Error::retry_delay()— the server-recommended pause, from the
google.rpc.RetryInfodetail (gRPC) or theretryInfofield (JSON).Error::correlation_id()— from thegoogle.rpc.RequestInfodetail
(gRPC) orcorrelationId/traceId(JSON), for quoting to the
participant operator.Error::is_retriableis now category-first for bothError::Status
andError::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" onABORTEDis not), a bare
RetryInfocounts 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
413JSON_API_MAXIMUM_LIST_ELEMENTS_NUMBER_EXCEEDED(category 2 with a
1-secondretryInfo), even though shrinking the requestlimitis 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_retryhonours the server'sretry_delaywhen it
exceeds the local backoff.
Fixed
canton-ledger(WebSocket error frames): aJsCantonErrorframe on a
WS stream is now surfaced asError::Http(with the gRPC code's canonical
HTTP status and the full error body), instead ofError::CommandRejected.
The frame is the same error object the JSON API returns in HTTP error
bodies, so it now carries itscategory()/retry_delay()/
correlation_id()— and, behaviour change,ws_updates_resumablenow
correctly reconnects on transient participant errors (categories 1/2/…),
which the never-retriableCommandRejectedmapping 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