Skip to main content
The latest stable Canton release is reproduced below verbatim from the upstream release notes. For the full Canton release history, including older versions, see the Canton release notes on the Digital Asset blog.

Release of Canton 3.5.1

Canton 3.5.1 has been released on May 27, 2026.

What’s New

Contract Keys

Overview

Canton 3.5 introduces contract keys. Compared to a similar feature available in Canton 2.x, there are two notable differences:
  • The keys are not unique, meaning multiple contracts may share the same key.
  • Negative lookups are not validated.
As a consequence, application developers must ensure key uniqueness through external enforcement mechanisms. Contract keys are available from Daml-LF 2.3 onwards, which itself is available from Protocol Version 35, see below.

Standard library

Daml language now supports several primitives associated with contract keys. In all cases, the contracts are returned in the following order:
  • first the contracts created within a transaction, starting with the most recent,
  • then explicitly disclosed contracts,
  • then contracts known to the participant in recency order.
The following primitives are available:
  • lookupByKey - Available in prelude. It checks whether a contract with the given key exists and if yes, returns the contract id. If multiple contracts exist, the most recently created is returned. Signature is the same as in 2.x.
  • fetchByKey - Available in prelude. It fetches the first contract id and contract data associated with the given contract key. If multiple contracts exist, the most recently created is returned. Signature is the same as in 2.x.
  • exerciseByKey - Available in prelude. Exercise a choice on the first contract associated with the given key. Signature is the same as in 2.x.
  • lookupNByKey - Available in DA.ContractKeys. It looks up up to n contracts associated with the passed key.

Daml Script

There are Daml Script functions - counterparts of the standard library primitives:
  • queryByKey - It looks up a contract associated with the passed key and returns its ids and data. It is of type Script, which means it must appear as top-level instruction as part of a Script.
  • queryNByKey - It looks up up to n contracts associated with the passed key and returns their ids and data. It is of type Script, which means it must appear as top-level instruction as part of a Script.
  • exerciseByKeyCmd - It exercises a choice on the first contract with the given key. It is of type Commands and must therefore be wrapped by a submit operation, and can be combined with other Commands.

Smart Contract Upgrades (SCU)

To support SCU upgrade for key and maintainer definitions, new guidelines have been added. At upgrade time, the recomputed key and maintainers are verified to be identical to the upgraded contract’s original key and maintainers. If they aren’t, an upgrade error is raised and the transaction is aborted. It is forbidden to add or remove a key definition from a template in a later version of that template. This is enforced at package vetting time.

Ledger API

Following contract-key related extensions have been made to the Ledger API
  • contract_key_hash has been added to the CreatedEvent message returned in the State- and UpdateService responses
  • prefetch_contract_keys field present in the Command and PrepareSubmissionRequest used by the Command- CommandSubmission- and InteractiveSubmissionService have been reactivated to allow the caller to request pre-heating the contract key cache underpinning the command interpretation. Use it when performance tests indicate that many sequential contract key lookups adversely impact the command interpretation speed.

PQS

In PQS, keys are mere metadata that can be queried like any other metadata. It is possible to query for all contracts with a given key:
select contract_id, payload ->> 'label'
from __contracts
where contract_key = jsonb_build_object(...)
order by created_at_ix

Daml-LF 2.3

A new version of Daml-LF is released: Daml-LF 2.3. Its main features are:
  • DA.Crypto.Text, originally released in 3.4 in early access (alpha) status, is part of LF 2.3, which means it is now marked as stable.
  • Support for Contract Keys.

Targeting LF 2.3

If you want to use new features available in the LF 2.3, select it explicitly as compilation target by setting the --target=2.3, either as direct argument on the command line or as part of a daml.yaml:
sdk-version: 3.5.1
name: some-name
source: daml
version: 0.0.1
dependencies:
  - daml-prim
  - daml-stdlib
build-options:
- --target=2.3
After changing the settings, the source code must be recompiled. Please note that this will cause the package id to change, which should be accompanied by a version change.

Logical Synchronizer Upgrades

Logical Synchronizer Upgrades, or LSU, replace the procedure previously used to upgrade the synchronizer (Synchronizer Upgrade with Downtime). LSU address the following shortcomings of the previous upgrade procedure:
  • Reduced downtime The downtime for Daml transactions is in the order of dozens of seconds. The downtime for topology transactions is in the order of hours before the upgrade. This will be improved in a subsequent version.
  • No manual coordination for validators Operators of validator nodes only need to upgrade their binary before the upgrade time. The rest of the procedure happens through automation
  • Reduced coordination for SVs SVs can progress independently with the preparation of the upgrade and signal their progress using dedicated topology transactions.
  • Asynchronous upgrade of each validator Each validator independently upgrade its binary before the upgrade time. Each validator automatically perform the upgrade when it processes the last message of the old synchronizer.
  • Preserved local history as well as cryptographic evidence Transaction history (including updates and their offsets) is preserved, as well as ACS commitments.

External submissions around upgrade time

External submissions prepared before upgrade time on a synchronizer running protocol version 34 cannot be submitted after LSU on a synchronizer running protocol version 35: they need to be re-prepared and re-signed.

DA BFT Beta

DA BFT is a new ordering service as part of the synchronizer that will replace the current single-leader CometBFT ordering service on the Global Synchronizer with a parallel, multi-leader consensus architecture, enabling significantly higher transaction throughput and fault tolerance. As part of this release DA BFT is ready in beta form for early access testing, but not recommended for production or close-to-production testing yet.

Multi Synchronizer Alpha

