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 whole agent at a point in time, as a single encrypted .vtabak file: its master seed, keys, identities, contexts, access control configuration, vault, credentials, and the rest of its state. 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, and across plain, hardened, and Nitro Enclave deployments. 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.

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 keyKey material of internal keys, created with pnm keys create --internal: their records are restored, their material stays on the VTA that created them
Keys (derived and imported) and contextsSessions
ACL entriesCaches
Vault: third-party secrets and the credentials the VTA holdsIn-flight backup transfers
Issued credentialsIn-progress passkey enrolment ceremonies
DID templatesMessaging outbox
WebVH DID records and logsIdempotency records
Imported secret material (KEK-wrapped)Trust Spanning Protocol (TSP) relationships
Consent records, approvers, and task-consent grants
Agent memory and application state
Holder identity (persona)
Operator security policy
Messaging room groups and invitations
Service state
Audit log keys, and the audit trail itself with --include-audit

Everything in the right-hand column except internal key material is re-established on its own after a restore. Internal keys come back as records only, so they sign only on the VTA that created them. The import result names each one.

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.5.3
  Audit:       false
Backup password (min 15 chars): [hidden]
Validating backup (trust-task descriptor flow, HTTPS stream)...

  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, GET /health/details reports the restore: when it happened, the source DID and deployment kind, and anything that did not come back.
  • 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>.

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.5.3
  Audit:       false
Backup password (min 15 chars): [hidden]
Validating backup (trust-task descriptor flow, HTTPS stream)...

  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.

Troubleshooting

SymptomLikely causeFix
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.Re-provision affected services using pnm bootstrap.

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.