Diff: how-to/issue-capability-token.es
From 1c02ec1 to 1c02ec1
+0 / −0 lines
| Before | After |
|---|---|
| --- | --- |
| schema: foundry-doc-v1 | schema: foundry-doc-v1 |
| title: "How to issue a capability token" | title: "How to issue a capability token" |
| slug: issue-capability-token | slug: issue-capability-token |
| category: how-to | category: how-to |
| content_type: how-to | content_type: how-to |
| type: how-to | type: how-to |
| status: active | status: active |
| last_edited: 2026-06-14 | last_edited: 2026-06-14 |
| editor: pointsav-engineering | editor: pointsav-engineering |
| paired_with: issue-capability-token.es.md | paired_with: issue-capability-token.es.md |
| --- | --- |
| A capability token is a signed credential that grants a device, service, or session access to a specific platform capability. Tokens are Ed25519-signed, self-describing, and time-bounded. Issuing a token means generating a signed credential using the platform's key material and delivering it to the recipient. This guide covers single-token issuance for a device or service. | A capability token is a signed credential that grants a device, service, or session access to a specific platform capability. Tokens are Ed25519-signed, self-describing, and time-bounded. Issuing a token means generating a signed credential using the platform's key material and delivering it to the recipient. This guide covers single-token issuance for a device or service. |
| For the authorization model that capability tokens operate within, see [[machine-based-auth]]. For the full pairing flow that culminates in a token, see [[pair-a-new-device]]. | For the authorization model that capability tokens operate within, see [[machine-based-auth]]. For the full pairing flow that culminates in a token, see [[pair-a-new-device]]. |
| ## Prerequisites | ## Prerequisites |
| - Administrator access to the platform's key issuance service (or the `tool-wallet` binary if issuing via wallet integration) | - Administrator access to the platform's key issuance service (or the `tool-wallet` binary if issuing via wallet integration) |
| - The recipient's public key or verified device identity | - The recipient's public key or verified device identity |
| - Knowledge of which capability scope is being granted (INPUT, USER, READ, or a named service scope) | - Knowledge of which capability scope is being granted (INPUT, USER, READ, or a named service scope) |
| ## Step 1: Identify the capability scope | ## Step 1: Identify the capability scope |
| Capability tokens carry a scope field that restricts what the holder can do. Before issuing, decide the scope: | Capability tokens carry a scope field that restricts what the holder can do. Before issuing, decide the scope: |
| | Scope | Grants | | | Scope | Grants | |
| |---|---| | |---|---| |
| | `READ` | Read-only access to public-facing data; no ledger writes | | | `READ` | Read-only access to public-facing data; no ledger writes | |
| | `USER` | Standard session access; can read entity data, submit queries | | | `USER` | Standard session access; can read entity data, submit queries | |
| | `INPUT` | Full operator access; can write to WORM ledger, trigger F12 Input Machine | | | `INPUT` | Full operator access; can write to WORM ledger, trigger F12 Input Machine | |
| | `service:<name>` | Named service-to-service scope; restricts the token to a specific service API | | | `service:<name>` | Named service-to-service scope; restricts the token to a specific service API | |
| Grant the minimum scope that satisfies the use case. Tokens cannot be narrowed after issuance — issue a new token with a narrower scope if the original is too broad. | Grant the minimum scope that satisfies the use case. Tokens cannot be narrowed after issuance — issue a new token with a narrower scope if the original is too broad. |
| ## Step 2: Obtain the recipient's public key | ## Step 2: Obtain the recipient's public key |
| The recipient generates an Ed25519 keypair on their device and provides the public key. The public key is a 32-byte value, typically encoded as base64 or hex. | The recipient generates an Ed25519 keypair on their device and provides the public key. The public key is a 32-byte value, typically encoded as base64 or hex. |
| For a PPN node, the public key is generated during device initialisation and available via: | For a PPN node, the public key is generated during device initialisation and available via: |
| ``` | ``` |
| service-vm-host --print-pubkey | service-vm-host --print-pubkey |
| ``` | ``` |
| For an application session, the public key is generated by the platform pairing tool at pairing time and stored in the device's local key store. | For an application session, the public key is generated by the platform pairing tool at pairing time and stored in the device's local key store. |
| ## Step 3: Issue the token | ## Step 3: Issue the token |
| Call the token issuance endpoint with the recipient's public key and the intended scope: | Call the token issuance endpoint with the recipient's public key and the intended scope: |
| ``` | ``` |
| curl -X POST http://<issuance-service-host>:<port>/v1/tokens \ | curl -X POST http://<issuance-service-host>:<port>/v1/tokens \ |
| -H "Authorization: Bearer <admin-token>" \ | -H "Authorization: Bearer <admin-token>" \ |
| -H "Content-Type: application/json" \ | -H "Content-Type: application/json" \ |
| -d '{ | -d '{ |
| "subject_pubkey": "<recipient-base64-pubkey>", | "subject_pubkey": "<recipient-base64-pubkey>", |
| "scope": "INPUT", | "scope": "INPUT", |
| "expires_in_seconds": 86400 | "expires_in_seconds": 86400 |
| }' | }' |
| ``` | ``` |
| The service returns a signed token payload. The token is a self-contained JSON structure signed over its fields with the platform's Ed25519 signing key. Copy the full token value — it is not stored server-side after issuance. | The service returns a signed token payload. The token is a self-contained JSON structure signed over its fields with the platform's Ed25519 signing key. Copy the full token value — it is not stored server-side after issuance. |
| ## Step 4: Deliver the token to the recipient | ## Step 4: Deliver the token to the recipient |
| Deliver the signed token payload to the recipient through a trusted channel. The recipient loads the token into their device's local key store: | Deliver the signed token payload to the recipient through a trusted channel. The recipient loads the token into their device's local key store: |
| ``` | ``` |
| os-console --load-token <token-payload> | os-console --load-token <token-payload> |
| ``` | ``` |
| Or for a service: | Or for a service: |
| ``` | ``` |
| export PLATFORM_TOKEN=<token-payload> | export PLATFORM_TOKEN=<token-payload> |
| ``` | ``` |
| The token is verified at every API call by checking the Ed25519 signature against the platform's published public key. | The token is verified at every API call by checking the Ed25519 signature against the platform's published public key. |
| ## Step 5: Verify the token is active | ## Step 5: Verify the token is active |
| After the recipient loads the token, confirm access by checking the console status bar (scope should show the expected tier) or by querying the token verification endpoint: | After the recipient loads the token, confirm access by checking the console status bar (scope should show the expected tier) or by querying the token verification endpoint: |
| ``` | ``` |
| curl -X POST http://<issuance-service-host>:<port>/v1/tokens/verify \ | curl -X POST http://<issuance-service-host>:<port>/v1/tokens/verify \ |
| -H "Content-Type: application/json" \ | -H "Content-Type: application/json" \ |
| -d '{"token": "<token-payload>"}' | -d '{"token": "<token-payload>"}' |
| ``` | ``` |
| A valid token returns the scope, expiry, and subject public key. An invalid signature or expired token returns a 401 response with a rejection reason. | A valid token returns the scope, expiry, and subject public key. An invalid signature or expired token returns a 401 response with a rejection reason. |
| ## Key takeaways | ## Key takeaways |
| - Tokens are Ed25519-signed and self-contained — the issuer does not need to store them after delivery | - Tokens are Ed25519-signed and self-contained — the issuer does not need to store them after delivery |
| - Grant the minimum scope; tokens cannot be narrowed after issuance | - Grant the minimum scope; tokens cannot be narrowed after issuance |
| - Token delivery must use a trusted channel — the token itself proves the holder holds the private key, but delivery determines who receives it | - Token delivery must use a trusted channel — the token itself proves the holder holds the private key, but delivery determines who receives it |
| - The platform's token verification endpoint can confirm a token's validity without a live session | - The platform's token verification endpoint can confirm a token's validity without a live session |
| ## See also | ## See also |
| - [[machine-based-auth]] — the authorization model that capability tokens are part of | - [[machine-based-auth]] — the authorization model that capability tokens are part of |
| - [[pair-a-new-device]] — the full device pairing flow that produces a token as its final step | - [[pair-a-new-device]] — the full device pairing flow that produces a token as its final step |
| - [[rotate-keys]] — how to replace a token and its underlying keypair at expiry or compromise | - [[rotate-keys]] — how to replace a token and its underlying keypair at expiry or compromise |
| - [[crypto-license-sales-architecture]] — the cryptographic license model used for software distribution tokens | - [[crypto-license-sales-architecture]] — the cryptographic license model used for software distribution tokens |
| - [[service-vm-tenant]] — the tenant proxy service that validates tokens on API calls | - [[service-vm-tenant]] — the tenant proxy service that validates tokens on API calls |