One decision, before the tool call.
Your agent runtime asks Anubis whether a proposed tool call may run. Anubis answers, records the answer, and returns it. That is the entire runtime product - and the reason it is worth trusting is what it does when something goes wrong.
The boundary sits between intention and execution.
Yours
intention
The model has decided it wants to call a tool.
Anubis
evaluate
One synchronous call. One of three answers, recorded before it returns.
Yours
execution
The side effect exists, or it never does.
Before, not during
Enforcement happens at one moment: after the model has decided and before the side effect exists. Anubis does not sandbox, contain or monitor a running tool, and after an allow it observes nothing.
You own the branch
Anubis returns a decision; your code acts on it. An integration that receives a decision and executes anyway is not governed - Anubis cannot detect that and cannot prevent it. Which is why the adapters write the branch for you.
Three answers. The list is closed.
Every governed call ends in one of three words. There is no fourth, no partial allow, and no severity scale to interpret.
allow
Execute the tool exactly once.
Policy permits this call. The decision is already recorded.
approval_required
Do not execute. Stop and escalate.
Held for a human reviewer. It has not been authorised, and nothing resumes it.
block
Do not execute. Do not retry.
The answer is about this request and will be identical. Surface the reason.
block and approval_required are returned as HTTP 200. A 200 means a decision was produced and durably recorded. It does not mean permission. The vocabulary is closed: Anubis will not return a fourth value, and a client that sees one must treat it as a refusal rather than guess.
Everything else is a refusal.
There is exactly one path on which a governed tool runs. Every error, every timeout and every value the contract does not name stops the call.
Refusals : none of these is a decision
| Request timeout | An enforcement gate that did not answer has not authorised anything. |
|---|---|
| Transport, DNS, connection refused | Anubis being unreachable stops the agent. That is the design, not a degradation. |
| 401 authentication failure | The caller is not a known agent. |
| 403 scope failure | The caller is known but this call is outside its scope. |
| 400 or 422 validation, 413 too large | Anubis was never asked a question it could answer. |
| 409 idempotency conflict | The key was reused for a different request. |
| 503 persistence unavailable | The decision could not be recorded. An unrecorded allow is not an allow. |
| 503 policy or configuration unavailable | No decision was reached. |
| A 2xx body that is not a JSON object | Unusable response. |
| A decision value outside the three | Never coerced to the nearest known outcome, and never to allow. |
| Retry budget exhausted | A retry budget is not a fallback. |
The backend cooperates with clients that only read the body: a 401, 403 or 503 carries "decision": "block" at the top level, so an integration that never inspects the status code still refuses. The SDKs go further and raise, so an authentication failure can never be pattern-matched as a policy outcome.
Retries that cannot multiply evidence.
| Connect, DNS, or pool failure | Retry - the request was never dispatched. |
|---|---|
| Read timeout, write error, protocol error | Only with an Idempotency-Key. |
| 429 · 502 · 503 · 504 | Only with an Idempotency-Key. |
| 400 · 401 · 403 · 409 · 413 · 422 · 500 | Do not retry. |
One allow, one execution
An allow authorises the call that was evaluated, once. Do not cache it for a later call, and do not let a downstream step re-derive permission - the side effect is gated once, on the decision.
Retries that cannot multiply evidence
Every accepted evaluation writes an evidence row, so a blind retry after a timeout turns one tool call into several audit entries. Both SDKs send an Idempotency-Key per call; derive your own from <run id>-<step id> when a retry must survive a process restart.
What runtime enforcement is not.
Stated plainly, so nobody has to infer the boundary from what is missing.
It does not execute, cancel or undo your tool call. It never touches your tool, your credentials for that tool, or its side effect.
It does not intercept traffic. There is no transparent, process-wide or network-level interception. Every enforcement point is one you installed.
It does not resume held work. No callback, no resume token, no consume endpoint, no poll API.
It does not rate-limit. A valid credential may be used at any rate.
It does not authenticate your end user. context.user is recorded, not verified.
It does not guarantee a tool ran. Anubis records the decision it made, not the outcome of your execution.
Private access
See it in front of your agent.
Bring one tool call you would not want made twice. We will put a decision in front of it.