Multi-synchronizer support is available in early access and has to be enabled explicitly. This feature should only be used in test environments. To enable contract reassignment across synchronizers, the flag PARTICIPANT_FEATURE_FLAG_ENABLE_ALPHA_MULTI_SYNCHRONIZER must be activated on all participants hosting a stakeholder of the contract on both the source and target synchronizers. For a synchronizer, it can be done as follows:
participant.topology.synchronizer_trust_certificates.propose(
  p.id,
  synchronizerId,
  featureFlags = Seq(ParticipantTopologyFeatureFlag.EnableAlphaMultiSynchronizer),
)

Functional Changes

Party Replication

Offline party replication

Concluding an offline party replication by clearing the onboarding flag now includes two major updates when using protocol version 35:
  • Added crash resilience for ongoing clearances.
  • Automatic scheduling for clearances when a participant (re)connects to the synchronizer.
These changes apply only to the participant.parties.import_party_acs and participant.parties.clear_party_onboarding_flag endpoints. Note: The replicated party ID must be included in the party ACS import call to enable automatic scheduling. The original behaviour is retained for protocol version 34.

Party replication onboarding topology event is exposed on Ledger API

The PartyToParticipant topology “onboarding” state used in the process of replicating a party with existing contracts is now visible via the Ledger API when a party onboards on a synchronizer on protocol version 35 or higher. Starting with PV=35, the newly introduced ParticipantAuthorizationOnboarding Ledger API topology event signals the beginning of party replication and transitions to ParticipantAuthorizationAdded once the party’s ACS is fully visible on the Ledger API.

Preview: Online party replication

  • Added the file-based online party replication command participant.parties.add_party_with_acs_async to be used along with participant.parties.export_party_acs and instead of the sequencer-channel-based add_party_async command.
  • The online party replication status command now returns status in a very different, “vector-status” format rather than the old “oneof” style. This impacts the participant.parties.get_add_party_status command and com.digitalasset.canton.admin.participant.v30.PartyManagementService.GetAddPartyStatus gRPC response type.
  • The participant configuration to enable online party replication has been renamed to alpha-online-party-replication-support from unsafe-online-party-replication for consistency with other alpha features and to reflect that the default file-based mode is more secure not relying on sequencer channels.
  • The sequencer configuration to enable sequencer channels for online party replication has been renamed to unsafe-sequencer-channel-support from unsafe-enable-online-party-replication for consistency and to refer specifically to sequencer channels.

Minor Improvements

  • Onboarding party submission prevention: Ensures a participant does not submit a transaction or reassignment on behalf of an onboarding party.
  • Upgraded gRPC to 1.81.0 and AWS SDK to 2.44.3 to resolve Netty 4.1.130 CVEs (CVE-2026-33870, CVE-2026-33871).

New Transaction Hashing Scheme v3

  • A new hashing scheme version HASHING_SCHEME_VERSION_V3 has been introduced that includes the transaction’s max_record_time in the hash computation and covers the new transaction node and fields of contract keys. This new version is avaialble from Protocol Version 35.
  • See the hashing algorithm documentation for the updated version.
  • The max_record_time is now enforced by all confirming participants.
  • The Ledger API and Ledger JSON API prepare InteractiveSubmissionService has been modified to take in a specific hashing scheme version in the request. The default hashing scheme is HASHING_SCHEME_VERSION_V2. Integrators are encouraged to move to HASHING_SCHEME_VERSION_V3 for synchronizers using protocol version 35. In particular, usage of contract keys requires HASHING_SCHEME_VERSION_V3. See the versioning documentation for details.

Active Contracts Head Snapshot (ACHS)

The Active Contracts Head Snapshot (ACHS) is a new optional feature that maintains a continuously updated snapshot of the currently active contracts. When enabled, the ACHS accelerates GetActiveContracts (ACS) queries by allowing them to read directly from a pre-computed snapshot rather than scanning the full event log to reconstruct the active set. ACHS is disabled by default. To enable it, configure the achs-config block under the participant’s indexer settings:
canton.participants.<participant>.parameters.ledger-api-server.indexer.achs-config {
    valid-at-distance-target = 1000000
    last-populated-distance-target = 500000
}
The valid-at-distance-target controls how far behind the ledger end (in event sequential IDs) the snapshot’s validity point is maintained. The ACHS is not used for serving queries below its validity point, logging at INFO level “ACHS for <filter> skipped since validAt (…) already surpassed requested activeAt (…)”. If the valid-at-distance-target value is too small, long-running ACS queries may observe the ACHS validity point moving (mid-stream) past their requested offset, causing the stream to fall back to the slower filter tables query, logging at INFO level “ACHS stream for <filter> fell back to filter tables from (…) since validAt (…) surpassed activeAtEventSeqId (…)”. If the value is too large, the tail portion of the ACS (between the ACHS validity point and the requested offset) must be resolved from the filter tables, making that last segment more expensive. As described above, when the ACHS validity point moves or is past the requested offset, an info-level log message is emitted indicating that the stream fell back to the filter tables. Two corresponding metrics, achs_skips and achs_midstream_fallbacks, are available under daml.participant.api.index to help operators monitor the frequency of these fallbacks and tune the valid-at-distance-target accordingly. The last-populated-distance-target controls the additional lag (in event sequential IDs) for the population of ACHS in order to store only the long-lived contracts. A larger value reduces database I/O by skipping short-lived contracts that are created and archived before they would be added to the snapshot. However, setting it too large increases the cost of the remaining ACS tail, as more data must be fetched from the filter tables to cover the gap between the last populated point and the ACHS validity point. Further tuning parameters include:
  • population-parallelism: number of parallel threads for adding activations to the ACHS during normal operation.
  • removal-parallelism: number of parallel threads for removing deactivated activations from the ACHS during normal operation.
  • aggregation-threshold: minimum batch size (in event sequential IDs) before ACHS maintenance work is emitted.
  • init-parallelism: number of parallel threads for ACHS population and removal during initialization.
  • init-aggregation-threshold: minimum batch size (in event sequential IDs) for ACHS maintenance during initialization.
  • buffer-size: size of the internal buffer between the indexer pipeline and the ACHS maintenance flow.
