Back up and restore VTA state

How to export and import a full VTA backup using pnm backup export and pnm backup import, including encryption details, what is included, and compatibility rules.

By the end of this guide, you have a password-encrypted backup of your VTA’s operator state, and you know how to restore it.

A VTA backup captures the agent at a point in time, as a single encrypted .vtabak file: its master seed, keys, identities, contexts, access control configuration, vault, credentials, and most of its other state. For what it leaves out, see What is backed up. Restoring it replaces all current data in the target VTA. For what the keys inside it are derived from, and why the master seed matters to recovery, see Keys and contexts.

Rebuilding a lost VTA by hand means re-minting every key, re-provisioning every application, and reconstructing the access control list (ACL) from memory, with the original DIDs gone. A backup is what turns that into a restore, onto the same VTA or a new one. A backup restores across plain, hardened, and Nitro Enclave deployments. A VTA in Isolated mode is a Nitro Enclave deployment: it runs in an AWS Nitro Enclave, a Trusted Execution Environment (TEE), with hardware-rooted attestation, keys and data. Runtime records such as sessions are re-established after a restore; see What is backed up.

Use this guide when:

  • You are about to depend on a VTA and need a recovery position first.
  • You are migrating a VTA to a new host.
  • You want to clone a VTA environment, for example to seed staging from development.
  • You are about to make a change you may need to roll back.
Running VTAkeys, ACL, contexts, WebVH DIDsExportpnm backup exportARGON2ID + AES-256-GCM.vtabak filepassword-encryptedImportpnm backup importREPLACES ALL CURRENT DATA

Prerequisites

  • A running VTA. See Quickstart.
  • pnm installed and connected to the VTA as a super-admin. Exporting and importing a backup are super-admin operations.
  • A DIDComm or Trust Spanning Protocol (TSP) connection to the VTA. The VTA refuses backup export and import over REST, because the backup password would be readable wherever TLS terminates. To add DIDComm to a self-hosted VTA, see Connect to a DIDComm mediator.

Exporting a backup

pnm backup export

You are prompted to enter and confirm a password (minimum 15 characters). The backup holds the master seed, so anyone with the file and its password can recreate every derived key: use a long, unique password. The backup is saved to a .vtabak file in the current directory:

vta-backup-<did-tail>-<timestamp>.vtabak

<did-tail> is the final segment of the VTA’s DID, not the full DID value.

Including audit logs

Audit logs are excluded from the default export because they can be large. Add --include-audit to include them:

pnm backup export --include-audit

Specifying the output path

pnm backup export --output /path/to/my-backup.vtabak

What is backed up

IncludedNot included
Master seed and JWT signing keySessions
Keys (derived and imported) and contextsCaches
ACL entriesIn-flight backup transfers
Vault: third-party secrets and the credentials the VTA holdsIn-progress passkey enrolment ceremonies
Issued credentialsMessaging outbox
DID templatesIdempotency records
WebVH DID records and logsTrust Spanning Protocol (TSP) relationships
Imported secret material (KEK-wrapped)Provider secrets of external accounts
Consent records, approvers, and task-consent grantsTrust Task push records
Agent memory and application stateThe deployment’s own boot material
Holder identity (persona)Internal keys (see Recovery boundary)
Operator security policy
Data room group state and consumed room invitations
External account records
Service state
Audit log keys, and the audit trail itself with --include-audit

After a restore, sessions, caches, idempotency records, and TSP relationships are re-established on their own. In-flight backup transfers, passkey enrolments, and queued outbound messages are discarded, so start them again. A restored external account asks for its provider secret again.

Recovery boundary

A backup is how you recover a VTA as a whole, so take one before you depend on the VTA, and again after changes you need to keep. On an Affinidi-hosted VTA, in Isolated or Shared mode, a backup is the only recovery path: you have no access to the host, and no command returns the VTA’s master seed to you outside a backup.

