Fail closed
When Veefore cannot evaluate, it refuses. There is no flag that changes this, and that is the point.
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.
`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
tsif (error instanceof VeeforeConnectionError || status === 503) {
// Retry with exponential backoff and full jitter.
// The work is deferred, not lost.
return scheduleRetry(request);
}
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
bashnpm 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.