Veefore docs

Authentication

Three kinds of principal, three mechanisms, and the one that cannot step up.

Four principal types, three authentication mechanisms.

Principal Authenticates with Reaches
USER Password, MFA, or SSO → session Control plane
API_KEY A bearer key Control plane
AGENT A signed Ed25519 assertion → short-lived token Gateway only
SYSTEM Internal Background jobs

principal.type is a policy field, so a rule can apply to automation but not to people, or the reverse.

#Human sessions

http
POST /v1/auth/signup     # creates an organization and its first owner, one transaction
POST /v1/auth/login
POST /v1/auth/mfa/verify
POST /v1/auth/step-up
GET  /v1/auth/session
POST /v1/auth/logout

Login returns a session token, or an MFA challenge to complete first.

bash
curl -s $CP/v1/auth/session -H "Authorization: Bearer $SESSION"

Returns the current session, its scope, and the effective permissions — the authoritative answer to "what can I do", and what the console uses to decide what to render.

Setting Default
SESSION_TTL_SECONDS 43200 (12 hours)
SESSION_IDLE_TIMEOUT_SECONDS 3600
STEP_UP_TTL_SECONDS 300
MAX_LOGIN_ATTEMPTS 10
LOGIN_LOCKOUT_SECONDS 900

Signup creating the organization and its first owner in one transaction means there is no window in which an organization exists with nobody able to administer it.

#Step-up

http
POST /v1/auth/step-up
{ "password": "…" }

Thirteen permissions require a fresh re-authentication at the moment of use, regardless of session age. Without a current step-up they return 403 STEP_UP_REQUIRED.

A twelve-hour session is a reasonable credential for reading dashboards and not for revoking a production agent. → Step-up

#CLI

bash
veefore login

Sign-in happens in the browser, never in the terminal. The CLI opens the console, you sign in there (password, MFA or SSO, exactly as for the console) and approve the terminal. The CLI receives a session token over a loopback redirect protected with PKCE (S256), so the terminal never sees your password.

Situation What happens
A browser on the same machine Opens it and waits for your approval
Over SSH, no display, or --device Prints a link and a short code to enter in any browser
--password Asks for email and password in the terminal, without echo

Only the session token is stored, in ~/.veefore/credentials.json at mode 0600. Revoke it from your profile in the console like any other session.

veefore logout ends the session on the server as well as removing the stored token. Signing out of the console asks whether to also sign out of the Veefore CLI on your devices, ticked by default; that ends every terminal session you approved through browser sign-in. A terminal cannot be told the moment that happens, so the next command there reports that you were signed out, removes the stored token and asks you to sign in again. Sessions signed in with VEEFORE_EMAIL and VEEFORE_PASSWORD, as in CI, are not affected. The CLI connects to Veefore by itself. For a self-hosted Veefore, add --control-plane https://veefore.your-company.com the first time; later commands reuse it.

For CI, where nobody can open a browser, prefer an API key. If a pipeline must sign in as a user, set the credentials in the environment and the CLI uses them without prompting:

bash
VEEFORE_EMAIL=ci@example.com VEEFORE_PASSWORD=… veefore login

#API keys

http
Authorization: Bearer aos_sk_…

For a service or a pipeline. Scoped to a role, optionally to a workspace, with an expiry up to 730 days.

bash
curl -X POST $CP/v1/api-keys \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{ "name": "ci-policy-gate", "role_id": "role_01JAV…", "workspace_id": "wsp_01JAV…", "expires_in_days": 90 }'

Shown exactly once, as `token`

Only a hash is stored and there is no endpoint that returns it again. The field is token; api_key is the metadata record.

An API key cannot step up

Step-up requires a human re-authenticating, and a key has no human. A key holding policy:activate still cannot activate a policy.

Which means CI cannot activate a policy, revoke an agent, rotate a credential or execute an emergency control — regardless of its role. Deliberate: activation moves the authorization boundary for every agent in scope. It is a decision, not a build step.

Pick the smallest role. Never OWNER — it holds organization:delete, and a CI secret is readable by every build step. → API keys

#Agent authentication

Agents authenticate at the gateway, with a signed assertion. No passwords, no API keys, no human credentials.

http
POST /gateway/v1/token
{
  "agent_id": "agt_01JAV…",
  "assertion": "eyJ0eXAiOiJhZ2VudG9zLWFzc2VydGlvbit…",
  "sdk": { "name": "@veefore/sdk", "version": "1.0.0", "runtime": "node20" }
}
json
{
  "data": {
    "token": "aos_at_…",
    "token_type": "Bearer",
    "expires_in": 900,
    "expires_at": "2026-10-03T12:53:04.000Z",
    "agent_id": "agt_01JAV…",
    "environment_id": "env_01JAV…",
    "organization_id": "org_01JAV…"
  }
}

Or POST /gateway/v1/handshake, which also returns the authorized tool list, the capabilities, the enforcement mode and the passport version.

#What is checked

Check Refusal
Signature against the stored public key AGENT_AUTHENTICATION_FAILED
aud matches the published audience, as a string AGENT_AUTHENTICATION_FAILED
Timestamp inside the window TIMESTAMP_OUT_OF_WINDOW
Nonce not seen before NONCE_REPLAYED
Key generation current IDENTITY_GENERATION_STALE
Identity 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 values. This is the most common onboarding failure and it presents as a bare 401.

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

Use the audience from that response verbatim.

#No refresh token

Token lifetime AGENT_TOKEN_TTL_SECONDS, 900
Refresh None. Sign a fresh assertion
On key rotation Every session dies; old assertions get IDENTITY_GENERATION_STALE
On revocation Every session dies; AGENT_IDENTITY_REVOKED

There is no refresh token on purpose

A refresh token is a long-lived bearer credential, and the agent already holds something strictly better — the private key. Re-signing costs a signature.

Treat 401 as "mint a new token and retry once". A second 401 is a real identity problem; stop.

Full wire format: Sign your own assertion.

#Comparison

Session API key Agent token
Principal USER API_KEY AGENT
Obtained by Password / MFA / SSO Created once Signed assertion
Lifetime 12h, 1h idle Up to 730 days 15 minutes
Reaches Control plane Control plane Gateway only
Can step up yes no n/a
Refreshable re-login n/a re-sign
Revocable logout revoke revoke the identity

#The published values

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

The platform public key, its id, and the gateway audience. No authentication, cached for five minutes.

Publishing the audience is safe

It is the value you must sign for. Knowing it grants nothing without the agent's private key, and every agent that can reach the gateway has to know it to talk at all.

Not publishing it would mean clients guessing, and a wrong guess produces a bare 401 with no explanation — which was a real onboarding failure before this endpoint carried it.

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