Self-hosted (open source)

Install the open-source VTA appliance, generate a signing identity, and connect PNM, all on infrastructure you control.

Run the VTA appliance on infrastructure you manage, such as your laptop for local development, or a server your organisation already operates. You install the vta binary, generate its signing identity with a single non-interactive setup file, and connect Personal Network Manager (PNM) to the instance you just started.

Install binariescargo install vta-service, pnm-cliGenerate Admin DIDpnm setupConfigure & start VTAvta setup --from setup.tomlMINTS THE VTA'S OWN did:webvhConnect PNMpnm setup continueYOUR MACHINE

Use this guide when:

  • You need infrastructure-level control over where the VTA runs and how it’s configured.
  • You’re doing local development or testing, and want a disposable instance.
  • Your organisation already operates the host you’ll deploy on.

For a fully managed option, use Deploy VTA appliance instead. Affinidi provisions and runs the infrastructure for you, hosted inside a Trusted Execution Environment.

Once your VTA is running, whichever path you took, continue to Create your first context.

Prerequisites

  • Install Rust version 1.95 or later (required for compiling the VTA and PNM CLI).
  • OS keyring support (macOS Keychain, GNOME Keyring, or Windows Credential Manager). This is the default backend for storing the VTA’s master seed.
  • A host and port the VTA will listen on. Use http://localhost:8100 for local development, or your server’s public HTTPS URL if other machines need to reach it.

Step 1. Install the VTA and PNM binaries

This path uses two binaries:

  • vta: the appliance itself. It holds the master seed and serves signing and vault requests.
  • pnm: the operator CLI. You use it to configure and connect to the appliance remotely.

cargo install pulls both directly from the registry, so you don’t need to clone the source repository unless you want to build from source or inspect the code.

cargo install vta-service --locked --registry crates-io
cargo install pnm-cli@0.16.4 --locked --registry crates-io

Confirm the installation

vta --help
pnm help

You should see vta’s subcommands (including setup and bootstrap-admin) and PNM’s subcommands (including setup, health, keys, and contexts).

Step 2. Generate your Administrator DID

pnm setup mints a did:key locally and stores its private key in your OS keyring. The DID starts in a pending state because no VTA exists yet to register it with. You generate it now so Step 3 can embed it directly into the VTA’s own setup file. That lets a single vta setup call both create the VTA and grant your DID admin access.

pnm setup --name "my-vta"

Sample output:

{"slug":"my-vta","admin_did":"did:key:z6Mk...","state":"pending"}

Copy the admin_did value. You need it in the next step.

Step 3. Write a setup file

vta setup --from <file> provisions the VTA from a single TOML file, with no interactive prompts. Passing admin_did seeds your Administrator DID and seals the VTA in this same step, so you don’t need a separate vta bootstrap-admin or vta import-did call. Each block in the file configures one part of that process:

  • config_path and data_dir are where the generated config.toml and the on-disk key store are written. Treat both as sensitive: the store holds every key this VTA derives.

  • admin_did and admin_label seed your Administrator DID as super-admin and seal the VTA. No further offline provisioning is possible after this step. This suits a single-developer instance. For multi-operator setups, consider seeding the admin later instead.

  • [secrets] selects where the generated master seed is stored. keyring uses your OS credential manager, the safest option for a workstation.

  • [vta_did] mints the VTA’s own identity. Two kinds are available:

    • create_webvh (recommended): a did:webvh, a resolvable DID with a full, publicly verifiable history.
    • create_did_key: simpler, good for local development or a throwaway test instance, but has no hosted history to inspect.

    See VTA DID for why did:webvh is recommended.

# setup.toml
config_path = "./vta-data/config.toml"
data_dir    = "./vta-data/data"
vta_name    = "my-vta"
public_url  = "http://localhost:8100"

# Paste the admin_did printed in Step 2.
admin_did   = "did:key:z6Mk..."
admin_label = "ops"

[secrets]
backend = "keyring"
service = "my-vta"

[vta_did]
kind = "create_webvh"
url  = "http://localhost:8100"