The deactivation_distances histogram metric which is available under daml.participant.api.indexer.deactivation_distances can help operators understand the distribution of contract lifetimes (the event sequential ID distance between a contract’s activation and its deactivation) and set an appropriate last-populated-distance-target. Ideally, the population distance should be large enough so that most short-lived contracts are already deactivated and thus not added to the snapshot. Three gauge metrics are available under daml.participant.api.indexer to monitor the ACHS state:
  • achs_valid_at: the event sequential ID at which the ACHS is currently valid. ACS queries with a requested offset at or after this value can read directly from the ACHS.
  • achs_last_populated: the last event sequential ID for which activations were added to the ACHS.
  • achs_last_removed: the last event sequential ID for which deactivations were looked up and the corresponding activations were removed from the ACHS.

Hardened Error Handling in Sequencer Connect Service

We have implemented strict error sanitization and rewording for the SequencerConnectService to mitigate information leakage. Detailed internal error messages are now redacted before being sent to clients. If detailed diagnostics are required in a non-production environment, sanitization can be toggled off via:
canton.monitoring.sanitize-public-error-messages = false

Ignoring of offboarded sequencers for submission requests

In the case where sequencers are offboarded but remain online and kept in the connectivity configuration, it was still possible that members pick them as the target for submission requests. The submission would fail, but the member would incur a delay as it requires retrying. This has now changed, and offboarded sequencers are ignored when sending submission requests.

API Changes

The previous method of returning errors via response fields has been removed in favor of canonical gRPC error propagation. The following fields are now obsolete:
  • HandshakeResponse.value.failure
  • VerifyActiveResponse.value.failure
Errors are now communicated strictly through io.grpc.Status codes to ensure a consistent and secure interface. Status codes have changed as follows:
  • SequencerAuthenticationService.challenge newly fails with INVALID_ARGUMENT (instead of FAILED_PRECONDITION), if the client does not support the sequencer’s protocol version.
  • SequencerConnectService newly fails with INVALID_ARGUMENT (instead of FAILED_PRECONDITION) if a non-participant tries to connect.
  • SequencerConnectService.registerOnboardingTopologyTransactions newly fails with INTERNAL (instead of FAILED_PRECONDITIONS)
  • if there are no dynamic synchronizer parameters.
  • SequencerConnectService.registerOnboardingTopologyTransactions newly fails with FAILED_PRECONDITION if
  • the transactions cannot be added to the topology state and sanitization of error messages is enabled.

Mediator Crash Fault Tolerance

The mediator is now crash fault-tolerant and guarantees that all verdicts will eventually be persisted and available on the inspection API.

Enhanced Reliability for GetHighestOffsetByTimestamp

Previously, the GetHighestOffsetByTimestamp RPC and the find_highest_offset_by_timestamp console command could return offsets not yet synced with the participant’s local cache. Furthermore, forcing a query with a future timestamp resulted in an error. Specific changes:
  • The required state is now retrieved atomically via a consistent database snapshot.
  • The endpoint now includes an internal barrier (waiting up to 10 seconds) to ensure the local Ledger API cache catches up with the database before returning the offset.
  • When force is true, requesting a future timestamp now gracefully returns the current ledger end instead of failing.
No migration required.

ACS stream continuation

The GetActiveContracts stream request has been extended with an optional stream_continuation_token field that allows clients to continue an interrupted ACS stream from the last element which made through. The field can be populated with the stream_continuation_token field of the last response element received before the interruption, and the stream will continue from the next element after that.

ACS Ledger API counting

Introduced a new memory-efficient consoled command participant.ledger_api.acs.count() to count the number of active contracts on a participant node.
Note: This command is currently under the Testing feature flag.

ACS pagination

A new, GetActiveContractsPage endpoint added to State Service API. This enables the client to retrieve the ACS in paginated form, by specifying a max_page_size. The pages can be accessed sequentially by using the page_token field. The token can be obtained from the GetActiveContractsPageResponse of the last page.

GetUpdates stream in descending order of events

The GetUpdatesRequest object has new optional parameter descending_order. When this parameter is true the events are streamed from the newest to the oldest ones. The pages can be accessed sequentially by using the page_token field.

GetUpdates pagination

A new GetUpdatesPage endpoint has been added to Update Service API. THis allows retrieval of updates in paginated form instead of requesting the stream.

Improvements for repair.add and migration advice

The participant.repair.add admin command has been revised to use the new ImportAcs backend, bringing significant memory performance improvements, stricter default safety validations, and several new parameters.

Important behavioral change: strict Validation by default

Previously, repair.add implicitly accepted all injected contracts without re-evaluating their cryptographic hashes. To prevent accidental data corruption, the command now defaults to Validation mode ( contractImportMode = ContractImportMode.Validation).
  • Impact: If you have existing scripts or recovery procedures that inject manually modified, synthetic, or inconsistent contracts (where the payload does not strictly match the ContractId hash), they will now fail with a "Failed to authenticate contract with id" error.
  • Migration: To bypass this cryptographic validation and restore the legacy behavior, explicitly pass the Accept mode in your command call:
    participant.repair.add(
      synchronizerId = mySynchronizer,
      protocolVersion = myProtocolVersion,
      contracts = myContracts,
      contractImportMode = ContractImportMode.Accept // Bypasses strict validation
    )
    

New parameters

