Skip to content
Daml

Initializing node identity manually

Initializing node identity manually

Manual vs automatic identity initialization

A Participant Node is initialized automatically by default, though it can also be initialized manually. Manual initialization is useful in the following circumstances:

  1. When a Participant Node does not control its own identity.

  2. To avoid storing the identity key on the Participant Node for security reasons.

  3. To explicitly set the Participant Node keys rather than relying on keys auto-generated by the Participant Node.

To disable the automatic initialization of a Participant Node, add the following to the configuration:

canton.participants.participant1.init = {
    generate-topology-transactions-and-keys = false
    identity.type = manual
}

Start the Participant Node with the above configuration settings.

Key initialization

Besides a namespace key and signing keys, a Participant Node also has an asymmetric encryption key used to encrypt and decrypt transactions. By default, Participant Nodes create keys automatically, but if you choose to manually set up a Participant Node, use the following commands to generate the namespace, signing, and encryption keys:

// Create a signing key used to define the node identity.
val namespaceKey =
  node.keys.secret
    .generate_signing_key(
      name = node.name + s"-${SigningKeyUsage.Namespace.identifier}",
      SigningKeyUsage.NamespaceOnly,
    )

// Create a signing key used to authenticate the node toward the Sequencer.
val sequencerAuthKey =
  node.keys.secret.generate_signing_key(
    name = node.name + s"-${SigningKeyUsage.SequencerAuthentication.identifier}",
    SigningKeyUsage.SequencerAuthenticationOnly,
  )

// Create a signing key used to sign protocol messages.
val signingKey =
  node.keys.secret
    .generate_signing_key(
      name = node.name + s"-${SigningKeyUsage.Protocol.identifier}",
      SigningKeyUsage.ProtocolOnly,
    )

// Create the encryption key.
val encryptionKey =
  node.keys.secret.generate_encryption_key(name = node.name + "-encryption")

If you use a Key Management Service (KMS) to manage Canton’s keys, and you want to use a set of pre-generated keys, use the commands register_kms_signing_key() and register_kms_encryption_key() instead. Please refer to External Key Storage with a Key Management Service (KMS) for more details.

Participant Node manual initialization

After the keys have been generated, or in the case of KMS, the KMS-generated keys have been registered, manually initialize a Participant Node as follows:

// Use the fingerprint of this key for the node identity.
val namespace = Namespace(namespaceKey.id)
node.topology.init_id_from_uid(
  UniqueIdentifier.tryCreate("manual-" + node.name, namespace)
)

// Wait until the node is ready to receive the node identity.
node.health.wait_for_ready_for_node_topology()

// Create the self-signed root certificate.
node.topology.namespace_delegations.propose_delegation(
  namespace,
  namespaceKey,
  CanSignAllMappings,
)

// Assign the new keys to this node.
node.topology.owner_to_key_mappings.propose(
  member = node.id.member,
  keys = NonEmpty(Seq, sequencerAuthKey, signingKey, encryptionKey),
  signedBy = Seq(namespaceKey.fingerprint, sequencerAuthKey.fingerprint, signingKey.fingerprint),
)

Wait for the Participant Node to initialize:

node.health.wait_for_initialized()

Finally, you can begin using the new Participant Node.