Authentication
What this page covers
API-key auth for client requests, peer-token auth for federation, checking your identity, naming conventions for local agents, and admin operations on keys.
Human users can authenticate via their organisation's IdP instead of a static key โ see OIDC / SSO Integration. Per-agent keypair auth is coming in Track C: Agent Keypairs.
API key auth (client requests)โ
All endpoints except /.well-known/stigmem and /healthz require a
Bearer token:
curl -H 'Authorization: Bearer <your-key>' http://localhost:8000/v1/facts
Auth is enabled by default. To disable for local development, set
STIGMEM_AUTH_REQUIRED=false.
Peer token auth (federation)โ
Federation endpoints (/v1/federation/facts,
/v1/federation/facts/push) require a short-lived Ed25519-signed JWT:
Authorization: Bearer <peer-token>
Peer tokens are generated by the originating node and verified against the well-known public key of the target node. See Spec-05-Federation-Trust replication protocol for the token schema.
Checking your identityโ
GET /v1/me returns the authenticated caller's entity_uri and
permissions:
curl -H 'Authorization: Bearer <your-key>' http://localhost:8000/v1/me
# โ {"entity_uri": "agent:my-service", "permissions": ["read", "write"], "tenant_id": "default"}
Naming local dogfooding agentsโ
Give each concurrent local agent its own key and collision-resistant entity_uri.
Do not reuse a broad identity such as agent:codex across
concurrent Codex, Claude, CI, or adapter sessions: facts, audit
entries, rate-limit buckets, and key listings all become ambiguous.
Recommended pattern:
agent:<tool>-<workspace-or-task>-<YYYYMMDDTHHMMSSZ>
Examples:
agent:codex-app-pr4-inf1-20260516T032000Z
agent:claude-code-docs-pass-20260516T032000Z
agent:ci-release-smoke-20260516T032000Z
For source-attested writes, set each fact's source to the same value
returned by GET /v1/me for the key in use:
{
"source": "agent:codex-app-pr4-inf1-20260516T032000Z"
}
Use a new identity for materially distinct agents or work sessions.
Use descriptions on API keys to capture human context, such as
"Codex app agent for PR 4-INF.1", instead of overloading the
entity_uri.
OIDC-issued keys (human users)โ
Human users can obtain a short-lived key from their IdP via
POST /v1/auth/oidc/exchange. These keys have the same shape as
static keys and work identically at all endpoints. See
OIDC / SSO Integration
for configuration and the full exchange flow.
Listing and revoking your keysโ
# List your active keys
curl -H 'Authorization: Bearer <your-key>' http://localhost:8000/v1/auth/keys | jq .
# Revoke a specific key by ID
curl -s -X DELETE \
-H 'Authorization: Bearer <your-key>' \
http://localhost:8000/v1/auth/keys/<key-id>
Registering an additional static key (admin only)โ
After the one-shot bootstrap (stigmem auth bootstrap-key), additional
static (non-OIDC) keys are minted via POST /v1/auth/keys
authenticated with an admin-scoped key โ typically the bootstrap key.
The node never generates key material.
The caller supplies the raw value; the node hashes and stores it. The
response carries the new key's metadata but never the raw
value โ only the caller knows it. Static keys receive an
expiry automatically: STIGMEM_API_KEY_MAX_AGE_DAYS
defaults to 90, and the node rejects any requested
expires_at beyond that window.
# Caller generates the key material (do this once, store the value securely)
NEW_KEY=$(openssl rand -hex 32)
# Register it (replace <admin-key> with your admin Bearer token)
curl -s -X POST \
-H 'Authorization: Bearer <admin-key>' \
-H 'Content-Type: application/json' \
http://localhost:8000/v1/auth/keys \
-d '{
"raw_key": "'"$NEW_KEY"'",
"entity_uri": "agent:ci-bot",
"permissions": ["read", "write"],
"description": "CI deployment bot"
}' | jq .
# Then use $NEW_KEY as the Bearer token for the new identity.
adminPOST /v1/auth/keys.read, write, federate, admin, audit.read.openssl rand -hex 32.Every successful registration writes an admin_action audit row
capturing the actor (caller's entity_uri), the new key's id, and
its permissions.
Expiring-soon visibilityโ
Admin callers can list active keys nearing expiry:
curl -s \
-H 'Authorization: Bearer <admin-key>' \
'http://localhost:8000/v1/auth/keys/expiring-soon?within_days=30' | jq .
The response includes key metadata, tenant_id, and days_remaining;
it never includes raw key material. Use this endpoint from monitoring
or SIEM jobs to page owners before expiry. The reference node does
not run a background notifier for key_expiring_soon events;
operators should alert on this endpoint or the equivalent database
view until a dedicated scheduler exists.
Entity URI policyโ
Multiple active keys may share one entity_uri. This is intentional:
it supports rotation overlap, devices, sessions, and service-account
redeploys without changing fact provenance. For materially distinct
agents or concurrent local tools, use distinct entity_uri values so
facts, audit rows, rate-limit buckets, and key listings stay
attributable. Use the key description field for human context such
as owner, deployment, or ticket number.
Permissionsโ
Keys carry a permissions array.
readwritefederateadminPOST /v1/auth/keys.audit.readMulti-tenant key provisioningโ
Keys may carry a tenant_id, but default installs resolve every
caller into the single default tenant. Install and enable
stigmem-plugin-multi-tenant before relying on non-default tenant
isolation. Then pass tenant_id to create_api_key when
provisioning a new tenant:
from stigmem_node.auth import create_api_key
key = create_api_key(
"agent:alice",
permissions=["read", "write"],
tenant_id="tenant-a", # omit for single-tenant ("default")
)
See Multi-Tenant Scoping for the full isolation model and migration details. In v0.9.0a10 this is experimental plugin behavior; it is not a default shared-node support claim.
Environment variablesโ
STIGMEM_AUTH_REQUIREDtruefalse only for local dev.STIGMEM_API_KEY_MAX_AGE_DAYS900 disables max-age enforcement.STIGMEM_API_KEY_EXPIRING_SOON_DAYS30/v1/auth/keys/expiring-soon.