Mint, inspect, and retire signing keys

Mint a signing key in a context, list and inspect the keys a context holds, relabel one, and revoke a key that should no longer sign.

By the end of this guide, you can add a signing key to a context, see what a context already holds, and take a key out of service.

A VTA holds the private half of every signing key it mints and signs on a caller’s behalf, so managing keys means managing records on the VTA rather than files on your machine. For what a key record contains, and how derived keys differ from internal ones, see Keys and contexts.

Without this, a context accumulates keys nobody can account for: minting is not idempotent, so a repeated setup script leaves duplicates behind, and a key that should have been retired keeps signing until someone removes it.

Use this guide when:

  • An application needs its own signing key inside a context.
  • You want to see which keys a context already holds before adding another.
  • A key is being retired, replaced, or was minted by mistake.
  • A key’s label no longer describes what it is used for.

You do not need this guide to replace an application’s credential, which is a separate thing from the keys it signs with; see Rotate an application credential.

Prerequisites

  • A running VTA. See Quickstart.

  • pnm installed and connected to the VTA.

    cargo install pnm-cli@0.16.4 --locked --registry crates-io
  • Admin access to the VTA. Minting and revoking keys are admin-only operations.

  • The ID of the context the key belongs to, from pnm contexts list.

Mint a key

Every context starts with no keys. Mint one so the applications in that context have something to sign with:

pnm --vta <vta-slug> keys create \
    --key-type ed25519 \
    --context  my-app \
    --label    "my-app-key-1"
Key created:
  Key ID:          m/26'/2'/2'/0'
  Key Type:        ed25519
  Derivation Path: m/26'/2'/2'/0'
  Public Key:      z6MkvZqY2CbpWQmbjuuNshtAptH8qbFaCGk3w6Jt72xBXiGi
  Status:          active
  Label:           my-app-key-1

The Key ID is the derivation path, not a short opaque identifier. Give every key a --label that says what uses it: the label is the only field that will still mean something to you a year from now.

This command is not idempotent. Running it twice mints two keys, so check what a context already holds before minting into it.

List the keys a context holds

pnm --vta <vta-slug> keys list --context my-app

Add --status active to hide keys that have already been revoked. A context with nothing in it reports No keys found.

Inspect one key

pnm --vta <vta-slug> keys get "m/26'/2'/2'/0'"
Key ID:          m/26'/2'/2'/0'
Key Type:        ed25519
Derivation Path: m/26'/2'/2'/0'
Public Key:      z6MkvZqY2CbpWQmbjuuNshtAptH8qbFaCGk3w6Jt72xBXiGi
Status:          active
Label:           my-app-key-1

Quote the key ID in your shell. It contains ' characters, which an unquoted shell will try to interpret.

Public Key is the value a third party needs to check a signature this key produced. It is safe to share, and it is the only half of the pair that ever leaves the VTA.

Relabel a key

A label is metadata, so changing it affects nothing that the key signs:

pnm --vta <vta-slug> keys rename "m/26'/2'/2'/0'" --label "my-app-signing-2026"

Retire a key

Revoking marks the key as no longer usable for signing. The record stays, so the key ID keeps resolving and your audit trail still makes sense:

pnm --vta <vta-slug> keys revoke "m/26'/2'/2'/0'"
Key revoked:
  Key ID:     m/26'/2'/2'/0'
  Status:     revoked

Mint the replacement and move the application onto it before revoking, rather than after. Signatures the key produced in the past remain verifiable against its public key; revoking stops new ones.

Confirm

Test 1: the new key is listed as active

pnm --vta <vta-slug> keys list --context my-app --status active

The key from “Mint a key” appears, with the label you gave it.

Test 2: the key signs

Ask the VTA to sign a short payload with the key. A base64url-encoded signature comes back, produced without the private key leaving the VTA:

KEY_ID="m/26'/2'/2'/0'"
KEY_ID_ENCODED=$(printf '%s' "$KEY_ID" | sed "s#/#%2F#g")
TOKEN=$(pnm --vta <vta-slug> auth show-token)

curl -X POST "https://<vta-url>/keys/${KEY_ID_ENCODED}/sign" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"payload": "aGVsbG8", "algorithm": "EdDSA"}'

The key ID contains / characters, so percent-encode it before putting it in a URL path.

Test 3: a revoked key no longer signs

Re-run Test 2 against a key you revoked. The VTA refuses it, which confirms the revocation took effect rather than only changing a displayed status.

Troubleshooting

SymptomLikely causeFix
pnm keys create returns unauthorizedMinting is admin-only, and your session holds a lesser role.Run pnm auth status to see the identity you are connected as, then use an admin credential.
Two keys with the same label in one contextkeys create is not idempotent, so a setup script ran twice.Revoke the duplicate, then list the context to confirm one active key remains.
The shell reports a quoting error on a key IDThe ID contains ' characters and was passed unquoted.Wrap the key ID in double quotes: "m/26'/2'/2'/0'".
curl returns 404 on the sign callThe key ID went into the URL without percent-encoding, so / split the path.Percent-encode the key ID, as in Test 2.
A revoked key still appears in pnm keys listRevocation sets the status rather than deleting the record, so the audit trail stays intact.Add --status active to list only usable keys.

Next steps

  Sign application payloads without exposing your keys: give an application its own identity and have it sign with a key you minted here.

  Keys and contexts: how keys derive from one master seed and why contexts isolate them.

  Grant and revoke access: control which DIDs can reach the keys in a context.