Skip to content

5.2 Deployment and DevOps Checklist

Successful deployment requires distinct focus on securing and ensuring high availability for all three architectural layers.

A. Canton Infrastructure

1. Production Canton Domain Setup

bash
# Initialize Canton domain
canton {
  domains {
    production {
      storage {
        type = postgres
        config {
          url = "jdbc:postgresql://canton-db:5432/canton"
          user = "canton"
          password = ${?CANTON_DB_PASSWORD}
        }
      }

      public-api {
        port = 5021
        tls {
          cert-chain-file = "/etc/canton/certs/domain.crt"
          private-key-file = "/etc/canton/keys/domain.key"
        }
      }

      monitoring {
        metrics {
          enabled = true
          reporters = ["prometheus"]
        }
      }
    }
  }

  participants {
    vault-operator {
      storage = ${canton.domains.production.storage}
      admin-api.port = 5022
      ledger-api.port = 5011
    }
  }
}

2. Access Control Configuration

daml
-- Configure Canton topology
namespace Canton.Topology

-- Add policy issuer party
topology.transactions.add_party(
  party_id = "PolicyIssuer::1234567890abcdef",
  participant = "vault-operator"
)

-- Grant signing key
topology.transactions.add_signing_key(
  party = "PolicyIssuer::1234567890abcdef",
  public_key = "0x...",  -- HSM-backed key
  key_purpose = SigningKeyPurpose.Policy
)

-- Add regulatory observer
topology.transactions.add_party(
  party_id = "Regulator::fedcba9876543210",
  participant = "regulator-node"
)

3. HSM/KMS Integration

typescript
// AWS KMS integration for Canton
import { KMS } from 'aws-sdk';

class CantonKMSIntegration {
  private kms: KMS;
  private keyId: string;

  constructor(keyId: string) {
    this.kms = new KMS({ region: 'us-east-1' });
    this.keyId = keyId;
  }

  async signIntent(intentHash: Buffer): Promise<Buffer> {
    const result = await this.kms.sign({
      KeyId: this.keyId,
      Message: intentHash,
      MessageType: 'DIGEST',
      SigningAlgorithm: 'ECDSA_SHA_256'
    }).promise();

    return Buffer.from(result.Signature!);
  }

  async rotateKey(): Promise<string> {
    // Generate new key
    const newKey = await this.kms.createKey({
      KeyUsage: 'SIGN_VERIFY',
      KeySpec: 'ECC_NIST_P256',
      Description: 'Canton Policy Issuer v2'
    }).promise();

    // Schedule old key deletion (30 days)
    await this.kms.scheduleKeyDeletion({
      KeyId: this.keyId,
      PendingWindowInDays: 30
    }).promise();

    return newKey.KeyMetadata!.KeyId!;
  }
}

B. Relayer Layer

1. High Availability Deployment

yaml
# docker-compose.yml
version: '3.8'

services:
  relayer-1:
    image: canton-relayer:latest
    environment:
      - RELAYER_ID=relayer-1
      - CANTON_API_URL=https://canton.example.com
      - LEADER_ELECTION=true
      - ETCD_ENDPOINTS=etcd-1:2379,etcd-2:2379,etcd-3:2379
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.zone==us-east-1a

  relayer-2:
    image: canton-relayer:latest
    environment:
      - RELAYER_ID=relayer-2
      - CANTON_API_URL=https://canton.example.com
      - LEADER_ELECTION=true
      - ETCD_ENDPOINTS=etcd-1:2379,etcd-2:2379,etcd-3:2379
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.zone==us-west-2a

  etcd-1:
    image: quay.io/coreos/etcd:latest
    command:
      - etcd
      - --name=etcd-1
      - --initial-advertise-peer-urls=http://etcd-1:2380

2. Leader Election

typescript
import { Etcd3 } from 'etcd3';

class RelayerCluster {
  private etcd: Etcd3;
  private election: Election;

