Rotate an integration's admin DID

Mint a fresh admin DID for a mediator, WebVH host, or other admin-scoped integration by re-running its provisioning flow with –admin-template.

By the end of this guide, an admin-scoped integration holds a freshly minted admin DID, and its previous one no longer has access.

Use this when an admin-scoped integration’s admin DID (a mediator, a WebVH host, or another admin-scoped integration) is compromised, lost, or due for rotation. Rotating this admin DID isn’t a standalone operation: it rides along with re-running the same provision-request / provision-integration flow used to provision the integration in the first place, this time passing --admin-template vta-admin. The VTA mints a fresh admin did:key, binds it to the integration’s context, and updates the access control list (ACL), while re-rendering the integration’s own DID material in the same bundle.

Use this guide when:

  • An integration’s admin private key has been exposed, or you suspect it has.
  • An integration’s admin DID is due for rotation under your own key-rotation policy.
  • You are migrating an integration to new infrastructure and want it to present a new admin identity.

You do not need this guide for an application credential that only signs or reads; see Rotate an application credential instead.

When to use this

SituationWhat to do
Compromised admin private keyRe-run provisioning with --admin-template vta-admin: revoke old, mint new
Routine key rotation policySame re-run
First provisioning (setup to long-term DID)Already handled by passing --admin-template vta-admin on the integration’s initial provision-request
Lost admin DID, no access to VTAA super-admin must run pnm acl create with a new DID manually

How it works

The VTA mints a fresh admin did:key and delivers it, together with the integration’s re-rendered DID material, as a sealed bundle, the same mechanism every credential-bearing VTA operation uses. The steps below walk through it: generate a request, send it, hand the bundle to the integration’s own setup tooling, then revoke the old admin DID once the new one is confirmed working.

Current adminholds the old admin DIDRotation bundlesealed to the requesting host's keyseals rotation bundle to the requesting host's public keySealed transfer:only the requesting host's private key can open it.Rotation bundlestill sealedIntegration setup toolinginstalls the new admin credentialunsealed locallyOld DID revoked:the previous admin credential stops working once rotation completes.

Prerequisites

  • A running VTA. See Quickstart.

  • Install Rust 1.95 or later on your machine.

  • pnm installed and connected to the VTA.

    cargo install pnm-cli@0.16.4 --locked --registry crates-io
  • Admin access scoped to the integration’s context, or super-admin access to the VTA.

Step 1: Generate a new provision request

Use the same --template this integration was originally provisioned with (for example didcomm-mediator or did-hosting-control), and add --admin-template vta-admin to roll the admin DID over in the same request.

pnm bootstrap provision-request \
    --template       <integration-template> \
    --admin-template vta-admin \
    --context-hint   my-app \
    --label          my-app-admin-rotation \
    --out            my-app-rotation-req.vp.json

Step 2: Send the request to the VTA

Note the SHA-256 digest from stdout. Keep it out-of-band from the bundle file.

pnm bootstrap provision-integration \
    --request  my-app-rotation-req.vp.json \
    --context  my-app \
    --out      my-app-rotation-bundle.armor

The VTA re-renders the integration’s DID material from --template, mints a new admin did:key under its own key custody because --admin-template was set, binds authorisation to it, and adds it to the ACL. The old admin DID is left in the ACL until you remove it in Step 5.

Step 3: Verify the bundle

This bundle carries the integration’s re-rendered DID material and the new admin authorisation together, not a standalone admin credential, so pnm bootstrap open --out <path> refuses to write it out as a plain credential file. Inspect and verify it instead:

pnm bootstrap open \
    --bundle        my-app-rotation-bundle.armor \
    --expect-digest <digest-from-step-2>

This checks the digest against the value from Step 2, verifies the authorisation VC, and prints a summary: the template name, how many DIDs it carries key material for, the template outputs, and the VTA URL. A digest mismatch stops here rather than printing anything.

Step 4: Install the new credentials

pnm bootstrap open prints the payload; it does not write it into the integration’s keystore. That install step is specific to each integration and belongs to its own setup tooling, the same tooling that consumed the bundle when the integration was first provisioned. An integration built on the VTA SDK does this through the SDK’s provisioning client rather than by hand.

Whatever performs the install, these are the fields it must place:

Field in the bundleWhere it goes
secrets[<integration-did>]The integration’s own signing and key-agreement keys, into its keystore.
secrets[<admin-did>]The new long-term admin DID keys, persisted as the integration’s admin identity. This is the half that completes the rotation.
outputs (for a did:webvh template)The did.jsonl log content, saved to disk and published at the URL supplied in Step 1.
VTA trust bundleThe VTA DID, root key, and context ID, so the integration trusts inbound DIDComm from this VTA.
The authorisation VCArchive it for audit. The VTA’s ACL is the authority in steady state, so this credential is never re-verified after the first open.

Once the install completes, tokens issued to the previous admin DID expire within 15 minutes. After that, any call using the old DID returns 401.

Step 5: Revoke the old admin DID

The old admin DID is not removed automatically. It retains ACL access until you delete it.

pnm acl delete <old-admin-did>

Once deleted, any token issued to the old DID is rejected at its next refresh, within 15 minutes. If the key was compromised, do this immediately.

Confirm

Test 1: the new admin DID is in the ACL

pnm acl list --context my-app

Both the new and the old admin DID appear at this point. Run this before Step 5, so you confirm the replacement exists before removing what it replaces.

Test 2: the integration operates with the new admin DID

Exercise an admin-scoped operation through the integration itself, such as the action its setup tooling performs on first boot. It completes without re-authenticating as the old DID.

Test 3: the old admin DID is gone

pnm acl list --context my-app

The old admin DID is absent and the new one remains.

Next steps

  Grant and revoke access: manage roles and context scope for all DIDs registered with the VTA.

  Rotate an application credential: the equivalent flow for a least-privilege application identity.