Skip to main content

Release of Canton 3.6.1

Canton 3.6.1 has been released on October 05, 2026.

Summary

This minor release brings new features, scalability improvements, as well as many other improvements. For scalability a complete redesign and new implementation of ACS commitments landed in protocol version 36.
  • Decouples the validator memory usage from the number of hosted parties and their stakeholder groups to lift party growth restrictions on the network
  • Constant-time effort for processing non-local party hosting changes for improved security and availability resilience
Daml now offers a new external call feature that allows to perform deterministic calls to extension services, which is supported as part of LF 2.4 and protocol version 36.

What’s New

New ACS commitment pipeline

ACS commitments have been completely reimplemented. The new implementation consists of three components:
  • The digest processor aggregates the ACS and the changes to it into per-party and per-counterparticipant digests.
  • The sender turns digests into commitments by signing them and sends them to the counterparticipants.
  • The matcher receives the commitments from counterparticipants and compares them to the local digests to detect mismatches.
The digest processor and matcher are enabled by default on all protocol versions. The sender is enabled by default for connected synchronizers with protocol version at least 36. The sender cannot be enabled for synchronizers with protocol version 35 or lower. The former ACS commitment processor is by default disabled for synchronizers with protocol version at least 36. The defaults can be changed via the configuration options canton.participants.<participant>.parameters.acs-commitment.enable-new-acs-commitment-processor (default true) and canton.participants.<participant>.parameters.acs-commitment.disable-old-acs-commitment-processor (default on-new-protocol-versions). Each time the new commitment processor is enabled afresh, the digest processor will reinitialize all the new digests from the current ledger end. While this happens in the background, the participant and its DB may see increased load. Conversely, whenever the new commitment processor is disabled, all the new digests are deleted from the DB to free the space. When the old commitment processor is reenabled after it has been disabled, commitments must be manually reinitialized on all connected synchronizers. Otherwise the old commitments are likely inconsistent with the ACS and commitment mismatches are to be expected. The existing ACS commitment inspection and tooling APIs work only against the old commitment processor. So when the old commitment processor is disabled, the APIs may produce outdated information or fail outright. In particular, no-wait and pruning configurations have no effect. The new commitment processor always operates in the safe-to-prune mode SAFE_TO_PRUNE_COMMITMENT_STATE_MATCH, i.e., outstanding commitments from counterparticipants never prevent pruning. The new commitment processor provides an API to trigger reinitialization <participant reference>.commitments.reinitialize_digest_commitments and query the status of reinitialization <participant reference>.commitments.digest_commitments_reinitialization_status. Reinitialization now also works when the participant is not connected to the targeted synchronizer. The new commitment processor also provides an API to trigger a consistency check of the ACS digests <participant reference>.commitments.run_digest_consistency_check and query the status of the check <participant reference>.commitments.digest_consistency_check_status. This can be used before performing a LSU. The new commitment processor exposes among others the following metrics. They are disjoint from the old commitment processor metrics.
  • daml.participant.sync.commitments.checkpoint-watermark tracks the progress of the digest processor in record time of the connected synchronizer. It corresponds to the former daml.participant.sync.commitments.last-locally-checkpointed metric.
  • daml.participant.sync.commitments.tick-watermark tracks the finished reconciliation interval of the digest processor. It corresponds to the former daml.participant.sync.commitments.last-locally-completed metric.
  • daml.participant.sync.commitments.received-watermark tracks the sequencing time of the latest received new ACS commitment. It corresponds to the former daml.participant.sync.commitments.last-incoming-received metric.
  • daml.participant.sync.commitments.matching-watermark tracks the record time up to where the matcher has progressed. It replaces the former daml.participant.sync.commitments.last-incoming-processed, which measures the end of the period of the received commitment instead of the record time of when the commitment was sequenced.
  • daml.participant.sync.commitments.sender.watermark-timestamp tracks the period end record time up to where the node has sent its commitments.
  • daml.participant.sync.commitments.digest-processor-health, daml.participant.sync.commitments.matcher-health, and daml.participant.sync.commitments.sender.sender-health, report on the health of the different components.
  • daml.participant.sync.commitments.running-digest-processor.loaded-digests counts the number of loaded digests in memory and gives an indication of the memory usage of the new processor.
  • daml.participant.sync.commitments.latest-matching-status tracks the matching status of the youngest reported commitment period. The metric is labeled with the counterparticipant.
  • daml.participant.sync.commitments.latest-matching-status-period-end tracks the timestamp of the end of the youngest reported commitment period. The metric is labeled with the counterparticipant.
