Static Configuration¶
Canton differentiates between static and dynamic configuration. Static configuration is immutable and therefore has to be known from the beginning of the process start. An example for a static configuration are the connectivity parameters to the local persistence store or the port the admin-apis should bind to. On the other hand, connecting to a domain or adding parties however is not a static configuration and therefore is not set via the config file but through console commands (or the administration APIs).
The configuration files themselves are written in HOCON format with some extensions:
- Durations are specified scala durations using a
<length><unit>
format. Valid units are defined by scala directly, but behave as expected usingms
,s
,m
,h
,d
to refer to milliseconds, seconds, minutes, hours and days. Durations have to be non-negative in our context.
Canton does not run one node, but any number of nodes, be it domain or participant nodes in the same process. Therefore, the root configuration allows to define several instances of domain and participant nodes together with a set of general process parameters.
A sample configuration file for two participant nodes and a single domain can be seen below.
canton {
participants {
participant1 {
storage.type = memory
admin-api.port = 5012
ledger-api.port = 5011
}
participant2 {
storage.type = memory
admin-api.port = 5022
ledger-api.port = 5021
}
}
domains {
mydomain {
storage.type = memory
public-api.port = 5018
admin-api.port = 5019
}
}
// enable ledger_api commands for our getting started guide
features.enable-testing-commands = yes
}
Configuration reference¶
The Canton configuration file for static properties is based on PureConfig. PureConfig maps Scala case classes and their class structure into analogue configuration options (see e.g. the PureConfig quick start for an example). Therefore, the ultimate source of truth for all available configuration options and the configuration file syntax is given by the appropriate scaladocs of the CantonConfig classes.
When understanding the mapping from scaladocs to configuration, please keep in mind that:
- CamelCase Scala names are mapped to lowercase-with-dashes names in configuration files, e.g.
domainParameters
in the scaladocs becomesdomain-parameters
in a configuration file (dash, not underscore). Option[<scala-class>]
means that the configuration can be specified but doesn’t need to be, e.g. you can specify a JWT token viatoken=token
in a remote participant configuration, but not specifyingtoken
is also valid.
Configuration Compatibility¶
The enterprise edition configuration files extend the community configuration. As such, any community configuration can run with an enterprise binary, whereas not every enterprise configuration file will also work with community versions.
Advanced Configurations¶
Configuration files can be nested and combined together. First, using the include required
directive (with relative paths), a
configuration file can include other configuration files.
canton {
domains {
include required(file("domain1.conf"))
}
}
The required
keyword will trigger an error, if the included file does not exist;
without the required
keyword, any missing files will be silently ignored.
The file
keyword instructs the configuration parser to interpret its argument as a file name;
without this keyword, the parser may interpret the given name as URL or classpath resource.
By using the file
keyword, you will also get the most intuitive semantics and most stable semantics of include
.
The precise rules for resolving relative paths can be found here.
Second, by providing several configuration files, we can override configuration settings using explicit configuration option paths:
canton.participants.myparticipant.admin-api.port = 11234
If the same key is included in multiple configurations, then the last definition has highest precedence.
Furthermore, HOCON supports substituting environment variables for config values using the syntax
key = ${ENV_VAR_NAME}
or optional substitution key = ${?ENV_VAR_NAME}
, where the key will only be set
if the environment variable exists.
Configuration Mixin¶
Even more than multiple configuration files, we can leverage PureConfig to create shared configuration items that refer to environment variables. A handy example is the following, which allows to share database configuration settings in a setup involving several participant or domain nodes:
# Postgres persistence configuration mixin
#
# This file defines a shared configuration resources. You can mix it into your configuration by
# refer to the shared storage resource and add the database name.
#
# Example:
# participant1 {
# storage = ${_shared.storage}
# storage.config.properties.databaseName = "participant1"
# }
#
# The user and password is not set. You want to either change this configuration file or pass
# the settings in via environment variables POSTGRES_USER and POSTGRES_PASSWORD.
#
_shared {
storage {
type = postgres
config {
dataSourceClass = "org.postgresql.ds.PGSimpleDataSource"
properties = {
serverName = "localhost"
# the next line will override above "serverName" in case the environment variable POSTGRES_HOST exists
# which makes it optional
serverName = ${?POSTGRES_HOST}
portNumber = "5432"
portNumber = ${?POSTGRES_PORT}
# user and password are equired
user = ${POSTGRES_USER}
password = ${POSTGRES_PASSWORD}
}
}
parameters {
# If defined, will configure the number of database connections per node.
# Please note that the number of connections can be fine tuned for participant nodes (see participant.conf)
max-connections = ${?POSTGRES_NUM_CONNECTIONS}
# If true, then database migrations will be applied on startup automatically
# Otherwise, you will have to run the migration manually using participant.db.migrate()
migrate-and-start = false
# If true (default), then the node will fail to start if it can not connect to the database.
# The setting is useful during initial deployment to get immediate feedback when the
# database is not available.
# In a production setup, you might want to set this to false to allow uncoordinated startups between
# the database and the node.
fail-fast-on-startup = true
}
}
}
Such a definition can subsequently be referenced in the actual node definition:
canton {
domains {
mydomain {
storage = ${_shared.storage}
storage.config.properties.databaseName = ${CANTON_DB_NAME_DOMAIN}
}
}
}
Multiple Domains¶
A Canton configuration allows to define multiple domains. Also, a Canton participant can connect to multiple domains. This is however only supported as a preview feature and not yet suitable for production use.
In particular, contract key uniqueness cannot be enforced over multiple domains. In this situation, we need to turn contract key uniqueness off by setting
canton {
domains {
alpha {
// subsequent changes have no effect and the mode of a node can never be changed
init.domain-parameters.unique-contract-keys = false
}
}
participants {
participant1 {
// subsequent changes have no effect and the mode of a node can never be changed
init.parameters.unique-contract-keys = false
}
}
}
Please note that the setting is final and cannot be changed subsequently. We will provide a migration path once multi-domain is fully implemented.
Fail Fast Mode¶
Be default, Canton will fail to start if it cannot access some external dependency such as the database. This is
preferable during initial deployment and development, as it provides instantaneous feedback, but can cause problems
in production. As an example, if Canton is started with a database in parallel, the Canton process would fail if the
database is not ready before the Canton process attempts to access it. To avoid this problem, you can configure a node
to wait indefinitely for an external dependency such as a database to start. The config option below will disable
the “fail fast” behaviour for participant1
.
canton.participants.participant1.storage.parameters.fail-fast-on-startup = "no"
This option should be used with care as, by design, it can cause infinite, noisy waits.
Init Configuration¶
Some configuration values are only used during the first initialization of a node and cannot be changed afterwards. These values are located under the init section of the relevant configuration of the node. Below is an example with some init values for a participant config
participant1 {
init {
// example settings
ledger-api.max-deduplication-duration = 1 minute
parameters.unique-contract-keys = false
identity.node-identifier.type = random
}
}
The option ledger-api.max-deduplication-duration
sets the maximum deduplication duration that the participant’s ledger configuration service reports and uses for command deduplication.