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 can no longer start or refresh a session.

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 VTAAn admin whose scope covers the integration’s context, or a super-admin, runs pnm acl create for a new DID

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 pnm bootstrap and pnm auth-credential create use. 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.23.1 --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

Run this on the integration host, since the bundle can only be opened on the machine that generated the 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 printed to stderr. 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: Open the bundle on the integration host

The bundle can be opened once. Opening it consumes the single-use seed that pnm bootstrap provision-request stored in Step 1, on the machine that ran it, so run Step 1 on the integration host and open the bundle there, as part of the install.

Open it with the integration’s 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. Give it the digest from Step 2, so it confirms the bundle it opens is the one the VTA produced.

pnm bootstrap open can also open the bundle, and with --expect-digest and --expect-vta-did it checks the digest and the authorisation VC. It prints only a summary and writes nothing to the integration’s keystore, and the seed is gone afterwards, so use it only when you are prepared to repeat Steps 1 and 2 before installing.

Step 4: Install the new credentials

The integration’s setup tooling places the bundle’s contents into the integration’s own stores. 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.

The previous admin DID keeps its access until you remove it in Step 5.

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, the old DID can no longer refresh, and an access token it already holds keeps working until it expires, 15 minutes by default. If the key was compromised, delete the entry immediately and revoke the DID’s sessions with DELETE /auth/sessions?did=<old-admin-did>, as described in Grant and revoke access.

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.