Veefore docs

How it works

The twelve stages a protected action passes through, what each one can refuse, and what gets recorded.

Every protected action runs the same pipeline, in the same order, every time. There is no fast path and no cache of a previous decision. The order is not arbitrary: each stage is cheaper than the one after it and narrows what the next stage has to consider, and anything that can refuse does so before a credential is ever brokered.

The stage list is a published enum — ACTION_STAGES — and the stage results come back on every decision in stages[], so what you read here is what you can inspect at runtime.

The twelve-stage enforcement pipelineA vertical list of the twelve pipeline stages in order, with a marked boundary before the credential stage showing where the first external side effect occurs.Every protected action, every timeNo cache, no fast path, no session-scoped grant1REQUESTShape, timestamp, noncecan refuse2AUTHENTICATIONSignature over the agent keycan refuse3IDENTITYGeneration current, not revokedcan refuse4LIFECYCLEAgent and environment operationalcan refuse5CAPABILITYCapability held, tool authorizedcan refuse6DELEGATIONChain resolves and covers thiscan refuse7POLICYStrictest matching rule winscan refuse8RISKSignals policy can already readcan refuse9APPROVALHuman decides, or hascan refuse10CREDENTIALShort-lived lease to the runtimecan refuse11EXECUTIONThe tool actually runscan refuse12RESULTLimits, masking, sealed recordfirst side effect
The twelve stages, in order. Everything above the dashed line can refuse with no effect outside Veefore.
Described in text

Stages one to nine each check one thing and each can refuse, and none of them touches anything outside Veefore. Stage ten brokers a credential, which is the first step with an external side effect. That ordering is what makes a blocked action mean the secret never left — see the table below.

The diagram is generated from ACTION_STAGES, so adding a stage to the system adds it here. The build fails if a new stage arrives without a description.

#The pipeline

# Stage Decides Refuses with
1 REQUEST The request is well-formed and in scope VALIDATION_FAILED, TIMESTAMP_OUT_OF_WINDOW, NONCE_REPLAYED
2 AUTHENTICATION The signature matches a live agent key AGENT_AUTHENTICATION_FAILED, AGENT_TOKEN_EXPIRED
3 IDENTITY The identity is current, not superseded or revoked AGENT_IDENTITY_REVOKED, IDENTITY_GENERATION_STALE, PASSPORT_INVALID
4 LIFECYCLE Agent and environment are operational AGENT_NOT_OPERATIONAL, AGENT_ISOLATED, ENVIRONMENT_PAUSED
5 CAPABILITY The agent holds the capability, and the tool is enabled and authorized CAPABILITY_NOT_GRANTED, TOOL_NOT_AUTHORIZED, TOOL_DISABLED, DESTINATION_BLOCKED
6 DELEGATION The delegation chain resolves and covers this request DELEGATION_INVALID, DELEGATION_EXPIRED, AUTHORITY_EXCEEDED
7 POLICY The active policy's decision ACTION_BLOCKED
8 RISK Risk score and signals, which policy can test VELOCITY_LIMIT_EXCEEDED
9 APPROVAL Whether a human must decide, or has APPROVAL_REQUIRED, APPROVAL_NOT_GRANTED, APPROVAL_EXPIRED
10 CREDENTIAL A scoped, short-lived credential is issued to the runtime INVALID_CREDENTIAL_REFERENCE, CREDENTIAL_SERVICE_UNAVAILABLE
11 EXECUTION The tool actually runs EXECUTION_FAILED, EXECUTION_TIMEOUT
12 RESULT Limits and masking are applied, the record is sealed —

#Why this order

Identity before authority. There is no point resolving what an agent may do until you know which agent it is. Signature verification is stage 2 and nothing downstream runs without it.

Lifecycle before capability. A paused agent is refused by state, not by policy. This matters operationally: pausing an agent must work instantly, and it must not depend on anyone having written a policy that happens to cover it.

Capability before delegation, delegation before policy. Capability is a static grant, delegation is a bounded runtime grant, policy is a conditional rule. Each is strictly narrower and strictly more expensive to evaluate than the one before.

Risk before approval. A policy rule can test risk.score and risk.signals, so the signals have to be computed before the rule that reads them. This is why an unusual request can require approval while the identical request an hour earlier did not.

Credential after every refusal. Stage 10 is the first stage with a side effect outside Veefore. Everything that can say no has already said it. A blocked action never causes a credential to be issued, which is what makes "blocked" mean blocked rather than "blocked, but the secret went out anyway".

Fail closed, at every stage

If a dependency a stage needs is unavailable, the action is refused, not permitted. Identity verification down means IDENTITY_SERVICE_UNAVAILABLE and no protected action proceeds. There is no mode in which an error becomes an allow. See Fail closed.

#What the mode changes

The pipeline is identical in all three modes. The mode only changes what happens to a refusal and whether execution is real.

