Deploy VTA appliance

Deploy a VTA appliance using Affinidi Portal to get started.

By the end of this guide, you will have a running VTA provisioned through Affinidi Portal, with PNM connected and ready to use.

The VTA is hosted as a managed appliance by Affinidi. You define the configuration through the portal, and Affinidi provisions, runs, and operates the underlying infrastructure for you, inside a Trusted Execution Environment (TEE). See VTA overview for the full picture.

You need this guide if you are starting from scratch. If you already have a running VTA, skip to Create your first context.

Deployment overview

Deploying an Affinidi-hosted VTA has three phases: install the PNM CLI, deploy the VTA in Affinidi Portal, then bootstrap PNM against it. Instead of you registering an Administrator DID yourself, PNM verifies the enclave’s attestation on first connect and only installs your admin credential if it matches. This is a Trust On First Use (TOFU) bootstrap: it refuses to proceed on any mismatch.

Install PNM CLICreate VTA ConfigurationAFFINIDI PORTALBootstrap PNMConfirm AccessPIN THE ENCLAVE & CONNECT via PNM CLI
PhaseWhereWhat you do
1 - Install the PNM CLIYour machineInstall the pnm binary. You need it in Phase 3, after the VTA is deployed.
2 - Deploy the VTAAffinidi PortalCreate a VTA configuration and wait for the status to reach Complete. Copy the ready-made bootstrap command from the configuration page.
3 - Bootstrap PNM against the VTAPNM (Personal Network Manager) CLIRun the copied pnm bootstrap connect command. It pins the enclave’s measurements, so PNM verifies the enclave before installing your admin credential. Confirm with pnm health.

Want to run the infrastructure yourself instead? See Self-hosted (open source).

Prerequisites

Step 1. Install the PNM CLI

To access and manage your VTA, you need the Personal Network Manager (PNM) CLI installed on your machine. You use it in Step 3 to connect to your VTA once it is deployed.

Install PNM

Run the command below to install a specific version of the PNM CLI from crates.io. Cargo fetches and builds the crate for you, so you do not need to clone the repository.

cargo install pnm-cli@0.23.1 --locked --registry crates-io

Confirm the installation

pnm help

You should see a list of subcommands, including bootstrap, health, keys, and contexts.

Step 2. Create the VTA in the Affinidi Portal

  1. Log in to Affinidi Portal and select your project.

  2. Go to Verifiable Trust Agent in the left sidebar.

  3. Click Create configuration and fill in the fields:

    Create VTA configuration in Affinidi Portal

    Create VTA configuration in Affinidi Portal

    FieldDescription
    Name of configurationA display name for this VTA instance in the portal.
    Description - optionalA short note on the purpose of this instance.
    Identity typeFixed to did:webvh for an Affinidi-hosted VTA.
    Mediator DID - optionalAdd a mediator DID to enable DIDComm-based message routing. Must use did:web, did:webvh, or did:peer (for example, did:webvh:example.com). To deploy one, see Deploy an Affinidi-hosted mediator.
    Appliance sizeChoose Basic, the size a VTA runs on. Depending on your plan, the list also shows Enhanced (Standard plan upward) and Custom (Premium and Affinidi plans), disabled for a VTA.
    VersionAppears only when more than one version set is available to your project. It defaults to the current version.
  4. Click Create and wait for the status to show Complete.

    VTA configuration page while deployment is in progress, with Deployment state In Progress and the VTA DID still pending

    Wait for the deployment to complete.

  5. Once the status is Complete and the page shows the VTA DID and PCR0 measurement, copy the bootstrap command from the Verify the enclave before you bootstrap alert on the configuration page. You run it in Step 3. If you dismissed the alert, click Show bootstrap instructions to bring it back.

    The command pins the first three of these values. The VTA URL is the fallback bootstrap target described in Step 3.

    ValueStarts withUsed for
    VTA DIDdid:webvh:Issuer identity on all credentials this VTA produces. The command pins it, so PNM connects only to this VTA.
    PCR0 measurementA hex stringThe measurement of the software running inside the VTA’s Trusted Execution Environment (TEE). The command pins it, so PNM refuses to connect to anything other than this exact build.
    PCR8 measurementA hex stringThe measurement of the certificate that signed that software image. Shown when Portal has recorded it, and the command includes it when Portal has it.
    VTA URLhttps://Public REST endpoint for admin calls and integrations. Also the fallback bootstrap target while the VTA DID is still publishing.
    VTA configuration page after deployment, showing the VTA DID, PCR0 and PCR8 measurements, and the Verify the enclave before you bootstrap alert with its bootstrap command

    Copy the bootstrap command from the alert below the configuration details.

Step 3. Bootstrap PNM against the VTA

PNM now verifies the enclave’s attestation and installs your admin credential, as described in Deployment overview above.

Paste the bootstrap command you copied in Step 2 into your terminal and run it. It looks like this:

pnm bootstrap connect --vta-did did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io --expect-pcr0 8f1d3c5a7b9e...c9a1f3b5 --expect-pcr8 4b2e9d6f0a1c...a7c1e0d2

