Local Deployment

In this tutorial you will set up a TSM on your local machine. The TSM will contain three MPC nodes, and each MPC node will connect to its own database server. The MPC nodes as well as the database servers will run in adjacent Docker containers on your machine.

The tutorial has been tested on Linux and Mac OS. If you use a different OS, you may need to do things slightly different.

Prerequisites

  • Docker on your local machine
  • Credentials for https://nexus.sepior.net. You can get these from our support team. We will use NEXUS_USERNAME and NEXUS_PASSWORD in the following, but remember to replace these with the credentials received from our support team.

Configuring the TSM

Configure the TSM with the following steps:

  • Create a new project folder on your local machine and open a terminal window in the folder.
  • Create a file docker-compose.yml in the folder, with the following content:
services:

  tsm_node_0:
    image: nexus.sepior.net:19001/tsm-node:65.0.0
    container_name: tsm-node-0
    ports:
      - "8500:8000"
    networks:
      - example-tsm
    environment:
      - WAIT_HOST=db0:5432
      - CONFIG_FILE=/config/config.toml
    volumes:
      - ./config0.toml:/config/config.toml

  tsm_node_1:
    image: nexus.sepior.net:19001/tsm-node:65.0.0
    container_name: tsm-node-1
    ports:
      - "8501:8000"
    networks:
      - example-tsm
    environment:
      - WAIT_HOST=db1:3306
      - CONFIG_FILE=/config/config.toml
    volumes:
      - ./config1.toml:/config/config.toml 

  tsm_node_2:
    image: nexus.sepior.net:19001/tsm-node:65.0.0
    container_name: tsm-node-2
    ports:
      - "8502:8000"
    networks:
      - example-tsm
    environment:
      - WAIT_HOST=db2:5432
      - CONFIG_FILE=/config/config.toml
    volumes:
      - ./config2.toml:/config/config.toml

  db0:
    image: postgres:15.4-alpine
    container_name: db0
    networks:
      - example-tsm
    environment:
      - POSTGRES_DB=db0
      - POSTGRES_USER=db0user
      - POSTGRES_PASSWORD=db0password
    restart: always

  db1:
    container_name: db1
    image: mariadb:10.8.2
    networks:
      - example-tsm
    environment:
      MARIADB_DATABASE: db1
      MARIADB_USER: db1user
      MARIADB_PASSWORD: db1password
      MARIADB_RANDOM_ROOT_PASSWORD: 1
    command:
      mysqld --max_connections=1500

  db2:
    image: postgres:15.2-alpine
    container_name: db2
    networks:
      - example-tsm
    environment:
      - POSTGRES_DB=db2
      - POSTGRES_USER=db2user
      - POSTGRES_PASSWORD=db2password
    restart: always

networks:
  example-tsm:

  • Create three files config0.toml, config1.toml, and config2.toml in the project folder. Make sure that the files have the following contents:
[Player]
  Index = 0
  PrivateKey = "MHcCAQEEIJZ2T0ESxG34wA77rhn+9KMOrkz296jeDUOenHsLmWO/oAoGCCqGSM49AwEHoUQDQgAE0AyIB0e0A00Z+ovqDQ5mjffEqVabU/eEOwOOrkElnSX1qPkgIn5eLIOC7OWQq6dgZnJLjElg6R4vR5a91aAE8w=="
  ExportWhiteList = ["*"]    # Allow any wrapping key when exporting; only do this in tests

[Players.0]
  Address = "tcp://tsm_node_0:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0AyIB0e0A00Z+ovqDQ5mjffEqVabU/eEOwOOrkElnSX1qPkgIn5eLIOC7OWQq6dgZnJLjElg6R4vR5a91aAE8w=="

[Players.1]
  Address = "tcp://tsm_node_1:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZsbGXaVTkx8iiXb7iDSBFs24xYdbe5jTRg57aU0F71BMxhlV46cKMsCDXARriCUBwApfCoAf/ByyJ7TpWRm4Rw=="

[Players.2]
  Address = "tcp://tsm_node_2:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJZx6N1ARYmc/6DQBL/47yRL/dMvWi5UQRUiqA05pdjLAb3eXO6yPioocnZNxsQjCerxcMJ2LnvELmK5L6Ovsqw=="

[Database]
  DriverName = "postgres"
  DataSourceName = "postgres://db0user:db0password@db0:5432/db0?sslmode=disable"
  EncryptorMasterPassword = "db0masterPassword"

[MPCTCPServer]
  Port = 9000

[SDKServer]
  Port = 8000

[[Authentication.APIKeys]]
  APIKey = "IntV2sEZRwwd2F+UkDLC7zmFhwvpxAwb0eQKwdEnSZU="  # apikey0
  ApplicationID = "demoapp"

