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.

> 📘
> 
> You can also see working examples of local Builder Vault configurations in our [demo](https://gitlab.com/Blockdaemon/tsm-demo/-/tree/main/tsm) repository.

# Prerequisites

- Docker on your local machine
- Credentials for our Nexus repository [https://bv.sepior.net](https://bv.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:

```yaml
services:

tsm_node_0:
    image: bv.sepior.net/tsm-node:73.1.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: bv.sepior.net/tsm-node:73.1.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: bv.sepior.net/tsm-node:73.1.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:16.12-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:11.8.6
    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:16.12-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:

### config0.toml

```toml
[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?symmetricConnection=true"
  PublicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0AyIB0e0A00Z+ovqDQ5mjffEqVabU/eEOwOOrkElnSX1qPkgIn5eLIOC7OWQq6dgZnJLjElg6R4vR5a91aAE8w=="

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

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

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

[MPCDirectServer]
  Port = 9000

[SDKServer]
  Port = 8000

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

[DKLS23.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    ExportKeyShare = true
    ImportKeyShare = true
    ExportKey = true
    ImportKey = true

[SEPD19S.Features]
    GenerateKey = true
    GeneratePresignatures = true
    Sign = true
    SignWithPresignature = true
    GenerateRecoveryData = true
    PublicKey = true
    ChainCode = true
    Reshare = true
    CopyKey = true
    ExportKeyShare = true
    ImportKeyShare = true
    ExportKey = true
    ImportKey = true

[ADN06.Features]
    PublicKey = true
    SignPKCS1v15 = true
    SignPSS = true
    Decrypt = true
    ExportKey = true
    ImportKey = true

[MRZ15.AESFeatures]
    GenerateKey = true
    ExportKey = true
    ImportKey = true
    CTRKeyStream = true
    CBCEncrypt = true
    CBCDecrypt = true
    GCMEncrypt = true
    GCMDecrypt = true
    CMAC = true

[MRZ15.HMACFeatures]
    GenerateKey = true
    ExportKey = true
    ImportKey = true
    HMACSHA256 = true
    HMACSHA512 = true
```

### config1.toml

```toml
[Player]
  Index = 1
  PrivateKey = "MHcCAQEEILWaOgXLxJUxodTrASskOfTN0y8RD/vuwuv/bOM+f2wroAoGCCqGSM49AwEHoUQDQgAEZsbGXaVTkx8iiXb7iDSBFs24xYdbe5jTRg57aU0F71BMxhlV46cKMsCDXARriCUBwApfCoAf/ByyJ7TpWRm4Rw=="
  ExportWhiteList = ["*"]    # Allow any wrapping key when exporting; only do this in tests

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

[MPCDirectServer]
  Port = 9000

[SDKServer]
  Port = 8000

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

[ADN06.Features]
    PublicKey = true
    SignPKCS1v15 = true
    SignPSS = true
    Decrypt = true
    ExportKey = true
    ImportKey = true

[MRZ15.HMACFeatures]
    GenerateKey = true
    ExportKey = true
    ImportKey = true
    HMACSHA256 = true
    HMACSHA512 = true
```

### config2.toml

```toml
[Player]
  Index = 2
  PrivateKey = "MHcCAQEEIHGb0I8CEE6db7/buOQiX8SgnbkkAI5aX9mowvCpUjOJoAoGCCqGSM49AwEHoUQDQgAEJZx6N1ARYmc/6DQBL/47yRL/dMvWi5UQRUiqA05pdjLAb3eXO6yPioocnZNxsQjCerxcMJ2LnvELmK5L6Ovsqw=="
  ExportWhiteList = ["*"]    # Allow any wrapping key when exporting; only do this in tests

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

[MPCDirectServer]
  Port = 9000

[SDKServer]
  Port = 8000

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

[ADN06.Features]
    PublicKey = true
    SignPKCS1v15 = true
    SignPSS = true
    Decrypt = true
    ExportKey = true
    ImportKey = true

[MRZ15.HMACFeatures]
    GenerateKey = true
    ExportKey = true
    ImportKey = 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://bv.sepior.net](https://bv.sepior.net/). So first, we must log into that image repository with the following command:

```shell
docker login -u NEXUS_USERNAME -p NEXUS_PASSWORD bv.sepior.net
```

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:

```undefined
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`:

```shell
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:

```shell
docker compose logs tsm_node_1
```

If you have trouble running the TSM, you may want to take a look at our [troubleshooting guide](https://builder-vault-tsm.docs.blockdaemon.com/docs/troubleshooting), 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](https://builder-vault-tsm.docs.blockdaemon.com/docs/getting-started-demo-tsm) 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 Node | URL |  |
| --- | --- | --- |
| MPC Node 0 | [http://localhost:8500](http://localhost:8500/) | apikey0 |
| MPC Node 1 | [http://localhost:8501](http://localhost:8501/) | apikey1 |
| MPC Node 2 | [http://localhost:8502](http://localhost:8502/) | apikey2 |

Updated 3 months ago

* * *

### What’s Next

- [Builder Vault SDK](https://builder-vault-tsm.docs.blockdaemon.com/docs/getting-started-demo-tsm)