PNM registers each VTA under a short local name, its VTA slug, which you pass as --vta <vta-slug> in later commands. The slug defaults to the hostname at the end of the VTA DID, your-appliance.vta.affinidi.io in this example. To choose a shorter name, add --slug <name> to the command before you run it.

Two independent checks run before your credential installs, and both must pass:

  • The DID pins which VTA answers. PNM resolves the did:webvh locally, verifying its self-certifying identifier (SCID) and its log, then connects to the REST endpoint that document advertises. A credential minted by a different VTA is refused.
  • PCR0 pins what is running inside it. The pinned value is checked against the enclave’s attestation quote, so a genuine VTA running a build you did not expect is refused as well. When the command also carries --expect-pcr8, PNM checks the certificate that signed that build in the same way.

The pinned PCR0 is also the trust anchor that replaces the digest here, which is why no --expect-digest is involved: bootstrap connect mints its bundle during the call, so no out-of-band digest can exist beforehand to compare against.

Expected output:

TEE attestation verified.
  Enclave module: i-0123456789abcdef0-enc0123456789abcdef
  PCR0:           8f1d3c5a7b9e...c9a1f3b5 (pinned ✓)
  PCR8:           4b2e9d6f0a1c...a7c1e0d2 (pinned ✓)

Bootstrap complete.
  VTA slug:   your-appliance.vta.affinidi.io
  Client DID: did:key:z6MkjLfAT61ZbMfUTytxjp6KnDh7hy9jE5YkQJNuouJEC4cU
  VTA DID:    did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io
  Digest:     b1946ac92492d234...7c6235b4d2611184

(pinned ✓) next to PCR0 is the line to check: it confirms the attested measurement matched the value you pinned, so the credential installed against the build you expected. (pinned ✓) appears next to PCR8 too when the command pinned it.

Step 4. Confirm the connection

pnm --vta <vta-slug> health

Replace <vta-slug> with the VTA slug value from Step 3’s output.

Expected output:

  VTA: your-appliance.vta.affinidi.io
  DID: did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io

── VTA ───────────────────────────────────────────
  DID           did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io
                ✓ resolves (webvh)
  Mode          DIDComm + REST
  URL           https://your-appliance.vta.affinidi.io (from DID)
  Service       ✓ ok

── Authentication ────────────────────────────────
  Client DID    did:key:z6MkjLfAT61ZbMfUTytxjp6KnDh7hy9jE5YkQJNuouJEC4cU
  Token         ✓ valid (expires in 899s)

── Mediator ──────────────────────────────────────
  DID           did:webvh:QmSW59XWpXN4xNX5ZJvkYmaJBpBH7d1WN1fR6ZZG8xtjGE:your-appliance.mediator.affinidi.io
                ✓ resolves (webvh)
                ✓ pong (14ms)

── VTA DIDComm ───────────────────────────────────
  Trust-ping    ✓ pong (25ms)

If any check shows ✗, see Troubleshooting below.

PNM treats the first VTA you register on a machine as its default, and --vta picks a specific VTA for a single command. To make this VTA the default for every later command, run:

pnm vta use <vta-slug>

pnm vta list shows every VTA registered on this machine.

Troubleshooting

SymptomLikely causeFix
pnm bootstrap connect fails with --expect-digest <hex> is required (or pass --no-verify-digest to opt out with a warning)pnm is older than 0.16.4, where a pinned PCR0 became a trust anchor in its own right. This error appears with the --vta-url form.Upgrade with the cargo install command in Install PNM, then re-run the command from Step 3.
pnm bootstrap connect fails with unexpected argument '--vta-did'Same cause: --vta-did was added in 0.16.4.Upgrade pnm, or use the --vta-url form until you do.
Could not resolve bootstrap endpoint for VTA DID ...The VTA’s DID log has not published yet, or this machine cannot reach it. Bootstrap always resolves locally, so a reachable resolver sidecar does not satisfy it: PNM_RESOLVER_URL and [resolver_url] are deliberately ignored for this call.Retry once the DID log is published and reachable from this machine, or bootstrap with --vta-url instead, accepting that it does not pin the VTA’s identity.
pnm bootstrap connect fails with PCR0 mismatch: enclave reported …, operator expected …The pinned PCR0 does not match the enclave’s live attestation, so the running software is not the build you expected.Treat this VTA as untrusted. Its one-time bootstrap is already used, so delete the configuration in Affinidi Portal, deploy a new one, and confirm the PCR0 on its configuration page before you bootstrap it.
pnm bootstrap connect fails with bootstrap request failed (410 Gone): {"error":"gone: TEE first-boot carve-out has already been used"}The VTA already has an admin credential installed; the attested bootstrap endpoint is single-useExpected once bootstrap has already succeeded. Run pnm --vta <vta-slug> health to confirm the existing connection instead.
pnm health shows ✗ trust-ping failed or ✗ trust-ping timed out on Trust-pingVTA DIDComm layer has stalledRestart the VTA with pnm vta restart, then wait for the confirmation VTA is back. Re-run pnm health to confirm Trust-ping shows ✓ pong.

Next steps

  Create your first context and signing key

  VTA overview

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