# Create your first context and signing key

> Create an isolated context on your VTA, mint a signing key inside it, and sign your first payload.

By the end of this guide, your VTA has a context set aside for one application, with a signing key inside it, and you will have used that key to sign a real payload.

## Prerequisites

- A running VTA with pnm connected. See [Deploy VTA appliance](/products/affinidi-elements/vta/get-started/vta-affinidi-portal.md) or [Self-hosted (open source)](/products/affinidi-elements/vta/get-started/self-hosted.md) if you haven’t deployed one yet.

- Your VTA’s slug to hand.

- Affinidi-hosted: the VTA slug printed by pnm bootstrap connect.

- Self-hosted: the slug value printed by pnm setup, which is my-vta in that guide.

- pnm --vta <vta-slug> health shows ✓ on every check.

- Admin access to the VTA.

## Steps

Create a context

A context is a named namespace: everything you create inside it, keys included, stays isolated from every other context on the same VTA.

```bash
pnm --vta  contexts create \
    --id   my-app \
    --name "My App"
```

Replace <vta-slug> with your VTA’s slug, from the Prerequisites above.

Expected output:

```text
VTA: 
  DID: did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io

Context created:
  ID:        my-app
  Name:      My App
  Base Path: m/26'/2'/2'
```

Mint a signing key

Every context starts empty. Mint an Ed25519 key inside it so there’s something to sign with.

```bash
pnm --vta  keys create \
    --key-type ed25519 \
    --context  my-app \
    --label    "my-app-key-1"
```

Expected output:

```text
VTA: 
  DID: did:webvh:QmVE1TQeCtg3aavpTqasqencJpagRr8JKdGRyoZ5Qx6kRp:your-appliance.vta.affinidi.io

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
  Created At:      2026-09-11 19:42:44 +08:00
```

The Key ID is the same value as the Derivation Path: a BIP-32 path, not a short opaque ID. That matters in the next step.

Sign your first payload

pnm has no built-in sign command, but you don’t need one to try signing right now: pnm auth show-token prints your current bearer token, and the VTA’s REST API takes it from there.

A Key ID like m/26'/2'/2'/0' contains / characters, so it cannot go into a URL path unencoded: each / would be read as a path separator, and the request would 404. Percent-encode it first:

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

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

Set KEY_ID to the Key ID from the previous step. For <vta-url>, use your VTA’s REST endpoint:

- Affinidi-hosted: the VTA URL shown on the configuration page in Affinidi Portal.

- Self-hosted: the public_url you set in setup.toml. Where that is an http:// address such as http://localhost:8100, change the https:// in the snippet to match.

payload is your message, base64url-encoded. aGVsbG8 decodes to hello. The signing key never leaves the VTA: only the signature comes back.
Note

This uses your own admin session token, which is fine for a quick check but not how an application should sign in production. See [Sign application payloads without exposing your keys](/products/affinidi-elements/vta/integration-guides/provisioning-app-signing.md) for provisioning a least-privilege application credential instead.

## Confirm

The curl call above returns a JSON body containing a base64url-encoded signature. That signature is proof your key works, without ever exposing the private key itself.

You can also confirm the key exists independently of signing:

```bash
pnm --vta  keys list --context my-app --status active
```

Your new key appears in the list, with Status: active and the my-app context.

## Troubleshooting

| Symptom | Likely cause | Fix |
| context already exists: my-app | A context with this ID already exists on the VTA. | Pick a different --id, or use the existing context instead of creating a new one. |
| unauthorized on pnm contexts create | Your session does not have admin access. | Run pnm auth status to check your session, then contact a VTA admin if you’re not one. |
| pnm keys list returns no keys | The key was minted in a different context, or --context doesn’t match. | Re-run pnm --vta <vta-slug> keys list --context my-app without --status to see every key, active or not. |
| curl returns 401 Unauthorized | The token from pnm auth show-token expired, or wasn’t captured correctly. | Re-run TOKEN=$(pnm --vta <vta-slug> auth show-token) immediately before the curl call; tokens are short-lived. |
| curl returns 404 Not Found on the key | KEY_ID_ENCODED wasn’t percent-encoded, so the / characters split the URL into the wrong path, or KEY_ID doesn’t match the Key ID printed when you minted the key. | Confirm KEY_ID_ENCODED has no raw / characters left in it, and that KEY_ID matches Step 2’s output exactly. |
| curl returns {"error":"unauthorized: invalid token: InvalidSignature"} | The <vta-url> in the curl command belongs to a different VTA than the one --vta <vta-slug> fetched the token from. Each VTA signs its own tokens; one VTA’s token never verifies against another. | Confirm --vta <vta-slug> on the pnm auth show-token call and <vta-url> in the curl command point at the same VTA. |

## Next steps

- [Sign application payloads without exposing your keys](/products/affinidi-elements/vta/integration-guides/provisioning-app-signing.md): Provision an application credential for this context and start signing with the key you just minted.

- [Keys and contexts](/products/affinidi-elements/vta/concepts/keys-and-contexts.md): Understand how context isolation and key derivation work underneath this guide.

- [Mint, inspect, and retire signing keys](/products/affinidi-elements/vta/vta-management/manage-signing-keys.md): list what a context holds, relabel a key, and take one out of service.

- [Glossary](/products/affinidi-elements/vta/get-started/glossary.md): decode the terms you see in pnm output, from VTA slug to PCR0.
