Capability Tokens
Every cross-layer action on Gao Internet requires a capability token — a time-bound, domain-scoped permission that the Capability Token Engine validates before forwarding your call to the target layer.
This is the core security mechanism. No capability = no action.
Concept#
Your dApp calls sdk.settlement.createIntent(...)
↓ SDK checks: do you have "settlement:intent:create" capability?
↓ yes ↓ no
Forward to L4 → throw GAO-4001
Token Format#
<layer>:<resource>:<action>
Examples:
intelligence:agent:run
intelligence:agent:deploy
intelligence:sandbox:execute
settlement:intent:create
identity:record:resolve
infrastructure:storage:write
infrastructure:compute:allocate
transport:message:send
developer:connector:gmail:send
developer:connector:slack:send
developer:connector:meshii:message
developer:connector:payii:intent
Request a Token#
const cap = await sdk.developer.capability.request(
'intelligence:agent:run', // capability string
'myapp.gao', // domain scope (usually your app domain)
3600 // TTL in seconds (default 3600 = 1 hour)
)
// cap.token — attach to SDK calls
// cap.hash — for audit lookups
// cap.expires_at — unix timestamp
How to Use#
The SDK auto-attaches tokens when you call methods that require them — as long as you've requested the token first in the current session.
For explicit attachment (payment, high-risk ops):
const cap = await sdk.developer.capability.request('settlement:intent:create', 'myapp.gao')
const intent = await sdk.settlement.createIntent({
amount: '25.00',
currency: 'USDC',
recipient: 'merchant.gao',
idempotency_key: crypto.randomUUID(),
capability: cap.token, // explicit attachment required for payments
})
Token Lifecycle#
request() → issued (stored in Redis, TTL)
↓
validate on each SDK call
↓
expires after TTL → GAO-4001 error
↓
request() again
Tokens are one-time for sensitive ops (payments, deploys) and reusable for read operations within TTL.
Handle Expiry#
import { GaoError } from '@gao/system-sdk'
async function runWithCapability(fn: (cap: CapabilityToken) => Promise<void>) {
let cap = await sdk.developer.capability.request('intelligence:agent:run', 'myapp.gao')
try {
await fn(cap)
} catch (err) {
if (err instanceof GaoError && err.code === 'GAO-4001') {
// Token expired — refresh and retry once
cap = await sdk.developer.capability.request('intelligence:agent:run', 'myapp.gao')
await fn(cap)
} else {
throw err
}
}
}
Validate a Token#
const result = await sdk.developer.capability.validate(cap.token)
// result.valid — boolean
// result.domain — token's domain scope
// result.capability — capability string
// result.expires_at — expiry timestamp
Revoke a Token#
await sdk.developer.capability.revoke(cap.hash)
// Token immediately invalid — cannot be used again
Use this when a user logs out, or when a sensitive operation is cancelled.
Capability by Layer#
| Layer | Common Capabilities |
|---|---|
| L8 Intelligence | intelligence:agent:run, intelligence:agent:deploy, intelligence:sandbox:execute |
| L4 Settlement | settlement:intent:create |
| L5 Identity | identity:record:resolve (usually auto-approved for reads) |
| L7 DePIN | infrastructure:storage:write, infrastructure:compute:allocate |
| L6 Transport | transport:message:send |
| L3 Connectors | developer:connector:gmail:send, developer:connector:slack:send |
Best Practices#
-
Request capability tokens just before use — not on app startup
-
Use short TTLs for sensitive ops (payments: 300s, deploys: 600s)
-
Always handle
GAO-4001with a re-request + single retry -
Never share capability tokens between users or sessions
-
For server-side automation, use service tokens with longer TTL (max 24h)