Veefore docs

Agent identity

Every agent holds its own Ed25519 key pair, proves possession on every request, and can be revoked terminally.

An agent's identity is a key pair. Not an API key, not a name, not a row in a table that a service happens to trust. The agent holds an Ed25519 private key and signs a fresh assertion for every session; Veefore holds only the public half and verifies it.

This matters because almost every other property in the system hangs off it. A capability is granted to an identity. A delegation is issued by an identity. An audit event records which identity acted. If identity is weak, none of the rest means anything.

#What identity is not

The name is not the identity. name is display metadata. Two agents can be called finance-agent. Policy can test agent.name, and it is occasionally useful to, but a rule that depends on a name is a rule that a rename breaks. Authorization resolves on agent.id.

A bearer token is not the identity. The gateway does issue a short-lived session token, but you get one by presenting a signed assertion. A stolen token expires in minutes (AGENT_TOKEN_TTL_SECONDS, 900 by default) and cannot be refreshed without the key.

The SDK is not the identity. The SDK formats and signs the assertion. The key is the identity. You can sign your own — see Sign your own assertion — and the gateway cannot tell the difference, which is exactly the property you want from a security boundary.

#How the key gets created

Two paths, and the first is better.

You generate it. veefore init creates the key pair locally and sends only the public key in POST /v1/agents. The private half never crosses the network, so there is no moment at which Veefore could have logged it, cached it or leaked it. This is the default.

Veefore generates it. If you omit public_key, the registration response includes private_key once, base64 PKCS#8. It is not stored and not recoverable. Use this only when the agent runs somewhere that cannot generate a key, and treat the response as a secret in transit.

The private key is written to `.env.local` at mode 0600

veefore doctor checks the file permissions and warns if the key file is group- or world-readable. It is a real finding: a key readable by another process on the host is a key that process can act with.

#Proving possession

http
POST /gateway/v1/token

The body is a signed assertion over a canonical JSON payload: the agent id, the key id, the audience, a purpose, a nonce and a timestamp. The gateway checks all of it.

Check Refusal
Signature verifies against the stored public key AGENT_AUTHENTICATION_FAILED
aud matches the gateway's published audience, as a string AGENT_AUTHENTICATION_FAILED
Timestamp is inside the acceptance window TIMESTAMP_OUT_OF_WINDOW
Nonce has not been seen before NONCE_REPLAYED
Key generation is current IDENTITY_GENERATION_STALE
Identity is not revoked AGENT_IDENTITY_REVOKED

The audience is compared as a string

http://127.0.0.1:4010 and http://localhost:4010 are the same host and different strings, and the second will be refused if the gateway is configured with the first. This is the single most common onboarding failure, and it presents as a bare 401.

Fetch the authoritative value rather than guessing it:

bash
curl -s https://gateway.veefore.example.com/.well-known/veefore-platform-key

The response carries audience alongside the platform verification key. veefore init does this for you and writes what the gateway actually published.

The nonce check is what makes replay useless. A captured assertion cannot be submitted a second time, even within its validity window, so an attacker who reads one off the wire gains a token they could already see.

#Lifecycle

Seven states, and the distinctions between them are operational, not cosmetic.

State Reversible Means
PROVISIONING — Registered, identity not yet fully issued
ACTIVE — Operating normally
PAUSED yes Operational stop. No new protected actions.
SUSPENDED yes Security stop. Same effect, different intent and audit severity.
REVOKED no Identity invalidated. Terminal.
EXPIRED — Credential lifetime elapsed
DECOMMISSIONED no Retired for good

PAUSED and SUSPENDED both stop protected execution immediately. Keeping them separate matters when someone reads the audit trail in six months: "paused for a deploy" and "suspended because the key may be compromised" are not the same event, and collapsing them loses the only information that mattered.

REVOKED is terminal by design. There is no un-revoke. If you revoke the wrong agent, you register a new one — which is inconvenient, and far less inconvenient than a revocation that can be undone by whoever caused the incident.

bash
veefore agents revoke agt_01ABC --reason "Key suspected compromised"

The CLI shows the full blast radius — sessions that die, delegations that cascade, approvals that are abandoned — before asking for confirmation. It also requires step-up authentication, because agent:revoke is both high impact and step-up gated.

#Rotation

bash
veefore agents rotate-key agt_01ABC

Rotation issues a new key pair and bumps the identity generation. Every outstanding session for that agent dies immediately, and any assertion signed with the old key is refused with IDENTITY_GENERATION_STALE rather than a generic authentication failure — so the agent's logs say what actually happened.

Rotate on a schedule, and rotate immediately on any suspicion. veefore doctor flags identities that have not rotated in a long time.

#What policy can see

Field Type
agent.id string
agent.name string
agent.status PROVISIONING ACTIVE PAUSED SUSPENDED REVOKED EXPIRED DECOMMISSIONED
agent.risk_level LOW MEDIUM HIGH CRITICAL
agent.verification_status UNVERIFIED PENDING VERIFIED FAILED
agent.protocols HTTP MCP A2A
agent.capabilities string list
agent.tags string list

Full list with operators in Policy fields.

Identity, authority, policy and enforcement for autonomous AI agents.