canton v2.10.1
Release of Canton 2.10.1
Canton 2.10.1 has been released on May 30, 2025. You can download the Daml Open Source edition from the Daml Connect Github Release Section. The Enterprise edition is available on Artifactory.
Please also consult the full documentation of this release.
Summary
This is a maintenance release that fixes one high, one medium, and two low severity issues.
Please update during the next maintenance window.
What’s New
Daml packages validation on Ledger API start-up
On Ledger-API start-up, the Daml package store of the participant node
is checked for upgrade compatibility for all persisted packages.
On compatibility check failure, the participant is shut down with an error message.
To disable the check (not recommended), set canton.participants.participant.parameters.unsafe-disable-upgrade-validation=true.
JWT Tokens in Admin API
Background
User authorization is extended to all service types for participant nodes in 2.10 for the Canton Admin API.
Specific Changes
The users is able to configure authorization on the Admin API of the participant node in a manner similar to what
is currently possible on the Ledger API. However, it is necessary to specify explicitly which users are
allowed in and which grpc services are accessible to them. An example configuration for both Ledger and Admin API
looks like this:
canton {
participants {
participant {
ledger-api {
port = 5001
auth-services = [{
type = jwt-rs-256-jwks
url = "https://target.audience.url/jwks.json"
target-audience = "https://rewrite.target.audience.url"
}]
}
admin-api {
port = 5002
auth-services = [{
type = jwt-rs-256-jwks
url = "https://target.audience.url/jwks.json"
target-audience = "https://rewrite.target.audience.url"
users = [{
user-id = alice
allowed-services = [{
"admin.v0.ParticipantRepairService",
"connection.v30.ApiInfoService",
"v1alpha.ServerReflection",
}]
}]
}]
}
}
}
}
While the users appearing in the sub claims of the JWT tokens on the Ledger API always have to be present in
the participant’s user database, no such requirement exists for the Admin API. The user in the authorization service
config can be an arbitrary choice of the participant’s operator. This user also needs to be configured in the associated
IDP system issuing the JWT tokens.
The configuration can contain a definition of either the target audience or the target scope depending on the specific
preference of the client organization. If none is given, the JWT tokens minted by the IDP must specify daml_ledger_api
as their scope claim.
Independent of the specific service that the operator wants to expose, it is a good practice to also give access rights
to the ServerReflection service. Some tools such as grpcurl or postman need to hit that service to construct their requests.
Impact and Migration
The changes are backwards compatible.
LF 1.17 templates cannot implement LF 1.15 interfaces.
Background
Bug 25-005, detailed below, prevents the execution of choices from LF
1.15 interfaces within LF 1.17 templates. To resolve this, we have
entirely restricted LF 1.17 templates from implementing LF 1.15
interfaces. However, this change is more than just a bug fix—it
reflects a deliberate alignment with upgradeability principles.
While mixing LF 1.15 interfaces with LF 1.17 templates may seem
advantageous, it is only useful if your model contains other LF 1.15
templates implementing those interfaces. Yet, this approach ultimately
disrupts the upgrade path. Maintaining two versions of a template
within the same model leads to inconsistency: LF 1.17 templates
support seamless upgrades, whereas LF 1.15 templates do not, requiring
an offline migration to fully upgrade the model.
Specific Changes
-
The compiler now prevents Daml models from implementing an LF 1.15
interface within an LF 1.17 template. -
For backward compatibility, participants can still load these models
if they were compiled with SDK 2.10.0. However:- A warning is issued at load time, if those models contain an LF 1.17 template implementing an LF 1.15 interface.
- Any attempt to execute a choice on an LF 1.15 interface within an
LF 1.17 template (compiled with SDK 2.10.0) will trigger a runtime
error during submission.
Impact and Migration
These changes preserve backward compatibility, while preventing
the participant from crashing.
Minor Improvements
- The daml values representing parties received over the Ledger API can be validated in a stricter manner. When the
canton.participants.<participant>.http-ledger-api.daml-definitions-service-enabledparameter is turned on,
the parties must adhere to a format containing the hint and the fingerprint separated by a double colon:
<party-hint>::<fingerprint>. The change affects the values embedded in the commands supplied to theSubmit*calls
to theCommandSubmissionServiceand theCommandService. - Added configuration for the size of the inbound metadata on the Ledger API. Changing this value allows
the server to accept larger JWT tokens.
canton.participants.participant.ledger-api.max-inbound-metadata-size=10240 canton.participants.participant.parameters.disable-upgrade-validationis explicitly deemed a dangerous configuration by:- renaming it to
unsafe-disable-upgrade-validation. - only allowing its setting in conjunction with
canton.parameters.non-standard-config = yes
- renaming it to
Bugfixes
(25-005, Low): Cannot exercise LF 1.15 interface choices on LF 1.17 contracts
Issue Description
Daml-LF 1.17 versions exercise-by-interface nodes according to the
interface's language version. This ensures that the choice argument
and result are not normalized if the interface is defined in LF 1.15,
which is important for Ledger API clients that have been compiled against
the interface and expect non-normalized values. Yet, the package name
is still set on the exercise node based on the contract's language
version, namely 1.17.
This leads to at least two problems:
-
When Canton tries to serialize the transaction in Phase 7, the
TransactionCoderattempts to serialize the node with version 1.15
into the SingleDimensionEventLog, but cannot do so because package
names cannot be serialized in 1.15. This serialization exception
bubbles up into the application handler and causes the participant
to disconnect from the domain. This problem is sticky in that crash
recovery will run into the same problem again and disconnect
again. -
When the template defines a key, Canton serializes the global key
for the view participant according to the exercise node
version. Accordingly, the hash of the key according to LF
1.15. This trips up the consistency check in ViewParticipantData
because the input contract's serialized key was hashed according to
LF 1.17. This failure happens only Phase 3 during when decrypting
and parsing the received views. Participants discard such views and
reject the confirmation request. The failure does not happen in
Phase 1 because the global key hashes are still correct in Phase 1.
Serialization correctly sets the package name, but uses the wrong
version (1.15) from the exercise node. Deserialization sees the
wrong version and ignores the package name.
Accordingly, the participant discards the views it cannot
deserialize and rejects the confirmation request if it is a
confirming node.
Affected Deployments
Participant nodes.
Affected Versions
- 2.10.0
Impact
- Participant node crashes and may need manual repair.
- Command submission fails with
INVALID_ARGUMENT.
Symptom
If the template defines a contract key, the participant logs
LOCAL_VERDICT_MALFORMED_PAYLOAD with error message "Inconsistencies
for resolved keys: GlobalKey(...) -> Assigned(...)" and rejects the
confirmation request. The command submission fails with
INVALID_ARGUMENT
If the template does not define a contract key, the transaction gets
processed, but the participant fails to store the accepted transaction
in its database with the error message "Failed to serialize versioned
transaction: packageName is not supported by transaction version 15",
disconnects from the domain with an ApplicationHandlerFailure and
possible crashes (depending on configuration). Reconnection attempts
will run into the same problem over and over gain.
Workaround
Recompile the interfaces with LF 1.17. This may break ledger API
clients that expected trailing None record fields in exercise arguments
and results.
Likeliness
Deterministic.
Recommendation
Upgrade to 2.10.1. If your Daml models contain LF 1.17 templates
implementing LF 1.15 interfaces, and you face issues rewriting
everything in LF 1.17, please contact Digital Asset support.
(25-006, High): Confidential configuration fields are logged in plain text when using specific configuration syntax or CLI flags
Issue Description
If the logConfigWithDefaults config parameter is set to false (which is the default),
the config rendering logic fails to redact confidential information (e.g DB credentials) when config substitution is used in combination with a config element override.
Suppose we have the following configuration file:
canton.conf
_storage {
password = confidential
}
canton {
storage = ${_storage}
}Now the confidential config element is changed via another config file:
override.conf
canton.storage.password = confidential2and then:
canton -c canton.conf -c override.confThis exposes the confidential password field in the log file.
Alternatively the config element can be changed as well via the CLI:
canton -c canton.conf -C "canton.storage.password=confidential2"In both cases the password field will not be redacted as it should when the config is logged.
Affected Deployments
All nodes.
Affected Versions
- 2.10.0
- 2.9.0-2.9.7
- 2.8.0-2.8.12
Impact
Confidential information in the config will be logged during node startup if one of the conditions described is met.
Confidential information like credentials may be used to gain unauthorized access by an attacker who has access to the canton logs and network-level access to the affected systems like a database server with an exposed database credential.
Symptom
During startup, if the node is configured to log its configuration without defaults values (default behavior), confidential information that should otherwise be redacted may be logged.
Workaround
Update the config to not use the structure described in the issue for any confidential config field.
Likeliness
Deterministic if the configuration uses the structure described in the issue.
Recommendation
Upgrade in the next maintenance window if affected by this issue. Roll the exposed credentials.
(25-004, Medium): RepairService contract import discards re-computed contract keys in the repaired contract
Issue Description
The repair service re-computes contract metadata when adding new contracts.
However, instead of repairing the contract with the re-computed keys, it re-uses the keys from the input contract.
Combined with a gap in the console macros which do not propagate contract keys during ACS export,
migrating contracts with keys in that way can result in an inconsistency between the ACS and contract key store,
which crashes the participant when attempting to fetch a contract by key.
Affected Deployments
Participant nodes.
Affected Versions
- 2.10.0
- 2.9.0-2.9.6
- 2.8.x
Impact
Contracts with keys cannot be used after migration via ACS export / import.
Symptom
The participant crashes with
"java.lang.IllegalStateException: Unknown keys are to be reassigned. Either the persisted ledger state corrupted or this is a malformed transaction"
when attempting to lookup a contract by key that has been migrated via ACS export / import
Workaround
No workaround available. Update to 2.10.1 if affected.
Likeliness
Deterministic if migrating contracts with keys using ACS export to an unpatched version.
Recommendation
Upgrade to 2.10.1 if affected by this issue.
(25-007, Low) Insufficient package compatibility upload check for 1.15 packages
Issue Description
When a DAR/package is uploaded to a participant node in 2.x, Canton
checks the upgrade compatibility against the DARs already present in
participant's DAR/package stores. Together with the invariant that all
active contracts must have their creation package in the store; this
ensures that the stakeholder participants of a contract will only vet
packages that are compatible with the creation package of the
contract.
These upload checks are insufficient in 2.10.0: For an LF 1.17 package
with a data dependency on a LF 1.15 package, the compatibility check
at upload compares neither the package IDs nor the contents of the
referenced LF 1.15 packages, only the package names, the package
versions, and the module names and identifiers matter.
Suppose that LF 1.17 package pgkId2' upgrades the LF 1.17 package
pkgId2, i.e., pkgId2' has the same package name as pkgId2 and a
higher version number. The only relevant difference between pgkId2
and pgkId2' is that pgkId2 declares a data dependency on the LF
1.15 package pkgId1 with package name pkgName1 and version x
whereas pgkId2' declares the data dependency on the LF 1.15 package
pkgId1' with package name pgkName1 and version y with y >
x. Then, the compatibility check for pgkId2 and pgkId2' will
pass independently of the contents of pkgId1 and pkgId1', because
LF 1.15 packages are not upgradable.
Moreover, the Daml engine assumes that all contracts read from the index
DB ia type correct, i.e., it does not reverify them. This leads to
the following problems when using a contract created with pkgId2 with
the target package pkgId2':
-
On the one hand, if the type or value serialization differs, the
engine's behavior is entirely unspecified—it may fail with an
internal error or silently produce an undefined output. In such
cases, even determinism is not formally guaranteed, yet a code
review has not identified any instances of nondeterministic
behavior. -
On the other hand, if the types differ, but the value serialization
is the same, command interpretation will succeed and Canton commits
the transaction. However, the result may not be what was intended.
A concrete example:
-- in pkgId1
data IouData = IouData with
owner: Party
issuer: Party
-- in pgkId1', the order of the fields is swapped
data IouData = IouData with
issuer: Party
owner: Party
-- in pgkId2 and pgkId2':
template Iou with
data: IouData
amount: Double
where
signatory data.owner, data.issuer
choice Transfer: ContractId Iou with
newOwner: Party
controller data.owner, newOwner
do
create Iou with
data = IouData { owner = newOwner, issuer = data.issuer }
amount = amount
Since the order of the IouData fields are flipped in pgkId1', this
means that the roles of owner and issuer are flipped when the contract
choice executes with pkgId2'. That is, the issuer can now transfer the
owner's IOU and the owner cannot anymore.
Affected Deployments
Participant nodes.
Affected Versions
- 2.10.0
Impact
-
Command submission fails with
INVALID_ARGUMENT. -
Unexpected behavior of the Daml model.
-
Ledger fork is not entirely ruled out.
Workaround
Recompile all packages with LF 1.17. However, this may break backward
compatibility for clients expecting a trailing None field in records.
Likeliness
Should be deterministic.
Recommendation
Upgrade to 2.10.1. Before upgrading, verify that the package store of
each of your participants does not contain invalid upgrades by running
the following script in a Canton console connected to the respective
participant. If you encounter any errors, contact Digital Asset
support.
remote_participant.ledger_api.packages.check_upgrade_validity
where remote_participant is a reference to a remote Canton participant on version 2.10.0.
(25-008, None) Race between multi-build and multi-IDE
Issue Description
Multiple IDE instances may start processes that create a package
database while a multi-build is running. These simultaneous operations
can lead to collisions, corrupting the package database.
Running a daml build while interacting with daml studio can sometimes collide when creating/updating the package-database, leading to corruption.
Affected Deployments
None.
Affected Versions
- 2.10.0
- 2.9.x
Impact
The Daml build fails, resulting in a suboptimal user experience.
Symptom
daml build fails with a random files missing. Here is an example of error message:
Could not find module `Daml.Script'
It is not a module in the current program, or in any known package.
Also, for a package that has interfaces it throws an error:
2025-04-22 12:41:12.32 [INFO] [multi-package build]
Building C:/tmp/v2/
damlc: /tmp/v1/.daml\dependencies\1.17\e491352788e56ca4603acc411ffe1a49fefd76ed8b163af86cf5ee5f4c38645b: getDirectoryContents:findFirstFile: does not exist (The system cannot find the path specified.)
File: /tmp/v1/
Hidden: no
Range: 1:1-2:1
Source: compiler
Severity: DsError
Message:
Failed to build package at /tmp/v1/.
CallStack (from HasCallStack):
error, called at compiler\damlc\lib\DA\Cli\Damlc.hs:1240:44 in compilerZSdamlcZSdamlc-lib:DA.Cli.Damlc
Workaround
Close the IDE, kill all damlc processes, and then run daml clean --all followed by daml build --all.
Likeliness
Undeterministic.
Recommendation
Update your SDK to 2.10.1 or later.
(25-009, None) Upgrade runtime error details are swallowed by the IDE
Issue Description
The IDE does not show the error details when a runtime error occurs
during the upgrade process. This is because the IDE does not propagate
the error details from the Canton server to the client.
Affected Deployments
None.
Affected Versions
- 2.10.0
- 2.9.x
Impact
Daml script fails without details, resulting in a suboptimal user experience.
Symptom
Runtime upgrade errors are reported as <missing error details> in the IDE.
Workaround
Run the script against the sandbox.
Likeliness
Deterministic.
Recommendation
Update your SDK to 2.10.1 or later.
(25-010, None) Compiler emits wrong upgrade warning about changed precondition
Issue Description
The compiler performs best-effort checks to verify that the
expressions defining the ensure clause, signatories, observers, or the
key of a template remain unchanged between templates in an upgrade
relationship. If a change is detected, a warning is emitted. However,
if the precondition expression calls a utility package—such as the
standard library—a warning may be erroneously triggered.
Affected Deployments
None.
Affected Versions
- 2.10.0
Impact
Compilation emits an erroneous warning, resulting in a suboptimal user experience.
Symptom
The compiler emit a warning of the form:
warning while type checking template Main.U precondition:
The upgraded template U has changed the definition of its precondition.
There is 1 difference in the expression:
Name fde31f3683d30c9638fbd0ef4420e1acd5938673e96dc68db28ac85d6ad81b50:Dep:someBool and name f70bec8f7a75adf6649f9a5dcd3b95b4ff2daefafc88e81670fdc1d48ede85cc:Dep:someBool differ for the following reason: Just Name came from package 'dep (fde31f3683d30c9638fbd0ef4420e1acd5938673e96dc68db28ac85d6ad81b50) (v0.1)' and now comes from package 'dep (f70bec8f7a75adf6649f9a5dcd3b95b4ff2daefafc88e81670fdc1d48ede85cc) (v0.2)'. Both packages support upgrades, but the previous package had a higher version than the current one.
Upgrade this warning to an error -Werror=upgraded-template-expression-changed
Disable this warning entirely with -Wno-upgraded-template-expression-changed
This warning is incorrect because it incorrectly states that version
v0.1 has a higher version than v0.2, which is not true. In reality,
the upgrade process is valid, and no warning should be
emitted. Additionally, had the dependency versions been swapped, the
compiler would not have emitted a warning, which is also incorrect
behavior.
Workaround
Ignore the warning.
Likeliness
Deterministic.
Recommendation
Update you SDK to 2.10.1 or later.
(25-011, None) IDE does not close daml processes when closed
Issue Description
An incompatibility between the latest version of VS Code and the VS Code client library we are using causes the Daml process to remain open improperly on Windows and Mac, preventing it from closing as expected.
Affected Deployments
None.
Affected Versions
- 2.10.0
- 2.9.x
Impact
Accumulation of unneeded processes.
Workaround
Kill the processes manually.
Likeliness
Deterministic.
Recommendation
Update you SDK to 2.10.1 or later.
Compatibility
The following Canton protocol versions are supported:
| Dependency | Version |
|---|---|
| Canton protocol versions | 5, 7 |
Canton has been tested against the following versions of its dependencies:
| Dependency | Version |
|---|---|
| Java Runtime | OpenJDK 64-Bit Server VM Zulu11.72+19-CA (build 11.0.23+9-LTS, mixed mode) |
| Postgres | Recommended: PostgreSQL 12.22 (Debian 12.22-1.pgdg120+1) – Also tested: PostgreSQL 11.16 (Debian 11.16-1.pgdg90+1), PostgreSQL 11.16 (Debian 11.16-1.pgdg90+1), PostgreSQL 13.21 (Debian 13.21-1.pgdg120+1), PostgreSQL 13.21 (Debian 13.21-1.pgdg120+1), PostgreSQL 14.18 (Debian 14.18-1.pgdg120+1), PostgreSQL 14.18 (Debian 14.18-1.pgdg120+1), PostgreSQL 15.13 (Debian 15.13-1.pgdg120+1), PostgreSQL 15.13 (Debian 15.13-1.pgdg120+1) |
| Oracle | 19.20.0 |