Validator CRD User Guide

Catalyst uses the operator pattern with Custom Resource definitions. The same operations that can be made in the Catalyst GUI can be performed directly on Kubernetes with access to the cluster. Bellow are instructions on how to create and configure a validator using the CRD’s directly

Creating a Validator

Creation Example

apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: database-validator-dev-foo
data:
  user: "YWRtaW4="
  password: "YWRtaW4="
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-onboarding-validator
data:
  secret: "ZXlKemNHOXVjMjl5YVc1blUzWWlPaUpIYkc5aVlXd3RVM2x1WTJoeWIyNXBlbVZ5TFVadmRXNWtZWFJwYjI0Nk9qRXlNakJqTnpZME9XSXlPVGc1WVRVM09UY3lPVFZpTjJJek5XTm1ZbUZrTnpJMFpXVXhabVV6T0RCak5qRm1NMkZrWWpVeFlUZzFabVl5WVRabU9UWXlZVFV3SWl3aWMyVmpjbVYwSWpvaVNXSmhUVlp0ZDJ0bU5VSkNObGxRUlVWMWNUbHlWbmQ0TDNwb1REUkROekJGUld4TVJWUm1XQzlHYnowaWZRPT0K"
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-cns-ui-auth
data:
  url: "aHR0cHM6Ly9rZXljbG9hay50ZXN0aW5nLmNhdGFseXN0LmludGVsbGVjdGV1LmlvL2F1dGgvcmVhbG1zL2NhbnRvbi1kZXY="
  clientId: "dmFsaWRhdG9yLWRldi1mb28tY25zLXVp"
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-wallet-ui-auth
data:
  url: "aHR0cHM6Ly9rZXljbG9hay50ZXN0aW5nLmNhdGFseXN0LmludGVsbGVjdGV1LmlvL2F1dGgvcmVhbG1zL2NhbnRvbi1kZXY="
  clientId: "dmFsaWRhdG9yLWRldi1mb28td2FsbGV0LXVp"
  username: "dmFsaWRhdG9yLWRldi1mb29fd2FsbGV0dXNlcg=="
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-ledger-api-auth
data:
  client-id: "dmFsaWRhdG9yLWRldi1mb28="
  client-secret: "MTBhSDNYeGx4dWxEWHljd2FPWDBwamdqZ0VrSmxsbmI="
  ledger-api-user: "c2VydmljZS1hY2NvdW50LXZhbGlkYXRvci1kZXYtZm9v"
  url: "aHR0cHM6Ly9rZXljbG9hay50ZXN0aW5nLmNhdGFseXN0LmludGVsbGVjdGV1LmlvL2F1dGgvcmVhbG1zL2NhbnRvbi1kZXYvLndlbGwta25vd24vb3BlbmlkLWNvbmZpZ3VyYXRpb24="
---
apiVersion: catalyst.manager.canton/v1
kind: Validator
metadata:
  name: validator-dev-foo
  namespace: canton-dev
