Skip to content

Product

One tool call, end to end.

Give AI agents access to your infrastructure without giving them unlimited power. This is what happens between the agent deciding to act and anything actually happening.

The path

Agent → Cirvix → policy → risk → approval → execution

Eight stages, in this order. Secret detection and risk classification run before policy, not after — a rule that says risk >= HIGH needs the risk to exist by the time it is evaluated, or it loads, validates, appears in the list, and silently never fires.
01

Parse & normalize

Whatever spoke it — MCP over stdio, MCP over HTTP, the local control socket, or an SDK wrapper — becomes one envelope. After this point nothing downstream knows which agent framework made the call.

Stops: A rule written once governs every agent that will ever connect.

02

Detect secrets

The arguments are scanned for credential material: AWS keys, GitHub and OpenAI tokens, private keys, database URLs with passwords. Findings never carry the value — only a masked shape and a fingerprint.

Stops: A key the model pasted into a request body, before it reaches the wire.

03

Classify risk

A deterministic classifier — not a model — scores the call LOW, MEDIUM, HIGH, or CRITICAL. Identical inputs give identical scores a year later, which is what makes replay meaningful.

Stops: Rules can say risk >= HIGH instead of enumerating every dangerous tool that will ever exist.

04

Evaluate policy

An ordered rule set. Deny always wins; an approval outranks an allow; a sanitizer outranks an allow; anything unmatched is denied. The same evaluator runs in local dev, CI, and production.

Stops: The call the agent was persuaded to make by a page it read.

05

Check approvals

A held call waits for a named human. The agent is told which one, and given the approval id — so it can say so and move on to other work rather than hanging.

Stops: A production deploy or a database write happening because nobody was asked.

06

Sanitize & record

Secret handles are substituted onto the wire and swapped back on the way home. Instructions embedded in fetched content are stripped before the model reads them. Then the decision is appended to a hash-chained log.

Stops: A credential entering a context window, a transcript, and a trace.

Decisions

Five outcomes, not two

A control plane with only allow and deny forces every interesting case into the wrong one. The two in the middle are where most real policy lives.
ALLOW

Forwarded unchanged.

SANITIZE

Forwarded, but not as written — credential material stripped from the arguments, embedded instructions stripped from the result.

REQUIRE APPROVAL

Suspended for a named human. It may still happen.

DENY

Refused, with a reason and a remediation the agent can act on — so it re-plans instead of retrying.

AUDIT ONLY

Recorded, not enforced. The honest shadow deployment: every record says enforced: false so nobody mistakes it for a protected run.

Policy

Rules you can read out loud

An ordered rule set in a format with no surprises. It compiles to the JSON the engine evaluates — the DSL is a front end, never a second language, so what you write is exactly what the conformance suite checks.
cirvix.policy
deny:  tool = filesystem.read  path = **/.aws/** deny:  network.destination = 169.254.169.254 deny:  tool = network.request  secrets >= 1 require_approval:  tool = shell.exec  risk >= HIGH sanitize:  tool = network.request  targets = result allow:  tool = git.status

Deny always wins

A permit cannot override a forbid, regardless of order or specificity. That is what makes a rule set safe to extend: adding a permissive rule can never silently punch a hole through an existing prohibition.

Unmatched means denied

A call no rule permits is refused. Fail-open is how a control plane becomes decorative the first time a rule file fails to parse.

The policy ships its own tests

cirvix policy test runs the cases declared in the file. A rule set nobody dares change ossifies until it is bypassed rather than updated.

In practice

An agent reads a page. The page tells it to read your credentials.

It believes the page — models cannot reliably tell instructions from data. The call it then makes is where the boundary actually is.
cirvix demo — one sample run
╔════════════════════════════════════════════════════════════╗║ CIRVIX SECURITY INTERCEPT                                  ║╠════════════════════════════════════════════════════════════╣║ Agent:     claude-code                                     ║║ Tool:      filesystem.read                                 ║║ Target:    ~/.aws/credentials                              ║║ Risk:      CRITICAL                                        ║║ Decision:  BLOCKED                                         ║║ Policy:    deny-credential-directories                     ║║ Latency:   1.7ms                                           ║╚════════════════════════════════════════════════════════════╝

Then the same agent, under the same policy, runs git status, reads ./src/app.ts, runs the test suite, and creates a branch — uninterrupted.

That contrast is the product. Cirvix does not disable agents. It makes the dangerous half controllable, and leaves the rest alone.

Run it yourself. Nothing in the demo is scripted — every decision, risk level, policy name, and latency figure is computed live against your own rule set, so the number you see is your machine’s, not ours.

npx @cirvix/agent-control demo

Overhead

The decision path is not where your latency goes

Measured with the harness in the repository — parse through record, excluding the upstream tool round trip and the disk-bound audit append, because including either would describe the network rather than the engine.

These are design targets measured on a developer machine, not a published SLA and not a comparison against other products. The harness prints the platform line alongside every figure, because a latency number with no machine attached is not a measurement.

Reproduce it: node benchmarks/decision.mjs