Two things stay outside every backup, so plan for them separately:

  • Your administrators’ private keys. A backup holds the access control list (ACL), which names each admin’s DID, but each private key stays with its admin, in that admin’s PNM keyring. If the only super admin’s key is lost, a restore brings back an entry nobody can use. Grant a second super admin, held by another person or stored offline, with pnm acl create --did <did> --role admin --label <label> and no --contexts. See Roles.
  • Internal keys. A key created as internal through the VTA SDK is generated on the VTA, separately from the master seed, and stays on that VTA. Its material is lost if you lose the VTA, and it stays behind when you restore onto a different VTA, for example in a migration: the record comes back, but it can’t sign there. A restore onto the VTA that took the backup keeps it. The import message lists internal keys as not in the backup and lost in both cases; pnm health after the restart shows which ones were actually lost.

Encryption details

Backups are encrypted with Argon2id + AES-256-GCM:

ParameterValue
KDFArgon2id
Memory cost64 MiB (OWASP-recommended profile)
Iterations3
Parallelism4 threads
EncryptionAES-256-GCM
Salt32 bytes, random per export

The KDF parameters are stored in the backup envelope. On import, the VTA validates that the parameters fall within accepted bounds. Extreme values are rejected to prevent memory exhaustion, which matters most for Nitro Enclave deployments.

Inspecting a backup before importing

Use --preview to see what a backup contains without applying any changes:

pnm backup import my-backup.vtabak --preview

Output:

The preview asks for the backup password, decrypts the backup, and counts its contents:

Backup file: my-backup.vtabak
  Source DID:  did:webvh:...
  Created:     2026-06-18 10:00:00.482913107 UTC
  Version:     0.20.0
  Audit:       false
Backup password (min 15 chars): [hidden]
Validating backup (trust-task descriptor flow, chunked over DIDComm)...
  sent chunk 1/1 (12.0 KiB / 12.0 KiB)

  Keys:        12
  ACL entries: 5
  Contexts:    3
  Audit logs:  0

Preview only — no changes applied.

Importing a backup

pnm backup import my-backup.vtabak

The import flow:

  1. Metadata display: shows source DID, creation time, version, and whether audit logs are included.
  2. Password prompt: enter the backup password (not confirmed on import).
  3. Preview: shows counts of keys, ACL entries, contexts, and audit logs to be restored.
  4. Confirmation: after WARNING: This will REPLACE ALL DATA in the VTA., type yes at Type 'yes' to confirm:. Any other answer prints Import cancelled. and changes nothing.
  5. Apply: the VTA commits the import, and pnm prints The VTA is restarting to apply the restore. All current data is replaced with the backup contents on that boot.

Compatibility rules

ConditionResult
Restoring a backup of the VTA’s own DIDAccepted
Restoring a backup of a different DID, such as onto a freshly set-up VTA during disaster recovery or a migrationRefused, unless you pass --replace-identity
Restoring across deployment kinds, between plain, hardened, and Nitro Enclave VTAsAccepted in any direction

A freshly set-up VTA already has a DID of its own, so a restore onto it replaces that identity. Run the import with --replace-identity to confirm that is what you want:

pnm backup import my-backup.vtabak --replace-identity

The restored VTA then runs as the backup’s DID.

On a self-hosted target, two more requirements apply:

  • Persistent seed store: a plain or hardened VTA must keep its seed in a store that survives a restart, such as the OS keyring, a cloud secret manager, Vault, or Kubernetes. A restore into one that cannot is refused before anything changes.
  • Same deployment kind until the restart: keep [hardened] enabled unchanged, and stay on the same kind of deployment, between the import and the restart that applies it.