  async startLeaderElection(): Promise<void> {
    this.etcd = new Etcd3({
      hosts: process.env.ETCD_ENDPOINTS!.split(',')
    });

    this.election = this.etcd.election('relayer-leader');

    await this.election.campaign(process.env.RELAYER_ID!);

    console.log(`${process.env.RELAYER_ID} became leader`);

    // Start processing intents only if leader
    if (await this.election.isLeader()) {
      this.startIntentProcessing();
    }

    // Watch for leadership changes
    this.election.observe().on('change', (leader) => {
      if (leader === process.env.RELAYER_ID) {
        this.startIntentProcessing();
      } else {
        this.stopIntentProcessing();
      }
    });
  }
}

3. Operational Logging

typescript
import winston from 'winston';

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    // Immutable log storage for auditing
    new winston.transports.File({
      filename: '/var/log/relayer/intents.log',
      maxsize: 100 * 1024 * 1024, // 100MB
      tailable: true
    }),

    // Ship to centralized logging
    new winston.transports.Http({
      host: 'logs.example.com',
      port: 443,
      path: '/api/logs',
      ssl: true
    })
  ]
});

// Log all operations
logger.info('intent_received', {
  intentId: intent.id,
  nonce: intent.nonce,
  vaultId: intent.vaultId
});

logger.info('transaction_submitted', {
  intentId: intent.id,
  txHash: receipt.transactionHash,
  chain: 'ethereum',
  gasUsed: receipt.gasUsed
});

C. On-Chain Vaults

1. Deployment Script

typescript
// scripts/deploy-vaults.ts
import { ethers } from 'hardhat';

async function deployVault(chain: string) {
  console.log(`Deploying vault on ${chain}...`);

  const [deployer] = await ethers.getSigners();

  // Deploy vault
  const Vault = await ethers.getContractFactory('CantonDeFiVault');
  const vault = await Vault.deploy(
    process.env.CANTON_SIGNER_ADDRESS!,
    process.env.AUTHORIZED_PARTY_ID!
  );

  await vault.deployed();
  console.log(`Vault deployed to: ${vault.address}`);

  // Deploy adapters
  const adapters = {
    aave: await deployAaveAdapter(vault.address),
    uniswap: await deployUniswapAdapter(vault.address),
    compound: await deployCompoundAdapter(vault.address)
  };

  // Register adapters
  for (const [protocol, adapter] of Object.entries(adapters)) {
    const protocolId = ethers.utils.id(protocol);
    await vault.registerAdapter(protocolId, adapter.address);
    console.log(`Registered ${protocol} adapter: ${adapter.address}`);
  }

  // Verify on Etherscan
  await hre.run('verify:verify', {
    address: vault.address,
    constructorArguments: [
      process.env.CANTON_SIGNER_ADDRESS!,
      process.env.AUTHORIZED_PARTY_ID!
    ]
  });

  return vault;
}

// Deploy to all chains
async function main() {
  const chains = ['ethereum', 'polygon', 'arbitrum', 'optimism'];

  for (const chain of chains) {
    await deployVault(chain);
  }
}

main().catch(console.error);

2. Canton Key Registry (Pattern 2)

solidity
// contracts/CantonKeyRegistry.sol
contract CantonKeyRegistry is Ownable {
    mapping(bytes32 => address) public cantonPartyToKey;

    event KeyRegistered(bytes32 indexed partyId, address publicKey);
    event KeyRevoked(bytes32 indexed partyId, address publicKey);

    function registerKey(
        bytes32 partyId,
        address publicKey
    ) external onlyOwner {
        require(publicKey != address(0), "Invalid key");
        cantonPartyToKey[partyId] = publicKey;
        emit KeyRegistered(partyId, publicKey);
    }

    function revokeKey(bytes32 partyId) external onlyOwner {
        address oldKey = cantonPartyToKey[partyId];
        delete cantonPartyToKey[partyId];
        emit KeyRevoked(partyId, oldKey);
    }
}
bash
# Deploy key registry
forge create CantonKeyRegistry \
  --rpc-url $ETH_RPC_URL \
  --private-key $DEPLOYER_KEY

# Register Canton public key
cast send $REGISTRY_ADDRESS \
  "registerKey(bytes32,address)" \
  $AUTHORIZED_PARTY_ID \
  $CANTON_PUBKEY_ADDRESS \
  --rpc-url $ETH_RPC_URL \
  --private-key $ADMIN_KEY