The command signature has been expanded to support several optional parameters:
  • workflowIdPrefix: Allows you to set a custom prefix for the generated workflow ID to easily track the repair transactions (defaults to import-<UUID>).
  • contractImportMode: Choose between Validation (default, validates that contract IDs comply with the scheme associated to the synchronizer where the contracts are assigned), or Accept the contracts as they are (if you know what you are doing).
  • representativePackageIdOverride: Allows you to remap or override the representative package IDs of the contracts as they are imported.
  • excludedStakeholders: When defined, any contract that has one or more of these parties as a stakeholder will not be added.

Improved party and repair ACS imports

We have completely overhauled the ACS import endpoints for both party replication and participant repair to be memory-efficient streaming endpoints:
  • Console command participant.parties.import_party_acs
  • Console command participant.repair.import_acs
  • gRPC RPC PartyManagementService.ImportPartyAcs
  • gRPC RPC ParticipantRepairService.ImportAcs
This resolves previous memory limitations, as these endpoints no longer load the entire ACS snapshot into memory at once.

Action required: Breaking API change

The synchronizerId is now a mandatory first parameter for both the import_party_acs and import_acs console commands as well as their analogous gRPC endpoints. You will need to update any existing scripts. For import_party_acs:
  • Old usage: participant.parties.import_party_acs("canton-acs-export.gz")
  • New usage: participant.parties.import_party_acs(mySynchronizerId, importFilePath = "canton-acs-export.gz")
For import_acs:
  • Old usage: participant.repair.import_acs("canton-acs-export.gz")
  • New usage: participant.repair.import_acs(mySynchronizerId, importFilePath = "canton-acs-export.gz")
Because of the mandatory synchronizerId parameter, to import a multi-synchronizer ACS snapshot, you must now call the endpoint sequentially for each synchronizer your participant is connected to, using the exact same snapshot file. The import process will ignore any contracts in the snapshot that are associated to a different synchronizer.
Details on the gRPC ImportAcs repair endpoint
The ImportAcs and ImportAcsV2 RPCs have been consolidated, introducing the following breaking changes and migration steps:
  • Endpoint removed: ImportAcsV2 (along with its request/response messages) is completely removed. All clients must migrate to the standard ImportAcs RPC.
  • Request signature and type changes:
    • Fields workflow_id_prefix (2), contract_import_mode (3), and representative_package_id_override (5) in ImportAcsRequest are now explicitly optional.
    • A new optional string synchronizer_id = 6 field was added.
    • Migration (ScalaPB): Adding optional changes generated code from base types to Option[T]. Existing clients will fail to compile and must be updated to wrap assigned values (e.g., workflowIdPrefix = Some("prefix")) and explicitly handle reading Option types.
  • Behavioral change (synchronizer_id): When filtering by synchronizer, mismatched contracts are now ignored. This breaks previous logic that relied on the import strictly aborting upon a mismatch.
Details on the gRPC ImportPartyAcs party replication endpoint
The ImportPartyAcs endpoint underwent the exact same consolidation (removing ImportPartyAcsV2), streaming semantics updates, generated code changes (ScalaPB Option[T]), and mismatched synchronizer behavior (ignoring rather than failing) as ImportAcs. Key differences specific to ImportPartyAcs:
  • New capability (party_id): A new optional string party_id = 6 field was added. Providing this in the first request of the stream enables automatic, crash-resilient scheduling of the onboarding flag clearance. If omitted, the participant logs a warning, and the flag must be cleared manually.

Topology-Aware Package Selection (TAPS) improvements

Topology-Aware Package Selection (TAPS) refinement for handling inconsistent vetting states:
  • The algorithm now considers a party’s package vetting state only for packages required by that party in the interpreted transaction. It starts with a minimal set of restrictions derived from the command’s root nodes and progressively accumulates more restrictions over a configurable number of passes. This iterative process increases the likelihood of finding a valid package selection set for the routing of the transaction.
  • The maximum number of TAPS passes can be set at the request-level via the optional taps_max_passes field in Commands or PrepareSubmissionRequest messages. If not specified, the default value is taken from the participant configuration via participants.participant.ledger-api.topology-aware-package-selection.max-passes-default (defaults to 3). A hard limit is enforced by participants.participant.ledger-api.topology-aware-package-selection.max-passes-limit (defaults to 4).
  • TAPS now ignores unvetted dependencies of packages that are not required for interpretation. complying now with the support of unvetted dependencies in the Canton protocol.

Ledger API Improvements

  • ApiRequestLogger now also used by Ledger JSON Api. Changes:
    • Redundant Request TID removed from logs.
    • Additional CLI options added: --log-access captures API access logs in a separate file (default: log/canton_access.log), and --log-access-errors captures API access errors in a separate file (default: log/canton_access_error.log).
    • Additional config options added: debugInProcessRequests logs in-process gRPC requests at DEBUG instead of TRACE, and prefixGrpcAddresses prefixes gRPC client addresses with grpc: (enabled by default).
  • LedgerAPI ListKnownParties supports an optional prefix filter argument filterParty. The respective JSON API endpoint now additionally supports identity-provider-id as an optional argument, as well as filter-party.
  • Protect the admin participant from self lock-out. It is now impossible for an admin to remove own admin rights or delete itself.
  • On Ledger API interface subscriptions, the CreatedEvent.interface_views now returns the ID of the package containing the interface implementation that was used to compute the specific interface view as InterfaceView.implementation_package_id.
  • OffsetCheckpoints are now always generated when an open-ended updates or completions stream is requested, even if there are no updates. The checkpoint can have the same offset as the exclusive start of the stream, making checkpoints visible even when starting from the ledger end. This enables client systems to recognize when the ledger end is advancing, even if the stream of updates is inactive.
  • Extended the set of characters allowed in user-id in the ledger api to contain brackets: (). This also makes those characters accepted as part of the sub claims in JWT tokens.
  • Functionality for managing internal and external parties has been improved, removing previous asymmetry:
    • User rights can now be assigned to an external party during allocation.
    • External parties can be allocated by the user themselves in the self-administration mode. Please note that users in self-administration mode can allocate up to N parties, depending on a setting of the parameter
    canton.participants.<participant-id>.ledger-api.party-management-service.max-self-allocated-parties
    
    By default the value of this parameter is 0.
  • An IDP administrator can now only allocate parties confined to their own IDP perimeter.
