Deploy VTA appliance
Affinidi-hosted Verifiable Trust Agent (VTA) is currently in Early Access. Join the Early Access Programme to get access before continuing. A Basic-tier configuration is automatically deleted 60 days after creation; Enhanced and Custom tiers are not. You can create a new configuration afterward.
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.
| Phase | Where | What you do |
|---|---|---|
| 1 - Install the PNM CLI | Your machine | Install the pnm binary. You need it in Phase 3, after the VTA is deployed. |
| 2 - Deploy the VTA | Affinidi Portal | Create a VTA configuration and wait for the status to reach Complete. Copy the VTA DID, VTA URL, and PCR0 measurement. |
| 3 - Bootstrap PNM against the VTA | PNM (Personal Network Manager) CLI | Run pnm bootstrap connect with the PCR0 measurement pinned, 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
- Join the Early Access Programme to get access to the Verifiable Trust Agent.
- An Affinidi Portal account with access to the target project.
- Install Rust 1.95 or later on your machine (required for compiling the PNM CLI).
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.16.4 --locked --registry crates-ioConfirm the installation
pnm helpYou should see a list of subcommands, including bootstrap, health, keys, and contexts.
Step 2. Create the VTA in the Affinidi Portal
Log in to Affinidi Portal and select your project.
Go to Verifiable Trust Agent in the left sidebar.
Click Create configuration and fill in the fields:

Create VTA configuration in Affinidi Portal
Field Description Name of configuration A display name for this VTA instance in the portal. Description - optional A short note on the purpose of this instance. Identity type Always did:webvhfor an Affinidi-hosted VTA.Mediator DID - optional Add a mediator DID to enable DIDComm-based message routing. Must use did:web,did:webvh, ordid:peer(for example,did:webvh:example.com).Appliance size Choose a size. Available sizes depend on your plan: Basic on every plan, Enhanced from Standard upward, Custom on Premium and Affinidi plans. NoteAppliance size cannot be changed after the VTA configuration is created. Create a new configuration to change it.
If you already know your Mediator DID, add it here rather than later. Adding it to an existing REST-only configuration currently requires the mediator to already allow the connection by default; when the mediator has a closed access control list (ACL), the handshake doesn’t go through.
Click Create and wait for the status to show Complete.

Wait for the deployment to complete.
Once complete, copy three values from the configuration page:
Value Starts with Used for VTA DID did:webvh:Issuer identity on all credentials this VTA produces. You pin this in Step 3, so PNM connects only to this VTA. VTA URL https://Public REST endpoint for admin calls and integrations. Also the fallback bootstrap target while the VTA DID is still publishing. PCR0 measurement A hex string The measurement of the software running inside the VTA’s Trusted Execution Environment. You pin this in Step 3, so PNM refuses to connect to anything other than this exact build. 
Copy the relevant fields to continue setting up PNM.
⚠ Early Access limitEach project is limited to one VTA configuration during Early Access. To deploy a new VTA, delete the existing configuration first.
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.
This is a one-time, single-use step. Once your admin credential is installed, a second pnm bootstrap connect against the same VTA fails. That’s expected: it means bootstrap already succeeded.
Pin the PCR0 measurement every time you bootstrap a new VTA. A mismatch means the running software is not the build you expected. Confirm the current PCR0 value on the Affinidi Portal configuration page before retrying.
Back in your terminal, run:
pnm bootstrap connect \
--vta-did <vta-did> \
--expect-pcr0 <pcr0>Replace <vta-did> and <pcr0> with the VTA DID and PCR0 measurement copied from Step 2. A filled-in command looks like this:
pnm bootstrap connect \
--vta-did did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io \
--expect-pcr0 8f1d3c5a7b9e...c9a1f3b5Two independent checks run before your credential installs, and both must pass:
- The DID pins which VTA answers. PNM resolves the
did:webvhlocally, 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.
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.
Bootstrap resolves the DID locally and deliberately ignores any configured resolver URL, so a VTA whose DID log has not published yet cannot be reached this way. Use the VTA URL instead until it publishes:
pnm bootstrap connect --vta-url <vta-url> --expect-pcr0 <pcr0>--vta-did and --vta-url are mutually exclusive. The URL form still pins the enclave through PCR0, and it does not pin which VTA answers, so prefer the DID form whenever the DID resolves.
Expected output:
TEE attestation verified.
Enclave module: i-0123456789abcdef0-enc0123456789abcdef
PCR0: 8f1d3c5a7b9e...c9a1f3b5 (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.
Step 4. Confirm the connection
pnm --vta <vta-slug> healthReplace <vta-slug> with the VTA slug value from Step 3’s output. --vta is required if PNM has more than one VTA registered: only the first VTA you ever bootstrap on a machine becomes the default, so pnm health alone would otherwise check the wrong one.
Expected output:
── 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 60s)
── Mediator ──────────────────────────────────────
DID did:webvh:QmSW59XWpXN4xNX5ZJvkYmaJBpBH7d1WN1fR6ZZG8xtjGE:your-appliance.mediator.affinidi.io
✓ resolves (webvh)
✓ pong (14ms)
── VTA DIDComm ───────────────────────────────────
Trust-ping ✓ pong (25ms)If any check shows ❌ or ⚠️, see Troubleshooting below.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
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.3, where a pinned PCR0 became a trust anchor in its own right. | 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.3. | 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 refuses with a PCR0 mismatch | The pinned PCR0 does not match the enclave’s live attestation | Do not proceed past a mismatch. Confirm the current PCR0 value on the Affinidi Portal configuration page before retrying. |
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-use | Expected once bootstrap has already succeeded. Run pnm --vta <vta-slug> health to confirm the existing connection instead. |
pnm health shows ❌ on Trust-ping with No response from API or the ping times out | VTA DIDComm layer has stalled | Restart the VTA with pnm vta restart, then wait for the confirmation VTA is back. Re-run pnm health to confirm Trust-ping returns ✓ pong. |
Next steps
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.