Skip to main content
Version: v0.9.0a2
Integrator

Authentication

4 min readNode operators ยท IntegratorsAccess control

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.

OIDC available

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.
Requirement
Constraint
Notes
Caller permission
admin
Required to call POST /v1/auth/keys.
Allowed new-key permissions
enum
read, write, federate, admin, audit.read.
Raw key length
โ‰ฅ 32 characters
Matching openssl rand -hex 32.
409 response
hash collision
Proposed raw key's hash collides with an existing key โ€” generate a new value.

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.

Permission
Surface
Grants
read
queries
Query facts, list gardens, list members.
write
mutations
Assert/retract facts, create gardens, manage membership.
federate
federation endpoints
Only grantable to static keys (not OIDC-issued).
admin
administrative
Includes minting additional API keys via POST /v1/auth/keys.
audit.read
audit endpoints
Spec-09-Audit-Log.

Multi-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โ€‹

Env var
Default
Description
STIGMEM_AUTH_REQUIRED
true
Require a valid Bearer token on all requests. Set false only for local dev.
STIGMEM_API_KEY_MAX_AGE_DAYS
90
Maximum lifetime for newly registered static API keys. 0 disables max-age enforcement.
STIGMEM_API_KEY_EXPIRING_SOON_DAYS
30
Default lookahead for /v1/auth/keys/expiring-soon.

See alsoโ€‹