Some new metrics have been added to monitor the status of an LSU.

Performance Improvements

Session Signing Keys

Session signing keys can now be used to reduce the number of calls to external KMS (Key Management Service) providers. When enabled, session signing keys are generated and cached locally for a limited duration and used for signing operations during their validity period. Please read the documentation on Session Signing Keys for details on how to enable and configure this feature. Session signing keys are only available from Protocol Version 35 and are not enabled by default.

Compatible sibling views compression

In protocol version 35, each envelope in TransactionConfirmationRequest contains multiple views grouped by recipients instead of one envelope per view. Assignment and re-assignments also use this new format, but they always have one view.

Single Topology Transaction for External Parties

Multiple topology transactions for external parties can now be represented with a single PartyToParticipant topology transaction. The generateExternalPartyTopology endpoint on the Ledger API now returns a single PartyToParticipant topology transaction to onboard the party. The transaction contains signing threshold and signing keys. This effectively deprecate the usage of PartyToKeyMapping. For parties with signing keys both in PartyToParticipant and PartyToKeyMapping, the keys from PartyToParticipant take precedence. Deprecated usage of PartyToKeyMapping. The functionality provided by PartyToKeyMapping is now available directly in PartyToParticipant. Please use PartyToParticipant for new transactions. PartyToKeyMapping is still fully supported in this version (including existing and new transactions). In future version, creation of new PartyToKeyMapping transactions may be disallowed. Deprecated TopologyManagerReadService.ListAll in favor of ListAllV2, which uses an inclusion list (include_mappings) instead of an exclusion list (exclude_mappings). This avoids sending mapping codes unknown to older servers. The console method topology.transactions.list now calls ListAllV2 by default and only falls back to ListAll when targeting a 3.4 node. The excludeMappings and protocolVersion parameters of topology.transactions.list are deprecated; use filterMappings instead. Deprecated TopologyManagerReadService.ExportTopologySnapshot and TopologyManagerWriteService.ImportTopologySnapshot, along with their console counterparts topology.transactions.export_topology_snapshot, topology.transactions.import_topology_snapshot, topology.transactions.import_topology_snapshot_from, and topology.transactions.export_identity_transactions. Please use the corresponding V2 variants (ExportTopologySnapshotV2 / ImportTopologySnapshotV2, export_topology_snapshotV2, import_topology_snapshotV2, import_topology_snapshot_fromV2, export_identity_transactionsV2) instead, which use an updated internal bytestring format. Deprecated SequencerInitializationService.InitializeSequencerFromGenesisState, SequencerInitializationService.InitializeSequencerFromOnboardingState, SequencerAdministrationService.OnboardingState, and TopologyManagerReadService.GenesisState, along with their console counterparts setup.assign_from_genesis_state, setup.assign_from_onboarding_state, setup.onboarding_state_for_sequencer, setup.onboarding_state_at_timestamp, and topology.transactions.genesis_state. Please use the corresponding V2 variants (InitializeSequencerFromGenesisStateV2, InitializeSequencerFromOnboardingStateV2, OnboardingStateV2, GenesisStateV2, assign_from_genesis_stateV2, assign_from_onboarding_stateV2, onboarding_state_for_sequencerV2, onboarding_state_at_timestampV2, genesis_stateV2) instead, which use an updated internal bytestring format that enables streaming ingestion, making snapshot export and import significantly less memory-intensive.

Minor Performance Improvements

  • The Postgres connection tuning configuration of the indexer is now separated from the configuration of the Ledger API server (canton.participants.<participant>.ledger-api.postgres-data-source). The new configuration section canton.participants.<participant>.parameters.ledger-api-server.indexer.postgres-data-source should be used instead to tune the indexer’s Postgres connections.
  • A new indexer pipeline batching strategy added under the feature flag useWeighetdBatching. When switched on, the batches are created using their estimated database processing time using the submissionBatchInsertionSize as a limit for individual batches
  • Changed the CompressedBatch structure in the sequencer protocol for protocol version 35 to separately keep recipients and envelopes (from gzip(Seq((recp1, payload1), (recp2, payload2))) to gzip(Seq(recp1, recp2)), Seq(gzip(payload1), gzip(payload2)))).
  • Batching configuration now allows setting different parallelism for pruning (currently only for Sequencer pruning): New option canton.sequencers.sequencer.parameters.batching.pruning-parallelism (defaults to 2) can be used separately from the general canton.sequencers.sequencer.parameters.batching.parallelism setting.
  • Made the config option ...topology.use-time-proofs-to-observe-effective-time work and changed the default to false. Disabling this option activates a more robust time advancement broadcast mechanism on the sequencers, which however still does not tolerate crashes or big gaps in block sequencing times. The parameters can be configured in the sequencer via canton.sequencers.<sequencer>.parameters.time-advancing-topology.
  • Additional metrics for the ACS commitment processor: daml.participant.sync.commitments.last-incoming-received, daml.participant.sync.commitments.last-incoming-processed, daml.participant.sync.commitments.last-locally-completed, and daml.participant.sync.commitments.last-locally-checkpointed.

Breaking Changes

Removal of legacy party replication repair console macros