After import

  • The VTA restarts itself to apply the restore. Once it is back, pnm health, run as an admin, reports the restore on the Restored line of its Deployment section: when it was applied and who staged it, the source DID and deployment kind, and anything that did not come back, such as internal keys or hosted DIDs.
  • If the VTA DID has changed, re-authenticate with credentials from the restored backup, and inform any integrations of the new VTA DID.
  • DIDs the VTA hosted on a DID-hosting server are detached from that server after a --replace-identity restore. Re-attach each with pnm did-mgmt dids register --did <did> --server <server-id>.
  • A self-hosted VTA, or a VTA in Shared mode, takes its public URL and mediator from the backup. A VTA in Isolated mode keeps the public URL and mediator it was created with. When you restore into a new Affinidi-hosted VTA, in Isolated or Shared mode, contact Affinidi support before you rely on the VTA. In both modes, the restored DID document advertises the backup’s public URL and mediator, so the new appliance must be reachable there.

Confirm

Test 1: the exported file exists and previews cleanly

Run this against the file pnm backup export wrote, before you rely on it for recovery. A backup you have never read back is not yet a backup.

pnm backup import vta-backup-<did-tail>-<timestamp>.vtabak --preview
Backup file: vta-backup-<did-tail>-<timestamp>.vtabak
  Source DID:  did:webvh:...
  Created:     2026-06-18 10:00:00.482913107 UTC
  Version:     0.20.0
  Audit:       false
Backup password (min 15 chars): [hidden]
Validating backup (trust-task descriptor flow, chunked over DIDComm)...
  sent chunk 1/1 (12.0 KiB / 12.0 KiB)

  Keys:        12
  ACL entries: 5
  Contexts:    3
  Audit logs:  0

Preview only — no changes applied.

Test 2: the counts match the VTA you exported from

Compare the preview counts against the live VTA:

pnm keys list
pnm acl list
pnm contexts list

Run as a super-admin, these commands return the same Keys, ACL entries, and Contexts totals as the preview. A context-scoped admin sees only its own contexts, so its counts are lower. If the preview is lower than a super-admin’s listing, the export ran before the most recent changes.

Test 3: the password is the one you recorded

A successful preview in Test 1 already proves the password, because the preview decrypts the backup to count its contents. Record the password somewhere separate from the backup file: there is no password recovery path for VTA backups.

Test 4: pnm health after an import returns the restore record

Once the VTA is back from the restart that applies an import, run this as an admin:

pnm health

The Deployment section shows the restore on its Restored line:

  Restored      yes — applied <applied-at>, staged by <admin-did>
                from did:webvh:...
                hardened → tee

The second line is the source DID, and the third the source and target deployment kinds: plain, hardened, or tee for a Nitro Enclave. Anything that did not come back follows on its own line, as lost internal key <key-id>, or as detached <did> with the command to re-register that DID. Restored no means the VTA has no record of a restore: check that the import finished and the VTA restarted.

Troubleshooting

SymptomLikely causeFix
Export or import fails with refused over a hop-by-hop transportpnm reached the VTA over REST, which carries no end-to-end encryption for the backup password.Connect the VTA to a DIDComm mediator so pnm can use DIDComm or TSP, then run the command again.
Import fails with backup vta_did mismatch: …The target VTA runs as a different DID from the backup source, for example a freshly set-up VTA.Re-run with pnm backup import <file> --replace-identity if replacing the target’s identity is intended. See Compatibility rules.
Import fails with incorrect backup passwordThe password does not match the one used at export.Re-enter the password recorded at export. A backup whose password is lost cannot be opened, so take a new export from the source VTA if it is still running.
A hosted DID stops updating after a restoreA --replace-identity restore detaches hosted DIDs from their DID-hosting server.Re-attach each with pnm did-mgmt dids register --did <did> --server <server-id>.
Service cannot authenticate after restoreService credentials were minted after the backup was taken.Issue each affected service a new credential with pnm auth-credential create --recipient, then open it on the service’s machine with pnm bootstrap open. See Rotate an application credential.

Next steps

  Retrieve credentials from the VTA vault at runtime: how services read the vault entries a restore brings back.

  Sign application payloads without exposing your keys: provision app credentials, which are also captured in a VTA backup.

  Grant and revoke access: grant, scope, and revoke access roles for DIDs restored from a backup.