---
title: Fail closed
description: When Veefore cannot evaluate, it refuses. There is no flag that changes this, and that is the point.
---

:::security The rule
If a dependency that enforcement relies on is unavailable, protected actions are
**refused**, not permitted.

There is no mode, no flag, no environment variable and no header that turns this
into fail-open. The behaviour is asserted by `tests/chaos` — 13 cases covering
dependency loss and recovery.
:::

## Why this is not negotiable

An authorization system that permits everything when it cannot evaluate is worse
than having no authorization system, because you believe you have one.

Consider what fail-open would mean in practice. An attacker who wants an agent
to perform an action policy would refuse has a much easier job than defeating
the policy: make the policy service unreachable. Every denial-of-service becomes
a privilege escalation, and the cheapest attack on the system becomes an attack
on its *availability* rather than on its *correctness*.

So the trade is explicit: Veefore being down means your agents stop performing
protected actions. That is a real cost, and it is the smaller one.

## What each refusal means

| Code | Status | Dependency |
| --- | --- | --- |
| `IDENTITY_SERVICE_UNAVAILABLE` | 503 | Identity verification is down; protected actions are blocked |
| `POLICY_SERVICE_UNAVAILABLE` | 503 | Policy evaluation is down; protected actions are blocked |
| `CREDENTIAL_SERVICE_UNAVAILABLE` | 503 | The broker is down; dependent actions cannot execute |
| `APPROVAL_SERVICE_UNAVAILABLE` | 503 | Approval-gated actions cannot execute |
| `AUDIT_SERVICE_UNAVAILABLE` | 503 | The audit pipeline is unavailable |
| `DEPENDENCY_UNAVAILABLE` | 503 | A required dependency is unavailable |

Each message says plainly that actions are blocked, because the agent's logs
should state the consequence and not just the cause.

:::danger `AUDIT_SERVICE_UNAVAILABLE` is the most serious
The others mean work is not happening, which is recoverable — the work waits.

Audit failure means **evidence is being lost right now**, and lost evidence does
not come back when the service recovers. Alert on
`veefore_audit_pipeline_errors_total` with zero tolerance.
:::

## Where it applies, specifically

**Identity.** No signature verification, no action. An agent cannot be
authenticated on a best-effort basis.

**Policy.** No evaluation, no action. There is no "assume allow" default, and
the evaluator never invents one — the caller supplies the no-policy-bound
default and the gateway's choice is conservative: `BLOCK` in `ENFORCE` mode for
a dangerous capability.

**Delegation.** A chain that cannot be resolved is `DELEGATION_INVALID`, not an
assumed grant.

**Approval.** An approval that cannot be created or checked means the action
does not proceed.

**Credential.** No lease, no execution. The tool is not called without
authentication.

**Egress.** A circuit breaker opens after five consecutive failures per tool, so
one broken downstream cannot consume the gateway's capacity. Open means refused,
not retried indefinitely.

## It also applies to smaller things

The same instinct appears in places that are not dependency outages:

**Unknown policy field.** A validation error at authoring time, not an
`undefined` at decision time. The field set is closed.

**Unresolvable comparison.** Evaluates to `false` rather than throwing. The
evaluator is total — which is itself a fail-safe property, because an exception
mid-evaluation would be an outcome nobody specified.

**Delegation that exceeds its parent.** Refused with `DELEGATION_ESCALATION`,
not silently clamped. Silent clamping means the caller believes it has authority
it does not have.

**Credential material under an unrecognised key.** Refused at registration with
`INVALID_CREDENTIAL_REFERENCE`. This one used to succeed and fail two layers
away, which is the opposite of failing closed — it failed *late*.

**MCP tool discovery.** A newly discovered tool is unauthorized. Discovery does
not grant.

**A2A connection.** Starts `PENDING` and unusable until its passport verifies.

**Webhook delivery with egress off.** Recorded as failed with an explicit
reason, never reported as delivered. A delivery claimed as successful when
nothing was sent would be a lie in the one record you would later rely on.

## How your agents should behave

```ts
if (error instanceof VeeforeConnectionError || status === 503) {
  // Retry with exponential backoff and full jitter.
  // The work is deferred, not lost.
  return scheduleRetry(request);
}
```

:::danger Do not build a bypass
A code path that proceeds when Veefore cannot evaluate is a code path an
attacker will find and trigger deliberately — and it is a much easier target
than the policy it circumvents.

If the work genuinely cannot wait, the answer is more Veefore instances, not a
fallback that skips authorization.
:::

The right shape is a queue. Protected actions that cannot be authorized right
now get deferred and retried. Work that does not need authorization — which is
usually most of what an agent does — continues unaffected.

## What this costs you, honestly

**Veefore becomes a dependency on your critical path** for protected actions.
Run more than one instance of each plane; both are stateless, with session state
and the job queue in the database.

**A database outage stops protected actions.** Readiness checks are honest about
it, so load balancers pull broken instances rather than letting them serve
errors.

**Recovery produces a risk spike.** Velocity and history baselines are disrupted,
so agents catching up on queued work trip `VELOCITY_ANOMALY` and `TIME_ANOMALY`
and generate approval requests. Expect it; do not retune weights for a one-off.

## Testing it

```bash
npm run test:chaos
npm run test:dr
```

Thirteen cases asserting that dependency loss fails closed and that recovery
restores enforcement. Both are stages of `npm run verify`.

Worth running yourself rather than taking on trust — the suite is the reason this
page can state the behaviour as fact rather than as intent.

## Related

:::cards

:::card Dependency outage | /operate/runbook-dependency-outage
What to do when this fires.
:::

:::card Errors and retries | /build/errors-and-retries
How agents should handle it.
:::

:::card The boundary | /secure/boundary
Why the refusal is authoritative.
:::

:::card Threat model | /secure/threat-model
Availability attacks as privilege escalation.
:::

:::
