Mint, inspect, and retire signing keys
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.
pnminstalled and connected to the VTA.cargo install pnm-cli@0.16.4 --locked --registry crates-ioAdmin 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-1The 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.
--internal mints a key from the system’s random number generator instead of the master seed. Your mnemonic will not recover it, it is excluded from backups, and no surface will ever export it. If this VTA’s storage is lost, the key is gone permanently, along with the ability to produce any signature it alone was trusted for.
Use it only when a key must be unable to leave the VTA even under administrative authority, and keep a derived key as the default everywhere else.
List the keys a context holds
pnm --vta <vta-slug> keys list --context my-appAdd --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-1Quote 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: revokedMint 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 activeThe 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
| Symptom | Likely cause | Fix |
|---|---|---|
pnm keys create returns unauthorized | Minting 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 context | keys 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 ID | The 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 call | The 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 list | Revocation sets the status rather than deleting the record, so the audit trail stays intact. | Add --status active to list only usable keys. |
Next steps
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.