Infrastructure as Code

Terraform Configuration

hcl
# infrastructure/main.tf

# Canton Domain
resource "aws_ecs_task_definition" "canton_domain" {
  family = "canton-domain"
  container_definitions = jsonencode([
    {
      name  = "canton"
      image = "digitalasset/canton:latest"
      memory = 4096
      cpu = 2048

      environment = [
        {
          name  = "CANTON_CONFIG"
          value = "/etc/canton/production.conf"
        }
      ]

      mountPoints = [
        {
          sourceVolume  = "canton-config"
          containerPath = "/etc/canton"
        }
      ]
    }
  ])
}

# Relayer Service
resource "aws_ecs_service" "relayer" {
  name            = "canton-relayer"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.relayer.arn
  desired_count   = 3  # High availability

  load_balancer {
    target_group_arn = aws_lb_target_group.relayer.arn
    container_name   = "relayer"
    container_port   = 8080
  }
}

# PostgreSQL for Canton
resource "aws_db_instance" "canton" {
  identifier           = "canton-db"
  engine              = "postgres"
  engine_version      = "14"
  instance_class      = "db.r5.2xlarge"
  allocated_storage   = 500
  storage_encrypted   = true
  multi_az            = true
  backup_retention_period = 30

  vpc_security_group_ids = [aws_security_group.canton_db.id]
}

Monitoring and Alerting

Prometheus Metrics

yaml
# prometheus/prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'canton-domain'
    static_configs:
      - targets: ['canton:5021']

  - job_name: 'relayer'
    static_configs:
      - targets: ['relayer-1:9090', 'relayer-2:9090']

  - job_name: 'ethereum-node'
    static_configs:
      - targets: ['geth:6060']

Grafana Dashboards

json
{
  "dashboard": {
    "title": "Canton DeFi Vault Operations",
    "panels": [
      {
        "title": "Intent Processing Rate",
        "targets": [
          {
            "expr": "rate(relayer_intents_processed_total[5m])"
          }
        ]
      },
      {
        "title": "Execution Success Rate",
        "targets": [
          {
            "expr": "rate(relayer_chain_execution_success_rate[5m])"
          }
        ]
      },
      {
        "title": "Canton Ledger Height",
        "targets": [
          {
            "expr": "canton_ledger_height"
          }
        ]
      }
    ]
  }
}

AlertManager Rules

yaml
# alertmanager/rules.yml
groups:
  - name: CantonVault
    interval: 30s
    rules:
      - alert: IntentProcessingDelay
        expr: relayer_intent_queue_depth > 10
        for: 5m
        annotations:
          summary: "High intent queue depth"

      - alert: ExecutionFailureRate
        expr: rate(relayer_execution_failures_total[5m]) > 0.05
        for: 2m
        annotations:
          summary: "High execution failure rate"

      - alert: CantonDomainDown
        expr: up{job="canton-domain"} == 0
        for: 1m
        annotations:
          summary: "Canton domain is down"
          severity: "critical"

Deployment Checklist

Pre-Production

  • [ ] Canton domain deployed and synced
  • [ ] HSM/KMS integration tested
  • [ ] Participant nodes configured
  • [ ] Topology properly set up (parties, keys)
  • [ ] Relayer cluster deployed (3+ instances)
  • [ ] Leader election working
  • [ ] Vault contracts deployed to testnets
  • [ ] Adapters registered
  • [ ] Key registry synchronized (if Pattern 2)
  • [ ] End-to-end tests passing
  • [ ] Security audit completed
  • [ ] Load testing performed
  • [ ] Monitoring configured
  • [ ] Alerting rules tested
  • [ ] Runbooks documented
  • [ ] On-call rotation established

Production Launch

  • [ ] Deploy to mainnet chains
  • [ ] Verify all contracts on explorers
  • [ ] Transfer ownership to multisig
  • [ ] Set emergency contacts
  • [ ] Enable monitoring
  • [ ] Start with limited AUM
  • [ ] Gradual rollout (1 chain → multi-chain)
  • [ ] Monitor for 48 hours before scaling
  • [ ] Regular health checks
  • [ ] Incident response ready

Canton DeFi - Multichain DeFi Technical Reference