What each enforcement mode changesA matrix of six behaviours against the three enforcement modes, using ticks, crosses and dots for yes, no and conditional.Same pipeline. Different consequences.OBSERVESIMULATEENFORCEStages 1–9 runA refusal stops the actionCredential brokeredTool actually executesDecision recordedProtects anythingyesnoonly when allowed
The same twelve stages in every mode. Only the consequence of a refusal changes.
Described in text

OBSERVE runs every stage, records what enforcement would have decided, and lets the action execute anyway — so it protects nothing. SIMULATE refuses for real and executes nothing. ENFORCE refuses for real, and brokers a credential and executes only when the action is allowed.

OBSERVE SIMULATE ENFORCE
Stages 1–9 run yes yes yes
A refusal stops the action no yes yes
Credential brokered yes no yes, if allowed
Tool actually executes yes no yes, if allowed
shadow.decision populated yes yes —
Decision recorded yes yes yes

OBSERVE executes everything

In OBSERVE the pipeline computes a refusal, records it in shadow, and then lets the action run anyway. That is the whole point of the mode — you learn what would break before it breaks — but it means OBSERVE provides no protection. Treat it as instrumentation, not as a safety setting.

SIMULATE is the honest middle ground: the full pipeline, no execution, no credential, no side effect anywhere. It is what veefore deploy replays your traffic through during preflight.

#What a decision looks like

json
{
  "actionId": "act_01JAV7YQ4T2K9M3E8H6R5N1XZ0",
  "requestId": "req_01JAV7YQ4T4ZC1P8W2M6B9K3QD",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "mode": "ENFORCE",
  "decision": "REQUIRE_APPROVAL",
  "outcome": "APPROVAL_REQUIRED",
  "allowed": false,
  "reason": "Refunds above ₹1,000 require a human approver.",
  "explanation": {
    "decision": "REQUIRE_APPROVAL",
    "policy": { "id": "pol_01JAV…", "key": "refund-limits", "name": "Refund limits", "version": 4 },
    "matched_rule": { "id": "rule-refund-ceiling", "name": "Approval above 1000" },
    "authority": {
      "valid": true,
      "max_amount": 500000,
      "requested_amount": 120000,
      "capabilities": ["refund.create"],
      "depth": 1,
      "violations": []
    },
    "delegation": { "valid": true, "chain": ["del_01JAV…"], "reason": null },
    "risk": { "level": "MEDIUM", "score": 46, "top_signals": ["Amount is 4.2× this agent's trailing median."] },
    "approval": { "required": true, "reason": "Refunds above ₹1,000 require a human approver." },
    "reason": "Refunds above ₹1,000 require a human approver."
  },
  "approval": {
    "required": true,
    "approvalId": "apr_01JAV…",
    "status": "PENDING",
    "expiresAt": "2026-10-03T12:40:00.000Z",
    "pollAfterMs": 2000
  },
  "risk": { "level": "MEDIUM", "score": 46 },
  "shadow": null,
  "limits": { "appliedAmount": null, "maskedFields": [] },
  "latencyMs": 38,
  "stages": [
    { "stage": "AUTHENTICATION", "outcome": "PASS", "message": "Signature verified.", "latency_ms": 3 },
    { "stage": "POLICY", "outcome": "PASS", "message": "Rule matched: Approval above 1000.", "latency_ms": 2 },
    { "stage": "APPROVAL", "outcome": "PENDING", "message": "Awaiting a human decision.", "latency_ms": 1 }
  ]
}

Three fields do the work:

  • decision is the policy outcome. One of ALLOW, BLOCK, REQUIRE_APPROVAL, STEP_UP_AUTH, LIMIT, MASK.
  • allowed is the single boolean to branch on. It already accounts for mode.
  • explanation names the policy, the version, the deciding rule, the resolved authority, the delegation chain, the top risk signals and any authority violations. This is what makes a refusal answerable rather than mysterious.

explanation.matched_rules carries every rule that matched, each with a per-condition trace of expected value, actual value and result. That is the field to read when a decision is surprising: it shows you not just which rule won, but which conditions in which rules were true.

LIMIT and MASK are the two decisions that let an action through in modified form: limits.appliedAmount is the ceiling that was imposed, and limits.maskedFields lists the response fields that were redacted before the agent saw them.

#The action record

Every run writes an action record, and the record is the full lineage: the request as received, the resolved authority, every rule consulted, every risk signal that fired, the approval and who decided it, the credential lease (by reference, never by value), the downstream response status, and the final outcome.

bash
veefore logs --decision BLOCK --limit 20

or GET /v1/actions/{actionId} for the whole thing. This is the difference between "the agent was blocked" and "the agent was blocked by rule rule-refund-ceiling of policy version 4 because amount was 120000 and the ceiling is 100000, at 12:38:04, and here is the delegation it was acting under".

#Where to go deeper

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