The original party replication method, which relied on a silent synchronizer, has been superseded by the offline party replication process. Consequently, the obsolete repair console macros associated with the legacy approach have been removed. Specifically, the following macros are no longer available:
  • step1_hold_and_store_acs
  • step2_import_acs
If you previously relied on the Silent synchronizer replication procedure, you will need to transition to the current offline party replication process. For details, please consult the Offline Party Replication documentation

Removal of deprecated, legacy ACS export and import endpoints

The legacy repair endpoints for the ACS export and import have been removed:
  • Console command participant.repair.export_acs_old
  • Console command participant.repair.import_acs_old
  • gRPC rpc ParticipantRepairService.ExportAcsOld
  • gRPC rpc ParticipantRepairService.ImportAcsOld

Migration advice

Use repair endpoints without the ‘old’ suffix:
  • Migrate to participant.repair.export_acs from participant.repair.export_acs_old
  • Migrate to participant.repair.import_acs from participant.repair.import_acs_old
  • Migrate to ParticipantRepairService.ExportAcs from ParticipantRepairService.ExportAcsOld
  • Migrate to ParticipantRepairService.ImportAcs from ParticipantRepairService.ImportAcsOld
Note that previously created ACS snapshots with the legacy endpoints cannot be imported with the current endpoints as the underlying data format has completely changed.
Migrating to export_acs
The most significant change is the removal of the timestamp parameter, which has been replaced by a mandatory ledgerOffset parameter. Console parameter changes:
  • New mandatory parameter: ledgerOffset (Long). You must now specify the exact ledger offset for the snapshot instead of a timestamp.
  • Removed parameters: partiesOffboarding, timestamp (replaced by ledgerOffset), force.
  • Renamed parameters: outputFile is now exportFilePath (default is "canton-acs-export.gz"), filterSynchronizerId is now synchronizerId.
  • New optional parameters: excludedStakeholders allows you to omit contracts that have one or more of these parties as a stakeholder; contractSynchronizerRenames allows mapping contracts from one synchronizer to another during export.
gRPC changes for ExportAcsRequest:
  • parties -> party_ids: Field renamed for consistency. If left empty, the endpoint will act as a wildcard and export the ACS for all parties hosted by the participant.
  • timestamp -> ledger_offset (Breaking): You must provide an exact int64 ledger_offset instead of a timestamp.
  • filter_synchronizer_id -> synchronizer_id: Field renamed for consistency.
  • Removed fields: force and parties_offboarding have been completely removed.
  • New fields: contract_synchronizer_renames and excluded_stakeholder_ids.
Migrating to import_acs
The import command remains largely the same in basic usage, but introduces new optional parameters for advanced validation and overrides, alongside strict memory-efficient streaming semantics for gRPC. Console parameter changes:
  • Renamed parameter: inputFile is now importFilePath (default is "canton-acs-export.gz").
  • New optional parameters: contractImportMode governs contract validation upon import (defaults to ContractImportMode.Validation); representativePackageIdOverride allows overriding representative package IDs during import; excludedStakeholders allows omitting contracts that have one or more of these parties as a stakeholder.
gRPC changes for ImportAcsRequest:
  • Streaming Semantics (Breaking): The new endpoint requires metadata fields (like contract_import_mode, synchronizer_id, etc.) to be populated only in the first request of the stream. Subsequent requests must omit metadata and only contain the binary acs_snapshot chunks.
  • New mandatory fields: contract_import_mode and synchronizer_id must be explicitly defined in the first stream request.
  • Removed fields: allow_contract_id_suffix_recomputation is completely removed.
  • New fields: excluded_stakeholder_ids and representative_package_id_override.
  • Response update: ImportAcsResponse is now a completely empty message (previously returned a contract ID mapping).

Only PackageName is accepted on Ledger API

Usage of package id for ledger queries was deprecated and now the validation will fail if used. The impacted APIs are:
  • GetUpdates
  • GetUpdateByOffset
  • GetUpdateById
  • GetActiveContracts
  • GetEventsByContractIdRequest
  • SubmitAndWaitForTransaction (the optional transaction_format)
  • SubmitAndWaitForReassignmentRequest
  • ExecuteSubmissionAndWaitForTransactionRequest

SynchronizerId field update in Externally signed transactions

In Protocol version 35, the synchronizer_id field in externally signed prepared transaction metadata will be populated with the physical synchronizer ID of the synchronizer on which the transaction will be processed, instead of the logical synchronizer ID, as is the case in PV 34. Applications must ensure they do not rely on the format of the synchronizer_id value.

Changes from NonNegativeLong to Long

Some console commands using a NonNegativeLong for the offset are changed to accept a Long instead. Similarly, some console commands returning an offset now return a Long instead of a NonNegativeLong. It brings consistency and allows to pass the output of participant.ledger_api.state.end(). Impacted commands:
  • participant.repair.export_acs
  • participant.parties.find_party_max_activation_offset
  • participant.parties.find_party_max_deactivation_offset
  • participant.parties.find_highest_offset_by_timestamp

Removal of automatic recomputation of contract ids upon ACS import

The ability to recompute contract ids upon ACS import has been removed.

Removal of multi-host name resolution tooling

Support for the multi-host name resolution was removed. This was only used if synchronizer connectivity defined a sequencer with multiple endpoints, which is not supported with our current sequencers: we now have multiple sequencers each with exactly one endpoint.

Ledger JSON API Spec Corrections