Connect to a DIDComm mediator (optional)

The setup above configures REST only. If an integration guide you’re following needs DIDComm transport, for example retrieving credentials from the vault, add a [messaging] block naming an existing mediator DID. You need a mediator already running first: see DIDComm mediator deployment options.

[messaging]
kind = "existing"
did  = "did:webvh:...mediator-did..."

# Set true only if the mediator uses explicit_allow (closed) mode.
# Leave false for an explicit_deny (open) mediator.
setup_acl = false

services.didcomm defaults to true, so no extra flag is needed unless you disabled it. setup_acl = true provisions a per-DID allow-all ACL entry on the mediator once the DIDComm connection is established. This is required on a closed mediator. Without it, the VTA’s messages are rejected.

Step 4. Run setup

vta setup --from setup.toml

This generates a fresh 24-word BIP-39 mnemonic, stores it in your OS keyring, writes config.toml, mints the VTA’s did:webvh identity, and seeds your Administrator DID as super-admin. The output includes the VTA’s own DID:

VTA DID: did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:localhost%3A8100

Copy this value. You need it in Step 6.

Step 5. Start the VTA

Setup only prepared the configuration and the key store. This step starts the daemon that actually serves signing, vault, and identity requests over REST using what Step 4 created.

vta --config ./vta-data/config.toml

Leave this running. Open a new terminal for the remaining steps.

Step 6. Finish connecting PNM

Your PNM session has been pending since Step 2, waiting to learn which VTA it belongs to. This step binds it to the VTA DID the daemon minted in Step 4, moving the session from pending to complete.

pnm setup continue my-vta --vta-did <vta-did-from-step-4>

Sample output:

{"slug":"my-vta","admin_did":"did:key:z6Mk...","state":"complete"}

Confirm

Test 1: PNM authenticates and the VTA reports healthy

pnm health

On this first authenticated call, PNM auto-rotates from the temporary did:key to a fresh, permanent one and removes the temporary DID from the VTA’s access control list. Expected output:

── VTA ───────────────────────────────────────────
  DID           did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:localhost%3A8100
                ✓ resolves (webvh)
  Mode          REST
  URL           http://localhost:8100
  Service       ✓ ok

── Authentication ────────────────────────────────
  Client DID    did:key:z6Mk...
  Token         ✓ valid (expires in 60s)

If you configured a mediator in Step 3, pnm health also prints a Mediator section: the mediator’s DID, a resolution check, and a trust-ping round-trip.

Troubleshooting

SymptomLikely causeFix
vta exits immediately with an error naming a missing signing identity[vta_did] was omitted or set to kind = "skip" in setup.tomlAdd a [vta_did] block with kind = "create_webvh" and re-run vta setup --from setup.toml --overwrite-config.
vta setup fails with data directory already holds a storeYou ran vta setup more than once against the same data_dirSet data_dir_exists = "delete" in setup.toml for a clean re-run, or point data_dir at a new path.
pnm health shows ❌ on ServiceThe VTA process is not running, or public_url in setup.toml does not match where vta is actually listeningConfirm vta --config ./vta-data/config.toml is still running, and that the port matches public_url.
vta exits with server error: io error: Address already in use (os error 48)Another process (or a previous vta instance you forgot to stop) is already bound to the configured portStop the process holding the port (lsof -i :8100 on macOS/Linux), or edit [server].port in config.toml and the matching port in public_url, then restart vta.
vta setup fails resolving the mediator DIDThe did in [messaging] doesn’t resolve, or the mediator’s DIDComm endpoint isn’t reachable from this host.Confirm the mediator DID resolves on its own and the endpoint is reachable, then retry vta setup --from setup.toml --overwrite-config.
unauthorized on any pnm commandNo session stored in your OS keyring, or the DID was removed from the VTA’s ACLRun pnm auth status. If no session exists, re-run pnm setup --name "my-vta" and repeat from Step 3.

Next steps

  Create your first context and signing key

  Back up and restore VTA state

  Glossary: the terms used across these pages and in pnm output.