With the new pipeline, journal garbage collection can now be controlled independently of the reconciliation interval: canton.participants.<participant>.parameters.journal-garbage-collection-minimum-gap (default 30 minutes) determines how frequently journal garbage collection shall be triggered if there is a continuous stream of ACS changes. Previously, this was tied to the reconciliation interval of the connected synchronizer.

Daml

Daml-LF 2.3 is now the default target version

A Daml project that does not specify an LF version in daml.yaml now compiles to Daml-LF 2.3 by default. Canton’s convention is to make an LF version the default one release after it first ships, and 2.3 shipped in 3.5. A project previously compiled for LF 2.2, whether by an explicit --target=2.2 or by relying on the prior default, will still operate. See Canton 3.5.1: Targeting LF 2.3 for the original guidance. To restore the previous behavior:
To set 2.3 explicitly:

Daml choices can now make external calls

Daml choices can call out to participant-configured extension services during interpretation, configured through ExtensionServiceConfig under:
For more information, please refer to the section External Calls Usage in the public documentation. The submitting participant executes each call and records the result in the transaction. Confirming participants independently re-validate that result against their own copy of the extension service before approving. Any disagreement is rejected and logged. The feature requires Daml-LF 2.4 or later and protocol version 36 or later. For externally signed transactions, the recorded call results are covered by the prepared transaction’s signed hash (hashing scheme version 4) available from protocol version 36. Daml-LF version lifecycle. The external_call feature is now released and enabled from LF 2.4 onward, together with protocol version 36. Daml-LF 2.4 is stable in this release. Daml Script gains a matching submit error:

Daml Script: cross-SDK dependencies

Daml Script now supports dependencies on packages built with a different SDK version, through data-dependencies. All participating daml-script versions must be 3.6.0 or later. A 3.6.x script cannot depend on a 3.5.x script, and the script runner must be at least as up to date as the highest daml-script version in use. Action required: submit-error types renamed (breaking). SubmitError, UpgradeErrorType, CryptoErrorType, ExternalCallErrorType, and DevErrorType are renamed to their Any<X> equivalents, and now use an IsX typeclass providing fromX and toX functions for each error type. Pattern synonyms preserve most of the existing source syntax, but some fields had to be renamed to stay unique. See Compatibility.daml in the daml-script sources for the mapping.

Daml Stdlib updates

  • (:|) and (<|) are added as synonyms for DA.NonEmpty, and Show is defined using the same representation.
  • Logic.reduce no longer incorrectly merges a nested Disjunction as a Conjunction.
  • toNNF in DA.Logic is now idempotent for stacked negations.
  • toDNS in DA.Logic is now idempotent for stacked negations.

Participant Query Store (PQS): renamed from Scribe

Overview

Scribe is renamed to Participant Query Store (PQS) across every deployment artifact. During the transition, both the new participant-query-store and the legacy scribe name, including their Docker images, are published side by side. Both configure the same pqs command and the same java -jar pqs.jar entrypoint, so existing deployments keep working while teams migrate to the new names. Environment variables using the new PQS_ prefix, for example PQS_TARGET_POSTGRES_HOST, are now supported. The old SCRIBE_ prefix still works as a fallback, but it now prints a deprecation warning. Migrate to PQS_ going forward. The target release to remove the deprecated scribe artifacts and configurations is Canton 3.8.

Action required: renamed deployment artifacts (breaking)

Anything that references Scribe’s binary names, container metadata, or metric prefixes needs to be updated: PQS configuration also no longer supplies default Postgres credentials. Supplying --target-postgres-username and --target-postgres-password, or the corresponding PQS_TARGET_POSTGRES_USERNAME and PQS_TARGET_POSTGRES_PASSWORD environment variables, is now mandatory.

SQL and query changes

Interface view rows no longer store contract_key_hash. Previously the hash of the underlying template’s contract key was duplicated onto every interface view row, even though contract_key was already empty there. Both columns are now empty for interface views, and the hash remains available on the template row. Upgrading clears the hash from interface view rows already stored. SQL migration V042__Clear_contract_key_hash_for_interface_views.sql performs this cleanup, with impact under 1 minute. A new print_create_index_for_contract SQL function generates the statement to create a concurrent index for a contract. A new --target-postgres-properties-<key>=<value> flag passes arbitrary pgjdbc connection properties through to the driver, enabling driver-level features such as JDBC authentication plugins, for example Azure Entra ID:

Reliability improvements

