Spectron uses API keys for all authenticated operations. Keys carry a scope floor that restricts what memory they can read and write. A key can never access memory outside its scope floor, and it can only mint sub-keys with a scope floor that is equal to or narrower than its own.
Creating keys
Keys are created via the management API (operator), Cloud-brokered access tokens, or member self-service. Each data-plane key is bound to a principal and carries grant regions per verb.
Management mint (requires management key):
See Management API for the full control-plane surface.
Each key requires:
Principal binding — the principal whose grants the key inherits (optional body
grantsonly attenuate)Name — a human-readable label unique within the Context
Expiry (optional) — via
?ttl_seconds=on mint or rotate
The command prints the key id and plaintext secret once — store the secret immediately. See Management API for the HTTP equivalent.
The plaintext key is returned only at creation time. Spectron stores only the hash. If the value is lost, delete the key and create a new one.
Principal types
| Principal | Description |
|---|---|
agent | An individual agent. Reads and writes memory within its scope floor. |
supervisor | Reads memory across all agents within its org scope. Typically used for reflection and cross-agent queries. |
management | Full administrative access to the context: key management, configuration, lifecycle operations. |
Self-service keys and Cloud brokerage
Two flows share the same key substrate on the Spectron data plane:
SurrealDB Cloud (Surrealist)
End users call the SurrealDB Cloud API, not Spectron's management API directly. Cloud holds the root management key and brokers on your behalf.
| Actor | Cloud API entry | Spectron effect |
|---|---|---|
| Member | POST /v1/organizations/{org}/spectron_contexts/{ctx}/access_tokens | Short-lived token for their own principal (external_id = surreal-cloud:<user_id>) |
| Admin | POST …/spectron_contexts/{ctx}/api_keys | Long-lived sk-ctx-… key (what Surrealist API keys creates) |
| Admin | POST …/spectron_contexts/{ctx}/scoped_keys, …/principals, …/scopes, … | Proxied management operations |
Members cannot call admin proxy routes (403). ttl_seconds on access tokens is required; re-broker before expiry — there are no refresh tokens.
For Surrealist and application integrations, use API keys and your context host. Members obtain brokered tokens through the Cloud API entry above.
Self-hosted operator broker
Operators with a Spectron management key can broker directly:
Self-service (data-plane API) — once a member holds a brokered or admin-minted key, they manage their own keys without admin access:
GET /me returns principal identity, grants, effective grants, and delegation state. POST /keys mints for the caller’s own principal; optional grants only attenuate (narrow) — widening returns 400. Self-service mutating routes reject unbound keys and delegated callers.
Operators can disable self-mint with config.allow_self_service_keys: false or cap TTLs with config.max_token_ttl_seconds (see Configuration).
There is no separate key:create grant verb — self-mint is safe by construction because attenuation can only narrow access, and gating it would be circular (you need a key to mint a key).
Key storage
Spectron stores the HMAC-SHA256 hash of each key in SurrealDB. The plaintext is never persisted. Authentication on each request hashes the presented key and compares it to the stored hash.
Because keys are hashed on storage, you cannot retrieve a key's value after creation. Treat key creation responses like you would treat newly generated secrets: copy the value immediately to a secrets manager or environment variable.
Key rotation
Spectron supports in-place rotation — swap the secret while keeping the same key id, principal binding, and grants:
The response includes the new secret (shown once). The old secret stops validating immediately; there is no grace-period overlap.
| Query param | Behaviour |
|---|---|
ttl_seconds omitted | Inherit the key’s current expiry |
ttl_seconds=N | Reset expiry to now + N seconds |
Manual rotation (create + delete)
To change principal binding or grants, mint a new key and retire the old one:
Create a new key with the desired scope floor and principal type.
Update all callers to use the new key.
Verify the old key is no longer in use (check
last_used_at).Delete the old key.
Deletion is immediate. Any in-flight requests using the deleted key will fail on their next API call.
Delegation
A key holder can mint sub-keys with a scope floor that is a superset of (or equal to) their own scope floor. This is the delegation invariant: a delegated key can never be broader than the key that created it.
Example: a planner agent key with {org: "acme", agent: "planner"} can mint a sub-key for a specific tool:
The created_by field records which key minted this sub-key, forming an auditable delegation chain.
Invalid delegation – the following would be rejected because the sub-key's floor is broader than the parent:
Use cases for delegation
Per-tool auditing: mint a sub-key per tool call so
last_used_attracks tool-level activitySession-bounded tokens: create a key that expires when a session ends
Temporary access: grant a contractor key scoped to a single agent for a time-bounded period
Principle of least privilege: give each component exactly the scope it needs
Expiry
Keys accept an expires_at field in RFC 3339 format. After this timestamp, the key is rejected on every request with a 401 Unauthorized response.
Expired keys are not automatically deleted. They remain in the key list with status: "expired" and can be inspected for audit purposes. Delete them when no longer needed.
When creating keys with spectronctl, you can pass --expires-in <seconds> instead of an absolute timestamp; the management API stores the resulting expiry as valid_until.
Acting on behalf of another principal
Some deployments need one agent to read or write memory for another principal — for example a supervisor orchestrator calling tools on a user’s behalf. Pass the target principal on each request:
Rules:
Depth 1 only — you cannot chain delegation headers.
Intersected grants — effective authority is the intersection of the caller’s key, the caller’s grants, and the target principal’s grants. Delegation narrows access; it never widens it.
Recorded on traces — reconciliation traces include both the acting principal and the
on_behalf_oftarget when present.
See REST API for the header on end-user routes.
Revocation
To immediately invalidate a key, delete it:
Deletion is hard and immediate. If you want to preserve the key record for audit purposes without allowing further use, set revoked_at instead:
A revoked key has revoked_at set to the current timestamp and status: "revoked". It cannot be unreveoked. Use deletion when the record is no longer needed; use revocation when you need the audit trail.
Audit fields
Every key carries the following audit fields:
| Field | Description |
|---|---|
id | Unique key identifier |
name | Human-readable label |
principal | Principal type |
scope_floor | The scope floor enforced on all requests using this key |
created_at | When the key was created |
created_by | Key ID of the parent key that minted this key (if delegated) |
last_used_at | Timestamp of the most recent authenticated request |
expires_at | Expiry timestamp (null if no expiry) |
revoked_at | Revocation timestamp (null if active) |
status | active, expired, or revoked |
The created_by chain lets you audit delegation trees: given any key, you can walk created_by back to the root management key.