JSON Ledger API OpenAPI/AsyncAPI spec corrections
  • Fields not marked as required in the Ledger API .proto specification are now also optional in the OpenAPI/AsyncAPI specifications. If your client code is using code generated using previous versions of these specifications, it may not compile or function correctly with the new version. To migrate:
    • If you prefer not to update your code, continue using the previous specification versions as the JSON API server preserves backward compatibility.
    • If you want to use new endpoints, features or leverage the new less strict spec, migrate to the new OpenAPI/AsyncAPI specifications as follows:
      • Java clients: No changes are needed if you use the OpenAPI Generator. Otherwise, potentially optionality of fields should be handled appropriately for other code generators.
      • TypeScript clients: Update your code to handle optional fields, using the ! or ?? operators as appropriate.
  • From Canton 3.5 onwards, OpenAPI/AsyncAPI specification files are suffixed with the Canton version (e.g., openapi-3.5.0.yaml).
  • Canton 3.5 is compatible with OpenAPI specification files from version 3.4.0 to 3.5.0 (inclusive).
  • The Ledger JSON API server now enforces that only fields marked as required by the Ledger API OpenAPI/AsyncAPI specification are mandatory in request payloads.

Change from grpcurl to grpc-health-probe in all Docker images

The tool used for health check probes changed from grpcurl to grpc-health-probe in all the docker images.

Minor Breaking Changes

  • The expert keep-alive-client configuration parameter for various client services moved to channel.keep-alive-client.
  • We reduced the defaults for setBalanceRequestSubmissionWindowSize and defaultMaxSequencingTimeOffset to 2 minutes.
  • The default OTLP gRPC port that the Canton connects to in order to export the traces has been changed from 4318 to 4317. This aligns the default configuration of Canton with the default configuration of the OpenTelemetry Collector. This change affects only the users who have configured an OTLP trace export through
    canton.monitoring.tracing.tracer.exporter.type=otlp
    
  • Removed the LastErrorsAppender along with the Admin API endpoints StatusService.GetLastErrors and StatusServiceGetLastErrorTrace, as well as the corresponding console commands last_errors and last_error_trace.

Deprecations

Deprecate scope-based access tokens

  • “Scope-based” access tokens, i.e. JWTs without any audience specified, have been deprecated.
  • A configuration that does not specify a target-audience will log a warning on node startup.
  • Configurations that specify both a target-audience and a target-scope are not supported in this version and will also log a warning on node startup.
  • Starting Canton version 3.7, support for “scope-based” tokens will be removed entirely to enforce a valid aud field in every incoming JWT.
  • The scope field will, in a future version, be repurposed to serve exclusively as an additional, optional claim for fine-grained permissions.

Removal of the old sequencer connection transports

The old sequencer connections transports have been removed, and only the new sequencer connection pool remains. Consequently, the configuration <node>.sequencer-client.use-new-connection-pool has been deprecated and no longer has any effect.

Deprecate initial protocol version configuration

The config key participant.parameters.initial-protocol-version was unused and has been marked as deprecated.

Configuration Deprecations

  • The configuration parameters topology.use-new-processor and topology.use-new-client have been deprecated and now default to true. Configuring those parameters to false will be ignored.
  • The parameter canton.participants.<participant>.parameters.package-metadata-view.init-takes-too-long-interval is now ignored, and a warning will only be printed once, rather than periodically.
  • The parameter canton.participants.<participant>.parameters.ledger-api-server.indexer.prepare-package-metadata-time-out-warning is now ignored.
  • The individual JVM metric flags classes, cpu, memoryPools, threads, gc, and buffers in canton.monitoring.metrics.jvm-metrics are no longer supported since the upgrade to OpenTelemetry instrumentation 2.26.0. All standard JVM metrics (classes, cpu, memory pools, threads, garbage collector) are now always enabled when jvm-metrics.enabled = true. A new experimental flag has been added to control experimental JVM metrics (e.g. buffer pools). Users who previously set buffers = true should migrate to experimental = true. See https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/16087 for details.
  • The Zipkin trace exporter configuration canton.monitoring.tracing.tracer.exporter.type=zipkin is deprecated following the OpenTelemetry specification deprecation of Zipkin exporters. The Zipkin exporter will be removed in a future release. Users should migrate to the OTLP exporter. See https://opentelemetry.io/blog/2025/deprecating-zipkin-exporters/ for details.
  • Removed the feature flag canton.sequencers.<node>.parameters.async-writer.enabled, as async writing is now the only supported mode.
  • Changed the path for crypto.kms.session-signing-keys (deprecated) to crypto.session-signing-keys so that session signing key configuration is no longer directly tied to a KMS. However, session signing keys can still only be enabled when using a KMS provider or when running with non-standard-config=true.
  • package-dependency-cache field in caching configuration is deprecated. It can be removed safely from node configurations.

Ledger JSON API package vetting endpoints

The Ledger JSON API v2/package-vetting endpoint exposes list functionality on the GET method by accepting a request body. This is not recommended by the HTTP specification, hence the endpoint is deprecated. For consistency, the POST method, used for updating the vetting state, of the same endpoint is also deprecated. In turn, two new endpoints are implemented to provide the same functionality:
  • v2/package-vetting/list accepts a POST request with the same body as the deprecated GET v2/package-vetting endpoint and returns the list of vetted packages in the same format.
  • v2/package-vetting/update accepts a POST request with the same body as the deprecated POST endpoint v2/package-vetting and returns the updated vetting state of the package in the same format.

Protocol version parameter in topology list commands

The protocolVersion parameter in all <node>.topology.<mapping>.list console commands has been deprecated and will be removed in a future version.

Minor Improvements