A performance optimization is that the core SQL functions now compute the nearest offset only once per query instead of repeatedly: creates, exercises, active, and archives. A timestamp (or duration) prune target older than all recorded history is now treated as a successful no-op instead of raising an error.

Experimental Post Quantum Cryptography

Added experimental support for ML-DSA. Currently only ML-DSA-65 is supported. Experimental algorithms need to be explicitly enabled via a node’s crypto configuration in <node>.crypto.enable-experimental = true.

Ledger API Improvements

Lookup by Transaction Hash

The Ledger API update service now exposes a GetUpdateByHash endpoint. Given a transaction hash, it returns the corresponding transaction if the caller has visibility over it. The hash is available on externally-signed (Interactive Submission Service) transactions.
  • gRPC: UpdateService.GetUpdateByHash
  • JSON API: POST /v2/updates/update-by-hash

Completion Lookup by Transaction Hash

The Ledger API command completion service now exposes a GetCompletionByHash endpoint. Given a transaction hash, it returns the accepted completion (if any) and recent rejected completions for that hash. The hash is available on externally-signed (Interactive Submission Service) transactions.
  • gRPC: CommandCompletionService.GetCompletionByHash
  • JSON API: POST /v2/commands/completion-by-hash

Party JWTs (self-signed JWTs)

  • The Ledger API now exposes a GetJwks endpoint. This can be used to obtain public keys for specific parties in JWK format.
    • gRPC: JoseService.GetJwks
    • JSON API: GET /v2/jose/jwks/synchronizer/<synchronizer-id>/party/<party-id>
  • A type = party-jwt can be added to participants.<participant>.ledger-api.auth-services to enable this feature.

New Sequencer Aggregator

Re-implemented the Sequencer Aggregator to be more resilient to misbehaving sequencers. Switching between the old and new implementation is controlled by the sequencer-client.use-new-aggregator configuration option, which defaults to true. One of the improvements allows the aggregator to detect sequencers that provide an incorrect event after that event has already reached consensus with sufficiently many other sequencers. A cache of past processed events is kept for that purpose, whose size is controlled by the sequencer-client.past-events-cache-size configuration option (default: 1000).

Support for Postgres 18

Canton is now supported on Postgres 18. Heads-up: Postgres 14 is becoming end-of-life by Nov 2026 and support of PG14 will be removed in future Canton versions.

Traffic Enforcement App

  • Add admin endpoint to prune events from the traffic enforcement event table - PruneEvents. This is used to keep the event table from growing indefinitely. This does not affect the account balance. WARNING: Affects de-duplication. If an event is pruned, de-duplication UpdateAccount requests on it will NOT be possible.
  • The configuration field for the traffic enforcement app has been renamed from traffic-enforcement to traffic-accounting. The old name is still accepted for backwards compatibility, but will be removed in a future release.

Synchronizer Limits

Added SynchronizerLimits in the StaticSynchronizerParameters, which are size limits on various collections, globally enforced by all synchronizer members. These limits are effective only starting with PV36. Synchronizer operators are required to choose explicit values for those limits when upgrading to PV36. Note: when using the console to bootstrap a synchronizer, the console will automatically set the limits to default values. However we strongly recommend to explicitly choose those values instead of relying on the defaults. When interactive with the gRPC API directly, limits must be explicitly set on the StaticSynchronizerParameters protobuf message when bootstrapping a synchronizer on PV36.

String Validation

Strings in protocol messages and APIs are strictly validated in protocol version 36 to be valid UTF16, not contain invalid escape characters or null characters. Invalid strings will be rejected during parsing.

Improved Sequencer Logging

On the sequencer, the log line mentioning all events in a block now also can contain the outcome of the event. By setting canton.sequencers.sequencer.parameters.enable-async-sequencer-logging = true, the logging will be moved to the end of the block processing, but will include the outcome of the events in the block. The default remains false to preserve the current behavior. Note that as part of this change, the sequencer-id of the traffic control metrics and of the block event processor metrics dropped the superfluous leading “SEQ::” string.

Heavy DB Migrations

The following DB migrations are applied automatically at startup and may have a impact on startup times the first time.

lapi_events_party_to_participant Database Migration

On upgrade, the participant database is migrated as follows:
  • A party column is added to the lapi_events_party_to_participant table. The migration takes roughly 30 seconds per 1 million parties.
  • The external_string column of the string_interning table now enforces a not null constraint. This is a no-op for existing data, but prevents future inserts of null values.

Reassignment store database migration

