Rules that run before the action, not after.
A policy is a JSON rule set, not a prompt and not a suggestion. It resolves deterministically, returns one verdict, and produces the evidence for why that verdict was reached. The engine is the enforcement core of AI agent security policy at runtime.
What a rule
actually is.
Match on the agent, the org, and the action. Return a verdict. Everything else is optional.
{
"name": "deny-dotenv-read",
"effect": "forbid",
"actions": ["fs.read"],
"resources": ["**/.env", "**/.env.*"],
"reason": "secrets are brokered, not read",
"remediation": "Request the value as a handle: secrets.get(\"STRIPE_KEY\")"
}
// Glob matching is literal. A pattern never
// escapes the segment it was written for.Four outcomes.
No fifth.
Every evaluation ends in exactly one of these. There is no partial pass and no implicit continue.
| Verdict | What happens | Recorded |
|---|---|---|
| allow | The action proceeds with the tenant scope it was granted. | Decision block written |
| deny | The action does not run. The caller receives the reason, not the payload. | Decision block written |
| hold | Execution pauses for a human decision. The request is preserved; the action is not started. | Decision + resolution written |
| audit_only | The rule records what it observed and does not authorize anything. A separate rule must still allow the action. | Observation written |
audit_only never authorizes. If the only matching rule is audit_only, no rule has allowed the action, and the engine denies it. This is enforced in the evaluator, not left to policy authors.
Three safe actions
that are not safe together.
Most engines judge one call at a time. Some failures only exist in the sequence, and judging calls individually cannot see them at all.
Private data was read
A credential, a .env, a customer record. Recorded with the call that did it and when.
Untrusted content was ingested
A fetched page, a tool result, an MCP response — anything from outside the trust boundary. Set either by injected instructions found in the result, or by provenance alone when nothing obvious was found.
An outbound action is proposed
A request, a write, a deploy. This leg is the call being judged, which is why the sequence can be refused before it completes rather than reported after.
Each one alone is ordinary work. An engine that refuses any of them refuses normal development. Together they are the shape of an exfiltration: something outside the boundary has had the opportunity to steer an agent that is holding secrets and is now talking outward.
LETHAL TRIFECTA all three conditions met in this session
1. sensitive data Read secret-shaped material from ./.env
2026-09-03T10:00:00.000Z
2. untrusted content A tool result contained 2 injected instructions
addressed to the model.
2026-09-03T10:01:00.000Z
3. outbound action Sends data to evil.example
2026-09-03T10:02:00.000Z
Cirvix refuses on capability and opportunity. It does not claim the
sensitive bytes are in this request — that needs data-flow analysis
it deliberately does not do.What this does not claim. Cirvix does not assert that the bytes read in step 1 are inside the request in step 3. Establishing that requires data-flow analysis, and every approximation of it that fits in a synchronous hot path is guesswork presented as proof. The refusal is on capability and opportunity, both of which are established, and the explanation says so rather than overstating.
Strictness follows blast radius. A completed trifecta denies in production and holds for a human everywhere else. The combination is common and benign while a developer is exploring, and a tool that refuses that outright gets uninstalled before it ever protects anything.
Controlling the side effect,
not just the tool call.
A valid agent with a valid credential calling a legitimate MCP tool can still attempt an unauthorized real-world consequence. Cirvix evaluates the effective side effect at machine speed (<1.2ms design target) before releasing execution.
| Consequence Dimension | What the Agent Attempts | Evaluator Boundary | Default Verdict |
|---|---|---|---|
| External State Mutation | Writing to public wikis, forums, external webhooks (e.g. unauthorized coordination channels) | Evaluates destination against mission scope. Reading is allowed; public writing requires explicit grant. | deny |
| Sub-Agent Inheritance | Spawning worker sub-agents with escalated tool sets or broader filesystem access | Monotonic delegation bounds: child agents can never inherit wider authority than parent. | deny |
| Cross-Resource Boundary | Valid database client querying another tenant's schema or 169.254.169.254 | Structural glob and tenant scoping; literal segment matching prevents traversal. | deny |
| Irreversible Consequence | Deleting cloud infrastructure, dropping tables, sending external emails | High-impact side effects trigger approval hold, preserving exact state for human release. | hold |
Machine speed beats attack speed. While enterprise attacks assisted by AI agents compress attack timelines from weeks into under 10 hours, human security consoles arrive too late. Cirvix policy evaluation is designed to run inline inside a <1.2ms budget per decision (design target; measured runs with hardware context on benchmarks), terminating unauthorized side effects before the packet or write leaves the host.
Rule sets that carry
their own provenance.
A pack is one .policy file. The manifest rides on comment lines the parser already skips, so a pack cannot disagree with itself about what it contains.
| Pack | Posture | Rules | For |
|---|---|---|---|
baseline | balanced | 24 | Local and CI. Nothing done a hundred times a day is interrupted. |
filesystem-guard | strict | 20 | Keeps writes and deletes inside the workspace root. |
network-guard | strict | 12 | Refuses cloud metadata; sanitises what comes back from outside. |
zero-exfiltration | strict | 24 | Credentials are brokered as handles, never read. |
The hash covers the rules, not the manifest. Bumping a version or fixing a description must not invalidate a review somebody already did; editing a single deny must. A fork records both the parent id and the parent rule hash — the id alone is a claim, the hash is checkable, including after the parent has moved on.
Deterministic
by construction.
Given the same request and the same rule set, the engine returns the same verdict — on any machine, in any SDK.
Explicit deny wins
A matching deny is terminal. No later rule can lift it, and rule order cannot be used to smuggle an allow past it.
No match means deny
The absence of a rule is not permission. An unmatched action is denied and recorded as unmatched.
Approvals bind to content
An approval is bound to the hash of what was approved. Editing the request un-approves it.
One engine, two runtimes
The Node and Python evaluators are held to a shared conformance fixture, so an SDK and the gateway cannot disagree about a verdict.
Watch a rule flip a verdict
The sandbox runs the same evaluator the gateway runs.
Bring every agent
under control.
Set durable policy, preserve a verifiable record, and give teams a safer way to put intelligent systems to work.