[DKLS19.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    BackupKeyShare = true
    RestoreKeyShare = true
    ExportKeyShares = true
    ImportKeyShares = true
    BIP32GenerateSeed = true
    BIP32DeriveFromSeed = true
    BIP32DeriveFromKey = true
    BIP32ConvertKey = true
    BIP32ExportSeed = true
    BIP32ImportSeed = true
    BIP32Info = true

[SEPD19S.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    BackupKeyShare = true
    RestoreKeyShare = true
    ExportKeyShares = true
    ImportKeyShares = true

[ADN06.Features]
    PublicKey = true
    SignPKCS1v15 = true
    SignPSS = true
    Decrypt = true
    ExportKeyShares = true
    ImportKeyShares = true

[MRZ15.AESFeatures]
    GenerateKey = true
    ExportKeyShares = true
    ImportKeyShares = true
    CTRKeyStream = true
    CBCEncrypt = true
    CBCDecrypt = true
    GCMEncrypt = true
    GCMDecrypt = true
    CMAC = true

[MRZ15.HMACFeatures]
    GenerateKey = true
    ExportKeyShares = true
    ImportKeyShares = true
    HMACSHA256 = true
    HMACSHA512 = true
[Player]
  Index = 1
  PrivateKey = "MHcCAQEEILWaOgXLxJUxodTrASskOfTN0y8RD/vuwuv/bOM+f2wroAoGCCqGSM49AwEHoUQDQgAEZsbGXaVTkx8iiXb7iDSBFs24xYdbe5jTRg57aU0F71BMxhlV46cKMsCDXARriCUBwApfCoAf/ByyJ7TpWRm4Rw=="
  ExportWhiteList = ["*"]    # Allow any wrapping key when exporting; only do this in tests

[Players.0]
  Address = "tcp://tsm_node_0:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0AyIB0e0A00Z+ovqDQ5mjffEqVabU/eEOwOOrkElnSX1qPkgIn5eLIOC7OWQq6dgZnJLjElg6R4vR5a91aAE8w=="

[Players.1]
  Address = "tcp://tsm_node_1:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZsbGXaVTkx8iiXb7iDSBFs24xYdbe5jTRg57aU0F71BMxhlV46cKMsCDXARriCUBwApfCoAf/ByyJ7TpWRm4Rw=="

[Players.2]
  Address = "tcp://tsm_node_2:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJZx6N1ARYmc/6DQBL/47yRL/dMvWi5UQRUiqA05pdjLAb3eXO6yPioocnZNxsQjCerxcMJ2LnvELmK5L6Ovsqw=="

[Database]
  DriverName = "mysql"
  DataSourceName = "db1user:db1password@tcp(db1:3306)/db1?parseTime=true"
  EncryptorMasterPassword = "db1masterPassword"
  MaxIdleConns = 500
  MaxOpenConns = 500

[MPCTCPServer]
  Port = 9000

[SDKServer]
  Port = 8000

[[Authentication.APIKeys]]
  APIKey = "1PebMT+BBvWvEIrZb/UWIi2/1aCrUvQwjksa0ddA3mA=" # apikey1
  ApplicationID = "demoapp"

[DKLS19.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    BackupKeyShare = true
    RestoreKeyShare = true
    ExportKeyShares = true
    ImportKeyShares = true
    BIP32GenerateSeed = true
    BIP32DeriveFromSeed = true
    BIP32DeriveFromKey = true
    BIP32ConvertKey = true
    BIP32ExportSeed = true
    BIP32ImportSeed = true
    BIP32Info = true

[SEPD19S.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    BackupKeyShare = true
    RestoreKeyShare = true
    ExportKeyShares = true
    ImportKeyShares = true

[ADN06.Features]
    PublicKey = true
    SignPKCS1v15 = true
    SignPSS = true
    Decrypt = true
    ExportKeyShares = true
    ImportKeyShares = true

[MRZ15.AESFeatures]
    GenerateKey = true
    ExportKeyShares = true
    ImportKeyShares = true
    CTRKeyStream = true
    CBCEncrypt = true
    CBCDecrypt = true
    GCMEncrypt = true
    GCMDecrypt = true
    CMAC = true

[MRZ15.HMACFeatures]
    GenerateKey = true
    ExportKeyShares = true
    ImportKeyShares = true
    HMACSHA256 = true
    HMACSHA512 = true
[Player]
  Index = 2
  PrivateKey = "MHcCAQEEIHGb0I8CEE6db7/buOQiX8SgnbkkAI5aX9mowvCpUjOJoAoGCCqGSM49AwEHoUQDQgAEJZx6N1ARYmc/6DQBL/47yRL/dMvWi5UQRUiqA05pdjLAb3eXO6yPioocnZNxsQjCerxcMJ2LnvELmK5L6Ovsqw=="
  ExportWhiteList = ["*"]    # Allow any wrapping key when exporting; only do this in tests

[Players.0]
  Address = "tcp://tsm_node_0:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0AyIB0e0A00Z+ovqDQ5mjffEqVabU/eEOwOOrkElnSX1qPkgIn5eLIOC7OWQq6dgZnJLjElg6R4vR5a91aAE8w=="

[Players.1]
  Address = "tcp://tsm_node_1:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZsbGXaVTkx8iiXb7iDSBFs24xYdbe5jTRg57aU0F71BMxhlV46cKMsCDXARriCUBwApfCoAf/ByyJ7TpWRm4Rw=="

[Players.2]
  Address = "tcp://tsm_node_2:9000"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJZx6N1ARYmc/6DQBL/47yRL/dMvWi5UQRUiqA05pdjLAb3eXO6yPioocnZNxsQjCerxcMJ2LnvELmK5L6Ovsqw=="

[Database]
  DriverName = "postgres"
  DataSourceName = "postgres://db2user:db2password@db2:5432/db2?sslmode=disable"
  EncryptorMasterPassword = "db2masterPassword"

[MPCTCPServer]
  Port = 9000

[SDKServer]
  Port = 8000

[[Authentication.APIKeys]]
  APIKey = "FfrI+hyZAiVosAi53wewS0U1SsXKR0AEHZBM088rOeM="  # apikey2
  ApplicationID = "demoapp"

[DKLS19.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    BackupKeyShare = true
    RestoreKeyShare = true
    ExportKeyShares = true
    ImportKeyShares = true
    BIP32GenerateSeed = true
    BIP32DeriveFromSeed = true
    BIP32DeriveFromKey = true
    BIP32ConvertKey = true
    BIP32ExportSeed = true
    BIP32ImportSeed = true
    BIP32Info = true

[SEPD19S.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    BackupKeyShare = true
    RestoreKeyShare = true
    ExportKeyShares = true
    ImportKeyShares = true

[ADN06.Features]
    PublicKey = true
    SignPKCS1v15 = true
    SignPSS = true
    Decrypt = true
    ExportKeyShares = true
    ImportKeyShares = true

[MRZ15.AESFeatures]
    GenerateKey = true
    ExportKeyShares = true
    ImportKeyShares = true
    CTRKeyStream = true
    CBCEncrypt = true
    CBCDecrypt = true
    GCMEncrypt = true
    GCMDecrypt = true
    CMAC = true

[MRZ15.HMACFeatures]
    GenerateKey = true
    ExportKeyShares = true
    ImportKeyShares = true
    HMACSHA256 = true
    HMACSHA512 = true

📘

Note

The files above contain a number of private keys and passwords. Make sure that you replace these with your own keys and strong passwords if you at any point use the TSM in production.

The files contain many details that are not explained in this Getting Started tutorial. The details on how to configure and run a TSM are explained in the section TSM Management.

Note also that all protocol features are enabled here. In production you probably only want to enable the features that are actually used.

Starting the TSM

With the above files all placed in your project folder, you can start the TSM with the following steps.

  • Docker will fetch the MPC node images from https://nexus.sepior.net:19001. So first, we must log into that image repository with the following command:
docker login -u NEXUS_USERNAME -p NEXUS_PASSWORD nexus.sepior.net:19001

Remember to replace NEXUS_USERNANE and NEXUS_PASSWORD with the credentials provided by our support team.

  • The TSM can now be started in the background by executing this command:
docker compose up -d

You should now have a local TSM running on your machine.

To check that everything went well you can execute the following command, that contacts each of the MPC nodes on an unauthenticated endpoint /ping:

curl http://localhost:8500/ping http://localhost:8501/ping http://localhost:8502/ping

This should result in the output PongPongPong.

You can also inspect the log messages generated by an MPC node, say tsm-node-1, as follows:

docker compose logs tsm_node_1

If you have trouble running the TSM, you may want to take a look at our troubleshooting guide, or you can contact our support team.

Using the TSM

Once the TSM is up and running, the next step is to connect to the TSM using one of the available SDKs. To do this, you can follow any of the tutorials in the Builder Vault SDK Quick Start section. In our case, you have just started a Builder Vault instance on your local machine, so you should use these URLs and API keys when connecting to the Builder Vault:

MPC NodeURL
MPC Node 0http://localhost:8500apikey0
MPC Node 1http://localhost:8501apikey1
MPC Node 2http://localhost:8502apikey2

What’s Next