On upgrade, a participant database migration updates the reassignment store: it backfills the new stakeholders column from the persisted contracts and drops the old contracts column. This runs automatically as part of the startup migration step. Its duration scales with the number of reassignments in the store, taking roughly 1 second per 10,000 reassignments.

Breaking changes

Removal of Deprecated Configuration keys

  • The following configuration keys were deprecated in 3.5 and no long exist in 3.6; and need to be removed from the configuration files:
    • `topology.use-new-processor
    • topology.use-new-client

Admin API

Canton console command <sequencerReference>.setup.initialize_from_lsu_predecessor, admin console command class SequencerAdminCommands.InitializeFromLsuPredecessor and the respective proto InitializeSequencerFromLsuPredecessorRequest now require to specify synchronizerId (logical) on the request.

Sequencer API

The sequencers now also enforce confirmation throughput caps. If throughput caps are configured, they will also apply to confirmations. The cap for confirmations can be configured with confirmation-response.per-client-tps-cap. It should be set slightly above the global confirmation request cap, as a member should not need to confirm more requests than can be submitted globally. The confirmation-response.global-tps-cap can be set to the maximum sequencer event capacity.

Error Codes

(Potentially) Breaking: Aggregatable submissions are now rejected eagerly to preserve bandwidth. This means that the submission error code SEQUENCER_AGGREGATE_SUBMISSION_ALREADY_SENT may now also be returned during the synchronous submission of the sequencer, as the state of the aggregation is also checked before ordering. In addition, the gRPC error code has been modified from FAILED_PRECONDITION to ALREADY_EXISTS to better reflect the nature of the error. Clients should be updated to handle this error code accordingly. Due to backwards compatibility, the old gRPC error code will be returned for PV35 and before on the async path, and the new capability must only be turned on when all nodes have been upgraded to a Canton version that supports this change. The new capability can be enabled using canton.sequencers.seq.parameters.enable-reject-delivered-aggregations-on-pv-35 = MED for mediators. This can be combined with the new configuration option of the mediator canton.mediators.mymediator.parameters.delayed-verdict-sender.enabled = true. Generally, the sequencer will send out the verdict after reaching the threshold. All subsequent sent verdicts are thrown away. The new option now allows threshold + extra verdicts to be sent immediately, while the rest of the mediators will wait a short amount of time. This allows to reduce the load on the sequencer by 30%, creating more capacity for other transactions.

Scala Console

The following changes may impact bootstrap scripts or console usage:
  • Removed the protocolVersion parameter from all <node>.topology.<mapping>.list console commands as it was not working properly.
  • The com.digitalasset.canton.config.PositiveInt.increment method now returns an Either[InvariantViolation, PositiveInt] instead of a PositiveInt.
  • The com.digitalasset.canton.config.NonNegativeNumeric.increment method now returns an Either[InvariantViolation, NonNegativeNumeric] instead of a NonNegativeNumeric.
  • NonEmpty has moved from com.daml.nonempty to com.digitalasset.nonempty, and its scalaz type class instances have been replaced by cats ones. Separately, scalaz has been removed from the published Daml-LF modules: for example the Order and Equal instances on Value.ContractId are now a standard Scala Ordering.

Security

TLS Versions

Public API servers now reject client connections using TLS 1.0 and 1.1. Clients must use TLS 1.2 or higher. This security enforcement aligns all public APIs with the existing Admin API requirement. For details on TLS version deprecations, see RFC 8996.

TLS Default Cipher Suites

Updated the list of default cipher suites according to the current OWASP recommendations. The list of removed suites:
  • TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
The list of new suites:
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
  • TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
To use non-default cipher suites for backwards compatibility, set the following values in the config (not recommended):
  • canton.participants.<participant>.ledger-api.tls.ciphers if you have canton.participants.<participant>.ledger-api.tls set already
  • canton.sequencers.<sequencer>.public-api.tls.ciphers if you have canton.sequencers.<sequencer>.public-api.tls set already
For full compatibility, set the entire old list of suites:

Metrics

Ledger API and Indexer Metrics

  • The indexer queue metrics daml.participant.api.indexer.indexer_queue_blocked, daml.participant.api.indexer.indexer_queue_buffered and daml.participant.api.indexer.indexer_queue_uncommitted are now exposed as gauges instead of meters.
  • The metric daml.participant.api.services.pruning.contract_pruning_retried, which tracks how many times contract pruning was retried, is now a histogram.
  • Removed the unused metrics daml.participant.api.lapi.streams.transaction_trees_sent and daml.participant.api.index.transaction_trees_buffer_size.

ACS Commitment Metrics

ACS commitment metrics distinguish the synchronizer alias and distinguished counterparticipants via a label instead of including it in the metric name. Affected are the following metrics:
  • daml.participant.sync.commitments.<synchronizer-alias>.counter-participant-latency.<participant> -> daml.participant.sync.commitments.counter-participant-latency
  • daml.participant.sync.commitments.<synchronizer-alias>.largest-counter-participant-latency -> daml.participant.sync.commitments.largest-counter-participant-latency
  • daml.participant.sync.commitments.<synchronizer-alias>.largest-distinguished-counter-participant-latency -> daml.participant.sync.commitments.largest-distinguished-counter-participant-latency

ACHS Processing Metrics

Fine-grained metrics for Active Contracts Head Snapshot (ACHS) processing. Please note ACHS is disabled by default.
  • Removed database metric daml.participant.api.indexer.achs_processing.* which captured runtime metrics for all types of database calls.
  • Added the following metrics to capture individual database metrics for ACHS initialization (establish snapshot baseline):
    • daml.participant.api.indexer.achs_processing.initialization.store_achs_valid_at
    • daml.participant.api.indexer.achs_processing.initialization.update_achs_last_pointers
    • daml.participant.api.indexer.achs_processing.initialization.add_activations_to_achs
    • daml.participant.api.indexer.achs_processing.initialization.remove_deactivated_from_achs
  • Added the following metrics to capture individual database metrics for ACHS maintenance (during indexing):
    • daml.participant.api.indexer.achs_processing.maintenance.store_achs_valid_at
    • daml.participant.api.indexer.achs_processing.maintenance.update_achs_last_pointers
    • daml.participant.api.indexer.achs_processing.maintenance.add_activations_to_achs
    • daml.participant.api.indexer.achs_processing.maintenance.remove_deactivated_from_achs

Configuration

Renamed participant parameter commit-after-failed-activeness-check

The participant parameter commit-after-failed-activeness-check has been renamed to crash-after-failed-validation, and its meaning is inverted. The default behavior is unchanged, so no action is needed unless the parameter was set explicitly: The old name is no longer accepted, so a configuration that still sets it is rejected at startup.

Minor Breaking Configuration Changes

  • Unknown config keys are now making config parsing failing. This mechanism was already in place, but it didn’t include all the config keys, which is now fixed.
  • Separated the config for support of dev and alpha protocol versions. In order to use pv=dev, you now have to specify dev-version-support = true instead of alpha-version-support = true (canton.parameters.non-standard-config = true is still needed).

Artifacts

  • kms-driver-api and kms-driver-testing are now published to Maven Central, and will no longer be available in Artifactory.
  • canton-protobuf.zip has been renamed to canton-api.zip to reflect that the archive now contains more than protobuf files.

Deprecations

Legacy Ledger API / JSON API endpoints disabled by default (removal in 3.7)

The Ledger API and Ledger JSON API endpoints and request fields that were deprecated in Canton 3.4 and 3.5 remain available in Canton 3.6, but they are now disabled by default. Calls to them fail with the DEPRECATED_API_DISABLED error (gRPC status FAILED_PRECONDITION) unless the participant is started with the feature flag that the error message names:
There is one flag per release in which the APIs were deprecated, and within a release one for the endpoints and gRPC methods that were deprecated as a whole and one for the deprecated request fields of endpoints that stay, so that an application that only has to shed a few request fields does not have to re-enable the endpoints it has already migrated away from. The flags are set per participant node. HTTP requests are rejected before the request body is read; on the deprecated WebSocket endpoints the connection is established and the error is sent as the first (and only) message. The endpoints stay documented in the OpenAPI/AsyncAPI definitions, marked as deprecated. They will be removed in Canton 3.7; please migrate to their replacements. Covered by canton.participants.<participant>.features.deprecated.enable-deprecated-endpoints-34:
  • gRPC InteractiveSubmissionService.GetPreferredPackageVersion and the console command preferred_package_version. Use GetPreferredPackages / preferred_packages instead, which resolve the preferred packages for one or more package-name vetting requirements in a single call.
  • GET /v2/interactive-submission/preferred-package-version. Use POST /v2/interactive-submission/preferred-packages instead.
  • (WebSocket) GET/POST /v2/updates/trees. Use /v2/updates instead with updateFormat.includeTransactions.transactionShape = TRANSACTION_SHAPE_LEDGER_EFFECTS
  • (WebSocket) GET/POST /v2/updates/flats. Use /v2/updates instead with updateFormat.includeTransactions.transactionShape = TRANSACTION_SHAPE_ACS_DELTA
  • GET /v2/updates/transaction-tree-by-offset/{offset}. Use POST /v2/updates/update-by-offset instead with updateFormat.includeTransactions.transactionShape = TRANSACTION_SHAPE_LEDGER_EFFECTS.
  • POST /v2/updates/transaction-by-offset. Use POST /v2/updates/update-by-offset instead with updateFormat.includeTransactions.transactionShape = TRANSACTION_SHAPE_ACS_DELTA
  • GET /v2/updates/transaction-tree-by-id/{update-id}. Use POST /v2/updates/update-by-id instead with updateFormat.includeTransactions.transactionShape = TRANSACTION_SHAPE_LEDGER_EFFECTS
  • POST /v2/updates/transaction-by-id. Use POST /v2/updates/update-by-id instead with updateFormat.includeTransactions.transactionShape = TRANSACTION_SHAPE_ACS_DELTA
  • POST /v2/commands/submit-and-wait-for-transaction-tree. Use POST /v2/commands/submit-and-wait-for-transaction with transactionFormat.transactionShape = TRANSACTION_SHAPE_LEDGER_EFFECTS instead. Note that the response carries a flat transaction.events array rather than a transactionTree.eventsById map.
Covered by canton.participants.<participant>.features.deprecated.enable-deprecated-parameters-34:
  • The deprecated filter and verbose fields of (WebSocket) GET/POST /v2/updates and (WebSocket) GET/POST /v2/state/active-contracts. Use updateFormat respectively eventFormat instead.
Covered by canton.participants.<participant>.features.deprecated.enable-deprecated-endpoints-35:
  • GET /v2/package-vetting. Use POST /v2/package-vetting/list instead.
  • POST /v2/package-vetting. Use POST /v2/package-vetting/update instead.
  • GET /v2/state/active-contracts-page. Use POST /v2/state/active-contracts-page instead.

Reminder: Legacy Ledger API / JSON API endpoints will be removed in version 3.7.

The Ledger API and JSON API endpoints and request fields deprecated in Canton 3.4 and 3.5 (see “Legacy Ledger API / JSON API endpoints disabled by default” above) are disabled by default in 3.6 and can only be re-enabled temporarily with canton.participants.<participant>.features.deprecated.enable-deprecated-endpoints-34, canton.participants.<participant>.features.deprecated.enable-deprecated-parameters-34 respectively canton.participants.<participant>.features.deprecated.enable-deprecated-endpoints-35. They will be removed in Canton 3.7.

Reminder: Support for scope-based access tokens will be removed in version 3.7

  • “Scope-based” access tokens, i.e. JWTs without any audience specified, have been deprecated in version 3.5.
  • Versions 3.5 and 3.6 allow configurations using the default or explicitly configured target audiences, and log a warning for non-compliant configurations.
  • In release 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 be repurposed to serve exclusively as an additional, optional claim for fine-grained permissions.

Logback upgraded to 1.6 deprecates conditionals

The <if condition= syntax is now deprecated due to security vulnerabilities. The recommended style is to use the <condition ...><if> syntax. For more information on conditional configuration see: https://logback.qos.ch/manual/configuration-conditional.html

Minor Deprecations

  • StaticSynchronizerParameters.defaultsWithoutKMS has been deprecated in favor of StaticSynchronizerParameters.defaults. Supported cryptographic schemes now have parity between KMS and non-KMS configurations.
  • The ledger-api-server-parameters.contract-id-seeding configuration parameter is deprecated and no longer used. The contract ID seeding now uses the same random source as the rest of Canton, which is the equivalent of the strong type.
  • Ledger JSON API /v2/state/active-contracts-page is now available via POST; the GET variant that expects a request body is deprecated.
  • Deprecated configuration settings: canton.participants.<participant>.parameters.ledger-api-server.indexer.use-weighted-batching and canton.participants.<participant>.parameters.ledger-api-server.indexer.submission-batch-insertion-size. These are no longer supported.

Minor Improvements

  • Mediator: Fix for the mediator verdict sender to correctly stop retrying to send verdicts if the sequencer reports that the verdict has already been successfully aggregated.
  • Minor performance fix: time proofs have now a max sequencing timeout of 2 minutes and they no longer create a performance regression during synchronizer catch-up. Furthermore, synchronous rejects were not properly cleaned up by the sequencer client immediately after the reject was processed, but relied on the timeout logic to pick up the requests.
  • Improved observability of indexer initialization (all related logs populated with a newly forged trace-context, terminating log-reporter upon failed ACHS initialization, additional WARN logs on failed ACHS initialization).
  • Improved observability of Ledger API streaming (improved DEBUG log markers to of ID queries, DEBUG logging with timing information for payload queries).
  • InternalIndexService streams improved with default retry/recovery and better observability.
  • Database network timeout errors reported via error code INDEX_DB_SQL_NETWORK_TIMEOUT_ERROR error category changed to TransientServerFailure making it retryable. This problems logged on WARN log level.
  • The HTTP server now rejects too deeply nested json structures. The check uses the same values as the already existing gRPC check for nested daml records. This means that the Ledger JSON API client may now observe an error from HTTP (BadRequest) where previously the INVALID_ARGUMENT/COMMAND_PREPROCESSING_FAILED or INVALID_ARGUMENT/VALUE_NESTING error was returned.
  • The submitter does not have to be a stakeholder of all contracts during automatic reassignment, only of those that are actually reassigned to a target synchronizer. The previous check was considered too strict: it required the submitter to be a stakeholder of all involved contracts, even ones that were, for instance, disclosed contracts already on the target synchronizer.
  • The HTTP server for the Ledger JSON API is now explicitly configured with a maximum content length. A new config option http-ledger-api.max-inbound-message-size has been added. If not configured, the gRPC setting ledger-api.max-inbound-message-size will be used. Previously, an implicit limit of 8 MB was used, so this change should not affect existing configurations.
  • The concurrency limit interceptor ActiveRequestInterceptor now caches rejection responses instead of generating a new error (and thereby filling the stack trace) for each rejected response, once the concurrency limit is filled.
  • Onboarding party submission prevention: Ensures a participant does not submit a transaction or reassignment on behalf of an onboarding party.
  • OpenAPI and AsyncAPI files are now included in the API archive, and the bundle is published as a Maven artifact on GAR.
  • New metric to track the number of active stakeholder groups of a participant: daml.participant.sync.commitments.active-stakeholder-groups
  • <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.
  • <canton-node>.replication.connection-pool.connection.max-inconclusive-read-only-checks is introduced that allows configuring the maximum number of inconclusive read-only checks before a connection is closed. The default value is 3.
  • Connection pool metrics:
    • Add a psid label, populated if it is provided when connecting. This should be the case starting from the second connection to a synchronizer, or upon LSU.
    • Close the connection-health and subscription-health metrics associated to the psid when the pool is closed, instead of closing all the existing ones when the pool is started.
  • Updated com.google.protobuf libs from 3.25.5 to 4.35.1
  • A call to AcknowledgeSigned with a timestamp before the upgrade time returns immediately, without any acknowledgement being done.
  • The ActAsAnyParty access right has been added to the Ledger API. This allows a user to submit transactions on behalf of any party. This feature is intended for use cases where a user needs to act on behalf of multiple parties, such as in a multi-tenant environment. The new access right can be granted to the users only by a participant administrator either at user creation time or through the GrantAccessRight command.
  • The JSON Ledger API now rejects malformed identifiers (e.g. template-id) provided in requests (one that does not follow the <package>:<moduleName>:<entityName> format) with a descriptive 400 Bad Request error that names the expected format, instead of surfacing it as an internal error.
  • The JSON Ledger API now honours an optional, client-supplied Request-Timeout header (value in milliseconds) that sets a per-request timeout. The value is validated against a configurable [lower-bound; upper-bound] window (defaulting to [1s; 60s]): values within the window are enforced as-is, values above, or below the lower bound, a non-positive value, or a non-numeric value is rejected with a descriptive 400 Bad Request. When the header is absent, the server default request timeout applies unchanged. The window can be configured under http-ledger-api.client-request-timeout:
  • The default size of the Ledger API in-memory fan-out buffer (<participant>.ledger-api.index-service.max-transactions-in-memory-fan-out-buffer-size) has been increased from 1000 to 1100 to accommodate serving ACS commitments from the buffer.
  • Health check service improvements:
    • liveness gRPC health check is now up and reporting as SERVING before the database migrations are performed (when used with migrate-and-start = true), to address the issue of k8s liveness probe failures during long-running migrations.
    • Mediators will report readiness NOT_SERVING when liveness is also NOT_SERVING, where previously it was possible for a mediator to report readiness SERVING while liveness was NOT_SERVING.
    • HTTP health checks now expose the liveness and readiness, under the URIs /health/liveness or health/live and /health/readiness or /health/ready endpoints, respectively. /health is still available for backward compatibility, mapping to readiness.
  • Improved log trace correlation in the JSON Ledger API: package and health endpoints that previously logged with an empty trace context now propagate the caller’s TraceContext.
  • Protocol messages now use Zstandard (zstd) compression starting with protocol version 36, while earlier protocol versions continue to use gzip. This internal optimization is applied automatically and does not require configuration changes.
  • getLedgerEnd endpoint in StateService can now return latest observed record time for the requested synchronizers along with ledger end offset.
  • Participant health state now includes indexer as a soft dependency. Indexer health state will be present in readiness endpoint response, but it won’t influece response code.
  • Removed redundant root-hash signature from informee and encrypted view messages.
  • participant_id label is added onto participant metrics
  • Added ErrorInfo metadata to traffic enforcement rejections so callers don’t need to parse the message text.
    • TRAFFIC_ACCOUNT_VALIDATION_FAILED: account_id, balance, traffic_cost
    • TRAFFIC_UPDATE_OUT_OF_BOUND: account_id, traffic_delta, delta_type
  • Support for OTLP remote metrics reporting, including optional OAuth2 Client Credentials authentication
  • AWS KMS keys created by Canton can now be configured with custom tags through the custom-tags setting.
  • The default of the indexer’s uncommitted-queue warning threshold (<participant>.ledger-api.indexer.queue-uncommitted-warn-threshold) has been increased from 5000 to 14000. This threshold controls when the Uncommitted queue is growing too large warning is emitted.
  • Admin API streaming calls are now bounded to protect nodes from resource exhaustion:
    • Their duration is capped by the new <node>.parameters.processing-timeouts.admin-stream-open-bound value (default: 2 hours).
    • The number of concurrently open calls is capped per method (default: 10) via the admin server’s limits configuration.
  • Changing identity providers for a party through UpdatePartyDetails is no longer possible, UpdatePartyIdentityProviderId must be used instead.
  • An internal change is introduced which grants ReadAsAnyParty permissions to the traffic enforcement service. This removes the need to use non-standard config options to run local traffic enforcement with auth enabled:
  • The participant admin party is now exempt from the traffic enforcement balance check during submission.
  • Subview package vetting, checked at submission time, is now verified during confirmation request validation.
  • Fixed the parsing of the Sec-WebSocket-Protocol header in the Ledger JSON API. As specified by RFC 6455, clients send the subprotocol values separated by a comma and a space (for example daml.ws.auth, jwt.token.<token>). Canton split the header on the comma only, so the leading space stayed attached to the following value and that value was silently discarded, making the request fail with an UNAUTHENTICATED error. The values are now trimmed, so such requests are accepted.
  • The Ledger API vetting endpoints are exposed in the Canton console:
    • The Ledger API endpoint PackageService.ListVettedPackages is exposed as participant.ledger_api.packages.list_vetted_packages
    • The Ledger API endpoint PackageManagementService.UpdateVettedPackages is exposed as participant.ledger_api.packages.update_vetted_packages
  • Added the request type to the sequencer cap rejection message.
  • PrefetchContractKey has a new optional limit field stating how many contracts to prefetch for that key, on top of disclosed contracts. Absence is interpreted as 1 (the previous behavior), 0 is rejected, and values are capped at 2^31 - 1. The system may impose further limits.
  • Commands referencing a contract ID whose Canton contract ID version is not supported (for example a malformed or foreign-format ID, including via a disclosed contract) are now rejected with UNSUPPORTED_CONTRACT_ID (gRPC NOT_FOUND), carrying the contract ID as an error resource. Previously this path raised an internal error. The same applies to contract key lookups resolving to such a contract.
  • canton.participants.<participant>.parameters.engine.transaction-limits bounds what the engine may produce during interpretation (value-size, node-children, transaction-nodes, total-informees, and others). All bounds default to their maximum, so the default behavior is unchanged.
  • The nonempty and base-validation libraries are now published to Maven Central.
  • The docker da-base-image has been bumped to 1.0.15 to fix an issue with bash in certain kubernetes environments.
  • Sequencer: fixed the V2 sequencer initialization endpoints (InitializeSequencerFromGenesisStateV2, InitializeSequencerFromLsuPredecessor, InitializeSequencerFromOnboardingStateV2) failing when the uploaded state exceeded 2GB, the maximum size of a single protobuf ByteString.
  • Parsing of protobuf messages carrying an unknown version number is now failing.
  • Participant: fixed the memory growth during ACS import caused by the repair indexer keeping all imported contract activations in an in-memory cache until the end of the import.

Compatibility

The following Canton protocol versions are supported: Canton has been tested against the following versions of its dependencies: