Skip to main content
Version: v0.9.0a2
Integrator

Per-Agent Keypair Registration

4 min readAgent developersTrack C ยท C1

What this page is

Agents register an Ed25519 public key with the node, then sign each fact payload. The node verifies the signature before writing โ€” cryptographically verifiable source provenance on asserted facts.

Keypair attestation is opt-in per assertion.

The existing Bearer-key model is unaffected. Set STIGMEM_ATTESTATION_REQUIRED=true to make it mandatory node-wide.

Stigmem's source field identifies who asserted a fact (agent:settings-sync, oidc:alice@example.com, etc.), but prior to Track C the node accepted any source value the caller provided. C1 closes this gap.

How it worksโ€‹

Agent Stigmem node
โ”‚ โ”‚
โ”‚ POST /v1/auth/agent-keys โ”‚
โ”‚ { public_key, description } โ”€โ”€โ”€โ–บ โ”‚ stores public key against entity_uri
โ”‚ โ”‚
โ”‚ POST /v1/facts โ”‚
โ”‚ { ...fact, attestation: { โ”‚
โ”‚ key_id, signature } } โ”€โ”€โ”€โ–บ โ”‚ verifies sig โ†’ stores attested_key_id
โ”‚ โ”‚
โ”‚ โ”‚ GET /v1/audit returns full trail:
โ”‚ โ”‚ principal โ†’ attested key โ†’ fact

Endpointsโ€‹

POST /v1/auth/agent-keys ยท register a keyโ€‹

Register an Ed25519 public key for the calling entity. Requires write permission.

Request body:

{
"public_key": "<base64url-encoded 32-byte Ed25519 public key>",
"description": "my-coding-assistant prod keypair"
}

description is optional but recommended for audit readability.

Response (201 Created):

{
"id": "uuid",
"entity_uri": "agent:my-service",
"public_key": "base64url...",
"description": "my-coding-assistant prod keypair",
"registered_at": "2026-05-03T01:00:00Z",
"status": "active"
}

Save the id โ€” it is the key_id you include in every attestation token.

GET /v1/auth/agent-keys ยท list my keysโ€‹

Returns all keys (active and revoked) registered by the calling entity.

curl -H 'Authorization: Bearer stgm_...' \
http://localhost:8000/v1/auth/agent-keys | jq .

DELETE /v1/auth/agent-keys/{key_id} ยท revoke a keyโ€‹

Marks the key as revoked. Revoked keys are rejected at attestation verification. Callers may only revoke their own keys.

curl -s -X DELETE \
-H 'Authorization: Bearer stgm_...' \
http://localhost:8000/v1/auth/agent-keys/<key-id>
# โ†’ 204 No Content

Error responses:

Status
When
Meaning
403
wrong owner
The key belongs to a different entity.
404
not found
Key id does not exist.
409
conflict
Already revoked.

Attesting a fact assertionโ€‹

Include an attestation object in the POST /v1/facts body:

{
"entity": "user:alice",
"relation": "memory:context",
"value": { "type": "str", "v": "working on stigmem docs" },
"source": "agent:my-service",
"confidence": 1.0,
"scope": "local",
"attestation": {
"key_id": "<uuid from registration>",
"signature": "<base64url Ed25519 signature>"
}
}

Canonical messageโ€‹

Sign the following UTF-8 string with your private key:

{entity}\n{relation}\n{value.type}\n{encoded_v}\n{source}
Value type
Encoding
Notes
str
verbatim
The string itself.
float
repr(float)
As Python formats it (use the same encoding the node uses when storing).
bool
"true" / "false"
json
compact JSON string

Python example (using the cryptography library):

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
import base64, json

private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()

# Register the public key once
pub_bytes = public_key.public_bytes_raw()
pub_b64 = base64.urlsafe_b64encode(pub_bytes).rstrip(b"=").decode()

# Per-assertion: build canonical message and sign
entity = "user:alice"
relation = "memory:context"
val_type = "str"
val_v = "working on stigmem docs"
source = "agent:my-service"

canonical = f"{entity}\n{relation}\n{val_type}\n{val_v}\n{source}".encode("utf-8")
sig_bytes = private_key.sign(canonical)
sig_b64 = base64.urlsafe_b64encode(sig_bytes).rstrip(b"=").decode()

payload = {
"entity": entity,
"relation": relation,
"value": {"type": val_type, "v": val_v},
"source": source,
"confidence": 1.0,
"scope": "local",
"attestation": {"key_id": "<registered-key-id>", "signature": sig_b64},
}

Making attestation mandatoryโ€‹

Set STIGMEM_ATTESTATION_REQUIRED=true in the node environment. With this flag:

Every assert needs attestation

Every POST /v1/facts must include a valid attestation object.

Unattested requests rejected

Returns 400 Bad Request: "attestation required; register an agent key at POST /v1/auth/agent-keys".

Default is false for backward compatibility with clients that predate Track C.

Migration pathโ€‹

Phase
Posture
How to adopt
Optional (default)
backward-compatible
Existing agents work unchanged. Agents that want attested provenance generate a keypair, register it, and add attestation to each assertion.
Enforced
STIGMEM_ATTESTATION_REQUIRED=true
All agents must register a key before asserting facts. API-key auth continues; only the attestation field becomes required.

See alsoโ€‹