spec:
  application:
    spec:
      domain: participant-validator-dev-foo
      extraPorts:
        metrics: 10013
        val-http: 5003
      resources:
        cpuLimit: "2"
        cpuRequested: "1"
        imagePullSecret: ""
        memoryLimit: 6Gi
        memoryRequested: 3Gi
        replicas: 1
      servicePorts:
        metrics: 10013
        val-http: 5003
      type: backend
      envVars: []
  applicationCantonNameServer:
    spec:
      domain: cns-validator-dev-foo
      extraPorts: {}
      port: 8080
      resources:
        cpuLimit: "1"
        cpuRequested: "0.1"
        imagePullSecret: ""
        memoryLimit: 1536Mi
        memoryRequested: 240Mi
        replicas: 1
      servicePorts: {}
      type: ui
      envVars: []
  applicationWallet:
    spec:
      domain: wallet-validator-dev-foo
      extraPorts: {}
      port: 8080
      resources:
        cpuLimit: "1"
        cpuRequested: "0.1"
        imagePullSecret: ""
        memoryLimit: 1536Mi
        memoryRequested: 240Mi
        replicas: 1
      servicePorts: {}
      type: ui
      envVars: []
  config:
    scanAddress: "https://scan.sv-1.dev.global.canton.network.sync.global"
    svSponsorAddress: "https://sv.sv-1.dev.global.canton.network.sync.global"
    defaultJvmOptions: "-Xms1152M -Xmx1152M -Dscala.concurrent.context.minThreads=4"
    partyHint: ieu-foo-001
    extraDomains: []
    failOnAppVersionMismatch: true
    openId:
      jwksUrl: "https://keycloak.testing.catalyst.intellecteu.io/auth/realms/canton-dev/protocol/openid-connect/certs"
      audience: "https://wallet-validator-dev-foo.canton-dev.testing.catalyst.intellecteu.io/api"
    ledgerAuth:
      secretName: "cn-app-validator-dev-foo-ledger-api-auth"
      confUrlField: "url"
      clientIdField: "client-id"
      clientSecretField: "client-secret"
      userField: "ledger-api-user"
      audience: "https://wallet-validator-dev-foo.canton-dev.testing.catalyst.intellecteu.io/api"
      tokenField: "token"
      useToken: false
    database:
      port: 5432
      pwdField: password
      schema: validator
      secretName: database-validator-dev-foo
      userField: user
    participant:
      address: participant-validator-dev-foo.canton-dev.svc.cluster.local
      adminSecretRef:
        usernameField: ledger-api-user
        secretName: cn-app-validator-dev-foo-ledger-api-auth
      database:
        dns: database-validator-dev-foo.canton-dev.svc.cluster.local
        port: 5432
        pwdField: password
        schema: participant
        secretName: database-validator-dev-foo
        userField: user
      nodeIdentifier: IEUDevFoo001
    walletApp:
      secretName: "cn-app-validator-dev-foo-wallet-ui-auth"
      authorityUrlField: "url"
      clientIdField: "clientId"
      username: "validator-dev-foo_walletuser"
    cnsApp:
      secretName: "cn-app-validator-dev-foo-cns-ui-auth"
      authorityUrlField: "url"
      clientIdField: "clientId"
  participant:
    spec:
      daemon: true
      resources:
        cpuLimit: "2"
        cpuRequested: "1"
        imagePullSecret: ""
        memoryLimit: 6Gi
        memoryRequested: 3Gi
        replicas: 1
      envVars: []
  customAuth: true
  disableAutoInit: false
  disabledWallet: false
  imageRepo: ghcr.io/digital-asset/decentralized-canton-sync/docker
  imageTag: 0.4.16
  migrationId: "0"
  migrationMigrating: false
  onboardingSecretName: cn-app-validator-dev-foo-onboarding-validator
  storageSize: 20Gi
  envVars: []

----
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: database-validator-dev-foo
data:
  user: "YWRtaW4="
  password: "YWRtaW4="
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-onboarding-validator
data:
  secret: "ZXlKemNHOXVjMjl5YVc1blUzWWlPaUpIYkc5aVlXd3RVM2x1WTJoeWIyNXBlbVZ5TFVadmRXNWtZWFJwYjI0Nk9qRXlNakJqTnpZME9XSXlPVGc1WVRVM09UY3lPVFZpTjJJek5XTm1ZbUZrTnpJMFpXVXhabVV6T0RCak5qRm1NMkZrWWpVeFlUZzFabVl5WVRabU9UWXlZVFV3SWl3aWMyVmpjbVYwSWpvaVNXSmhUVlp0ZDJ0bU5VSkNObGxRUlVWMWNUbHlWbmQ0TDNwb1REUkROekJGUld4TVJWUm1XQzlHYnowaWZRPT0K"
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-cns-ui-auth
data:
  url: "aHR0cHM6Ly9rZXljbG9hay50ZXN0aW5nLmNhdGFseXN0LmludGVsbGVjdGV1LmlvL2F1dGgvcmVhbG1zL2NhbnRvbi1kZXY="
  clientId: "dmFsaWRhdG9yLWRldi1mb28tY25zLXVp"
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-wallet-ui-auth
data:
  url: "aHR0cHM6Ly9rZXljbG9hay50ZXN0aW5nLmNhdGFseXN0LmludGVsbGVjdGV1LmlvL2F1dGgvcmVhbG1zL2NhbnRvbi1kZXY="
  clientId: "dmFsaWRhdG9yLWRldi1mb28td2FsbGV0LXVp"
  username: "dmFsaWRhdG9yLWRldi1mb29fd2FsbGV0dXNlcg=="
---
apiVersion: v1
kind: Secret
metadata:
  namespace: canton-dev
  name: cn-app-validator-dev-foo-ledger-api-auth
data:
  client-id: "dmFsaWRhdG9yLWRldi1mb28="
  client-secret: "MTBhSDNYeGx4dWxEWHljd2FPWDBwamdqZ0VrSmxsbmI="
  ledger-api-user: "c2VydmljZS1hY2NvdW50LXZhbGlkYXRvci1kZXYtZm9v"
  url: "aHR0cHM6Ly9rZXljbG9hay50ZXN0aW5nLmNhdGFseXN0LmludGVsbGVjdGV1LmlvL2F1dGgvcmVhbG1zL2NhbnRvbi1kZXYvLndlbGwta25vd24vb3BlbmlkLWNvbmZpZ3VyYXRpb24="