Bugfixes

  • Fixed a mid-crash recovery issue for offline party replication and repair ACS imports. Previously, if an ACS import was interrupted (for example by a participant node restart or crash), a subsequent recovery attempt could result in missing contracts on the Ledger API. The recovery process now properly rolls back uncommitted partial states upon retrying the ACS import, ensuring recovered contracts are completely synchronized across both internal storage and the Ledger API.
  • Fixed a bug where the Ledger API PackageService.ListVettedPackages used to return a potentially not yet effective state of the vetted packages. Now it returns the state of vetted packages effective at the time of the request.
  • Sequencer health status used to incorrectly return the synchronizer uid instead of the sequencer uid.
  • Prevent Ledger API crashes after running ParticipantRepairService.PurgeContracts admin command. Fixes a critical issue where using the ParticipantRepairService.PurgeContracts command (when multi-synchronizer support is disabled) generated malformed Daml values for the choice argument and choice result of the Archive choice of the purge contract events in the Ledger API event store. This previously caused the Ledger API streams reading the generated Archive events to crash. The repair command now generates correct Daml values for the corresponding entries, that can be safely delivered by the Ledger API.
  • Fixed a bug in the repair service’s changeAssignation where only a single repair counter was allocated when reassigning multiple contracts, violating the monotonicity expected by the indexer.

Ledger API Multi-Synchronizer Events Alpha Support

Adds a new participant node parameter, alpha-multi-synchronizer-support (Boolean).
  • Default (false): Uses standard Create and Archive events.
  • Enabled (true): Uses Assign and Unassign events.
This flag is required in multi-synchronizer environments to preserve the reassignment counter of a contract. Using the default (Create events) resets this counter to zero. Note: Multi-synchronizer support is currently in Alpha; most Ledger API consumers may not yet be compatible with Assign/Unassign events. Only enable this if your application specifically requires non-zero reassignment counters and can process these event types.

Support for adding table settings for PostgreSQL

Added support for adding table settings for PostgreSQL. One can use a repeatable migration (Flyway feature) in a file provided to Canton externally.
  • Use the new config repeatable-migrations-paths under the canton.<node_type>.<node>.storage.parameters configuration section.
  • The config takes a list of directories where repeatable migration files must be placed, paths must be prefixed with filesystem: for Flyway to recognize them.
  • Example: canton.sequencers.sequencer1.storage.parameters.repeatable-migrations-paths = ["filesystem:community/common/src/test/resources/test_table_settings"].
  • Only repeatable migrations are allowed in these directories: files with names starting with R__ and ending with .sql.
  • The files cannot be removed once added, but they can be modified (unlike the V__ versioned schema migrations), and if modified these will be reapplied on each Canton startup.
  • The files are applied in lexicographical order.
  • Example use case: adding autovacuum_* settings to existing tables.
  • Only add idempotent changes in repeatable migrations.

Offline root namespace key scripts

Offline root namespace key scripts:
  • Renamed prepare-certs.sh to prepare-cert.sh
  • Changed assemble-certs.sh to automatically suffix the generated certificate with a .cert extension, similarly to what is being done in prepare-cert.sh
  • Removed the 10-offline-root-namespace-init example folder as its content is now integrated in the documented how-to: https://docs.digitalasset.com/operate/3.5/howtos/secure/keys/namespace_key.html
  • Committed the buf image necessary to run the script to the repository (also available in the release artifact), making usage from the open source repo easier

Reliability Improvements

  • Added a field MaxConcurrentCallsPerConnection and corresponding default defaultMaxConcurrentCallsPerConnection (set to 100000) to ServerConfig. This corresponds to max-concurrent-streams-per-connection in the app configs, e.g., docker/canton/images/canton-sequencer/app.conf and can be changed there. At present the value for sequencers is configured to be 500 for the public API and 100 for the Admin API.
  • Added network timeout and client_connection_check_interval for db operations in the Ledger API server and indexer to avoid hanging connections for Postgres (see PostgresDataSourceConfig). The defaults are 60 seconds network timeout and 5 seconds client_connection_check_interval for the Ledger API server, and 20 seconds network timeout and 5 seconds client_connection_check_interval for the indexer. These values can be configured via the new configuration parameters canton.participants.<participant>.ledger-api.postgres-data-source.network-timeout for network timeout of the Ledger API server and canton.participants.<participant>.parameters.ledger-api-server.indexer.postgres-data-source.client-connection-check-interval for the client_connection_check_interval of the indexer.
  • <canton-node>.replication.connection-pool.connection.client-connection-check-interval is introduced that allows configuring the PostgreSQL-specific client_connection_check_interval parameter for DB locked connections. This is a safety mechanism to prevent hanging connections in case of network issues. The default value is 5 seconds.
  • The Ledger API now enforces a maximum number of signatures per party that can be provided for external submissions. This value defaults to 50 and can be changed at the following config path: canton.participants.<participant_name>.ledger-api.interactive-submission-service.maximum-number-of-signatures-per-party
  • Added a new configuration parameter canton.participants.<participant_name>.ledger-api.index-service.max-lookup-limit that caps the maximum number of contracts returned by a contract key lookup per request. The default value is 1000.
  • When the AcsCommitmentProcessor is initializing, read stakeholder groups from the snapshot in batches of size canton.parameters.general.batching.max-stakeholder-groups-batch-size (default 1000), rather than all at once. This allows early termination of this initialization if the node is shutting down.
  • The release version is now exposed in NodeStatus.NotInitialized, so the node version can be retrieved even before the node is initialized.

Compatibility

The following Canton protocol versions are supported:
DependencyVersion
Canton protocol versions34, 35
Canton has been tested against the following versions of its dependencies:
DependencyVersion
Java RuntimeOpenJDK 64-Bit Server VM (build 21.0.10+7-nixos, mixed mode, sharing)
PostgresRecommended: PostgreSQL 17.9 (Debian 17.9-1.pgdg13+1) – Also tested: PostgreSQL 14.23 (Debian 14.23-1.pgdg13+1), PostgreSQL 15.18 (Debian 15.18-1.pgdg13+1), PostgreSQL 16.14 (Debian 16.14-1.pgdg13+1)