---
apiVersion: catalyst.manager.canton/v1
kind: Validator
metadata:
  name: validator-dev-foo
  namespace: canton-dev
spec:
  application:
    spec:
      domain: participant-validator-dev-foo
      extraPorts:
        metrics: 10013
        val-http: 5003
      resources:
        cpuLimit: "2"
        cpuRequested: "1"
        imagePullSecret: ""
        memoryLimit: 6Gi
        memoryRequested: 3Gi
        replicas: 1
      servicePorts:
        metrics: 10013
        val-http: 5003
      type: backend
      envVars: []
  applicationCantonNameServer:
    spec:
      domain: cns-validator-dev-foo
      extraPorts: {}
      port: 8080
      resources:
        cpuLimit: "1"
        cpuRequested: "0.1"
        imagePullSecret: ""
        memoryLimit: 1536Mi
        memoryRequested: 240Mi
        replicas: 1
      servicePorts: {}
      type: ui
      envVars: []
  applicationWallet:
    spec:
      domain: wallet-validator-dev-foo
      extraPorts: {}
      port: 8080
      resources:
        cpuLimit: "1"
        cpuRequested: "0.1"
        imagePullSecret: ""
        memoryLimit: 1536Mi
        memoryRequested: 240Mi
        replicas: 1
      servicePorts: {}
      type: ui
      envVars: []
  config:
    scanAddress: "https://scan.sv-1.dev.global.canton.network.sync.global"
    svSponsorAddress: "https://sv.sv-1.dev.global.canton.network.sync.global"
    defaultJvmOptions: "-Xms1152M -Xmx1152M -Dscala.concurrent.context.minThreads=4"
    partyHint: ieu-foo-001
    extraDomains: []
    failOnAppVersionMismatch: true
    openId:
      jwksUrl: "https://keycloak.testing.catalyst.intellecteu.io/auth/realms/canton-dev/protocol/openid-connect/certs"
      audience: "https://wallet-validator-dev-foo.canton-dev.testing.catalyst.intellecteu.io/api"
    ledgerAuth:
      secretName: "cn-app-validator-dev-foo-ledger-api-auth"
      confUrlField: "url"
      clientIdField: "client-id"
      clientSecretField: "client-secret"
      userField: "ledger-api-user"
      audience: "https://wallet-validator-dev-foo.canton-dev.testing.catalyst.intellecteu.io/api"
      tokenField: "token"
      useToken: false
    database:
      port: 5432
      pwdField: password
      schema: validator
      secretName: database-validator-dev-foo
      userField: user
    participant:
      address: participant-validator-dev-foo.canton-dev.svc.cluster.local
      adminSecretRef:
        usernameField: ledger-api-user
        secretName: cn-app-validator-dev-foo-ledger-api-auth
      database:
        dns: database-validator-dev-foo.canton-dev.svc.cluster.local
        port: 5432
        pwdField: password
        schema: participant
        secretName: database-validator-dev-foo
        userField: user
      nodeIdentifier: IEUDevFoo001
    walletApp:
      secretName: "cn-app-validator-dev-foo-wallet-ui-auth"
      authorityUrlField: "url"
      clientIdField: "clientId"
      username: "validator-dev-foo_walletuser"
    cnsApp:
      secretName: "cn-app-validator-dev-foo-cns-ui-auth"
      authorityUrlField: "url"
      clientIdField: "clientId"
  participant:
    spec:
      daemon: true
      resources:
        cpuLimit: "2"
        cpuRequested: "1"
        imagePullSecret: ""
        memoryLimit: 6Gi
        memoryRequested: 3Gi
        replicas: 1
      envVars: []
  customAuth: true
  disableAutoInit: false
  disabledWallet: false
  imageRepo: ghcr.io/digital-asset/decentralized-canton-sync/docker
  imageTag: 0.4.16
  migrationId: "0"
  migrationMigrating: false
  onboardingSecretName: cn-app-validator-dev-foo-onboarding-validator
  storageSize: 20Gi
  envVars: []
  • The onboarding secret provided is one time use.

  • The validator name, participant identifier and party hint should be changed when deploying a new validator

  • The yaml defines several secrets:

    1. database-validator-dev-foo - provides user credentials for a new postgresql database for a

    2. cn-app-validator-dev-foo-onboarding-validator - Canton node onboarding secret

    3. cn-app-validator-dev-foo-wallet-ui-auth - Canton wallet application oauth credentials

    4. cn-app-validator-dev-foo-cns-ui-auth - Canton dashboard application oauth credentials

    5. cn-app-validator-dev-foo-ledger-api-auth - Canton ledger application oauth credentials

  • These secrets (names and fields) are referenced by inside the Validator definition itself so it’s necessary to keep them in-sync

  • Pay attention that some data is not provided as part of the secret part, but as a part of the reference itself, f.e. look at the database reference excerpt below, you can see that the port, schema values are specified as raw values, not as a reference:

spec:
  config:
    database:
      port: 5432
      schema: validator
      secretName: database-validator-dev-foo # <--- part of reference
      pwdField: password # <--- part of reference
      userField: user # <--- part of reference

Validator operations

Migration

The following fields are used in migration

spec:
  migrationId: "2"
  migrationMigrating: false

To migrate set the the values in the following way

spec:
  migrationId: "new mig id"
  migrationMigrating: true

Once it is done set migrationMigrating to false

Recovery from Id dump

To recover the Canton Coin balances using a Identities Backup, the Catalyst operator expects a secret in following format

Secret:

apiVersion: v1
data:
  content: <Identity Backup>
kind: Secret
metadata:
  labels:
    validator: <validator crd name>
  name: id-backup-secret-example
  namespace: namespace-of-val
type: Opaque

Then we can create a validator (removing the old of the same name if on the same cluster) with the following added fields

Changes to spec:

spec:
  particpant:
    nodeIdentifier: "validator-foooooooo"
  config:
    partyHint: "oldParty"
    identitiesImport:
      newParticipantIdentifier: "validator-foooooooo"
      secretName: "secret-with-identity-dump-party"
    isMigrateValidatorParty: true

The participant should have the same node identifier as the newParticipantIdentifier field that is different from the previous validator

Configuring the Validator

Environment variable overrides

In order to add extra environment variables you can add the following fields

spec:
  config:
    validatorOverrides:
      - name: OVERRIDE_EXAMPLE
        value: "5432"
    participantOverrides: [] #Can be empty or null
    walletOverrides:
      - name: OVERRIDE_WITH_SECRET
        valueFrom:
         secretKeyRef:
           key: password
           name: secret
    cnsOverrides:[]

These will override any variables generated by the spec.config

Note:

In older versions all environment variables (operator and user defined) are defined in

  • application.spec.envVars

  • applicationWallet.spec.envVars

  • applicationCantonNameServer.envVars

  • participant.spec.envVars

This made it harder to use the CRD’s and distinguish user vs operator actions.

On upgrade to 1.10 a script will be used to generate the spec.config fields from these. Any new changes will only apply to spec.config so the operator will have the following order of priority to set environment variables.

  • Environment variables defined spec.config overrides

  • Environment variables generated by the standard spec.config fields

  • Environment variables in the deprecated <app>.spec.envVars format

The <app>.spec.envVars format will be removed in a future release. Any changes through the GUI will apply in the new spec.config override fields

Customise annotations

In order to set your own annotations on pods or services managed by catalyst the following fields can be set:

spec:
  # Main validator application (backend) resources
  application:
    spec:
      resources:
        cpuLimit: "4"
        cpuRequested: "2"
        memoryLimit: 8Gi
        memoryRequested: 4Gi
        replicas: 1

  # Canton Name Server (CNS) UI application resources
  applicationCantonNameServer:
    spec:
      resources:
        cpuLimit: "2"
        cpuRequested: "0.5"
        memoryLimit: 2Gi
        memoryRequested: 512Mi
        replicas: 1

  # Wallet UI application resources
  applicationWallet:
    spec:
      resources:
        cpuLimit: "2"
        cpuRequested: "0.5"
        memoryLimit: 2Gi
        memoryRequested: 512Mi
        replicas: 1

  # Participant node resources
  participant:
    spec:
      resources:
        cpuLimit: "4"
        cpuRequested: "2"
        memoryLimit: 8Gi
        memoryRequested: 4Gi
        replicas: 1
  #Database persistent volume size
  storageSize: 50Gi

Other fields

Update examples:

  • enabled top up

spec:
  config:
    topUp:
      enabled: true
      minInterval: 2m
      targetThroughput: 80000
  • enable scheduled pruning

spec:
  config:
    scheduledPrune:
      retention: "2m"
      cron: "* * * * *"
      maxDuration: "30d"
  • provide additional config

spec:
  config:
    additionalConfigOther: |
      canton.validator-apps.validator_backend.participant-bootstrapping-dump {
        type = file
        file = /participant-bootstrapping-dump/content
        new-participant-identifier = "validator-fooooooo"
      }
  • set contact point

spec:
  config:
    contactPoint: "mycompany@mail.com"