Trending-skills crabtrap-llm-proxy
LLM-as-a-judge HTTP/HTTPS proxy that secures AI agents by intercepting and evaluating outbound requests against security policies before they reach external APIs.
git clone https://github.com/Aradotso/trending-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/Aradotso/trending-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/crabtrap-llm-proxy" ~/.claude/skills/aradotso-trending-skills-crabtrap-llm-proxy && rm -rf "$T"
skills/crabtrap-llm-proxy/SKILL.mdCrabTrap LLM Proxy
Skill by ara.so — Daily 2026 Skills collection.
CrabTrap is a transparent HTTP/HTTPS forward proxy that sits between AI agents and external APIs. Every outbound request is intercepted, checked against deterministic static rules, then evaluated by an LLM judge against a natural-language security policy. Blocked requests return a 403 with a reason; all decisions are logged to PostgreSQL.
Architecture Overview
Agent → CrabTrap Proxy (:8080) → [Static Rules] → [LLM Judge] → External API ↓ Admin UI (:8081) ↓ PostgreSQL
Key concepts:
- Static rules — deterministic prefix/exact/glob URL matching, checked first (no LLM call)
- LLM judge — natural-language policy evaluated only when no static rule matches
- Audit log — every request, decision, and response stored in PostgreSQL
- SSRF protection — blocks RFC 1918, loopback, link-local, and other private ranges
Installation
Docker Compose (Recommended)
# docker-compose.yml services: crabtrap: image: quay.io/brexhq/crabtrap:latest ports: - "8080:8080" # proxy - "8081:8081" # admin UI environment: - DATABASE_URL=postgres://crabtrap:password@postgres:5432/crabtrap - OPENAI_API_KEY=${OPENAI_API_KEY} volumes: - ./config/gateway.yaml:/app/config/gateway.yaml depends_on: - postgres postgres: image: postgres:16 environment: POSTGRES_USER: crabtrap POSTGRES_PASSWORD: password POSTGRES_DB: crabtrap volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata:
docker compose up -d # Copy the generated CA certificate (needed for HTTPS interception) docker compose cp crabtrap:/app/certs/ca.crt ./ca.crt
Initial Setup
# Create an admin user and capture the token admin_token=$(docker compose exec -it crabtrap ./gateway create-admin-user my-admin \ | tail -n1 | cut -d" " -f2) # Create an agent user (returns a gateway_auth_token) token=$(curl -X POST http://localhost:8081/admin/users \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${admin_token}" \ -d '{"id": "my-agent@example.com", "is_admin": false}' \ | jq -r '.channels[] | select(.channel_type == "gateway_auth") | .gateway_auth_token') echo "Agent proxy token: $token" # Test the proxy curl -x "http://${token}:@localhost:8080" \ --cacert ca.crt \ https://httpbin.org/get
Configuration
Full Configuration Reference
# config/gateway.yaml proxy: port: 8080 read_timeout: 30s write_timeout: 30s idle_timeout: 120s rate_limit: requests_per_second: 50 burst: 100 # CIDR ranges allowed even though they're private (e.g. internal APIs) ssrf_allowlist: - "10.0.0.0/8" # only if you explicitly need internal access tls: ca_cert_path: /app/certs/ca.crt ca_key_path: /app/certs/ca.key cert_cache_size: 10000 # per-host cert cache approval: mode: llm # "llm" or "passthrough" timeout: 30s llm_judge: provider: openai model: gpt-4o fallback_mode: deny # "deny" or "passthrough" when LLM unavailable circuit_breaker: failure_threshold: 5 reset_timeout: 10s database: url: ${DATABASE_URL} # supports env var expansion audit: output: stderr # "stderr", "stdout", or a file path like "/var/log/crabtrap.json" log_level: info # debug | info | warn | error
Environment Variables
DATABASE_URL=postgres://user:password@host:5432/dbname OPENAI_API_KEY=sk-... # if using OpenAI as LLM judge ANTHROPIC_API_KEY=sk-ant-... # if using Anthropic
CLI Commands
# Start the proxy ./gateway serve --config /app/config/gateway.yaml # Create an admin user (outputs web token on last line) ./gateway create-admin-user <username> # Run database migrations ./gateway migrate # Replay audit log entries against a policy (eval mode) ./gateway eval --policy-id <id> --limit 100
Admin API
All admin endpoints require
Authorization: Bearer <admin_token>.
User Management
# List all users curl http://localhost:8081/admin/users \ -H "Authorization: Bearer ${admin_token}" # Create a user curl -X POST http://localhost:8081/admin/users \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${admin_token}" \ -d '{ "id": "agent-prod@example.com", "is_admin": false }' # Delete a user curl -X DELETE http://localhost:8081/admin/users/agent-prod@example.com \ -H "Authorization: Bearer ${admin_token}"
Static Rules
# List static rules for a user curl "http://localhost:8081/admin/users/agent-prod@example.com/rules" \ -H "Authorization: Bearer ${admin_token}" # Create an allow rule (prefix match) curl -X POST "http://localhost:8081/admin/users/agent-prod@example.com/rules" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${admin_token}" \ -d '{ "pattern": "https://api.github.com/repos/myorg/", "pattern_type": "prefix", "action": "allow", "methods": ["GET"], "description": "Allow reading our org repos" }' # Create a deny rule (glob match) curl -X POST "http://localhost:8081/admin/users/agent-prod@example.com/rules" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${admin_token}" \ -d '{ "pattern": "https://api.github.com/repos/*/delete", "pattern_type": "glob", "action": "deny", "description": "Never allow repo deletion" }' # Create an exact match rule curl -X POST "http://localhost:8081/admin/users/agent-prod@example.com/rules" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${admin_token}" \ -d '{ "pattern": "https://slack.com/api/chat.postMessage", "pattern_type": "exact", "action": "allow", "methods": ["POST"], "description": "Allow posting Slack messages" }'
Pattern types:
— URL must start with the patternprefix
— URL must match exactlyexact
— wildcard matching withglob*
Rule priority:
deny rules always take priority over allow rules.
LLM Policies
# Get current policy for a user curl "http://localhost:8081/admin/users/agent-prod@example.com/policy" \ -H "Authorization: Bearer ${admin_token}" # Set/update a policy curl -X PUT "http://localhost:8081/admin/users/agent-prod@example.com/policy" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${admin_token}" \ -d '{ "policy": "This agent assists with GitHub repository management for the myorg organization.\n\nALLOWED:\n- Read operations (GET) on any github.com endpoint\n- Creating issues and pull request comments in myorg repositories\n- Posting messages to the #eng-alerts Slack channel only\n\nDENIED:\n- Any write operations outside the myorg GitHub organization\n- Deleting any resources\n- Accessing credentials, secrets, or environment variables\n- Any requests to non-whitelisted domains" }' # List policy versions curl "http://localhost:8081/admin/users/agent-prod@example.com/policy/versions" \ -H "Authorization: Bearer ${admin_token}"
Audit Log
# Query audit entries curl "http://localhost:8081/admin/audit?limit=50&offset=0" \ -H "Authorization: Bearer ${admin_token}" # Filter by user curl "http://localhost:8081/admin/audit?user_id=agent-prod@example.com&limit=20" \ -H "Authorization: Bearer ${admin_token}" # Filter by decision curl "http://localhost:8081/admin/audit?decision=deny&limit=20" \ -H "Authorization: Bearer ${admin_token}"
Connecting an Agent
Python Agent Example
import os import httpx PROXY_TOKEN = os.environ["CRABTRAP_TOKEN"] PROXY_URL = f"http://{PROXY_TOKEN}:@localhost:8080" CA_CERT_PATH = "./ca.crt" # httpx client with CrabTrap proxy client = httpx.Client( proxies={ "http://": PROXY_URL, "https://": PROXY_URL, }, verify=CA_CERT_PATH, ) # All requests through this client go through CrabTrap response = client.get("https://api.github.com/repos/myorg/myrepo")
Using Environment Variables (Standard Proxy)
export HTTP_PROXY="http://${CRABTRAP_TOKEN}:@localhost:8080" export HTTPS_PROXY="http://${CRABTRAP_TOKEN}:@localhost:8080" export REQUESTS_CA_BUNDLE="./ca.crt" # Python requests export SSL_CERT_FILE="./ca.crt" # general export NODE_EXTRA_CA_CERTS="./ca.crt" # Node.js
Node.js Agent Example
import { HttpsProxyAgent } from 'https-proxy-agent'; import fetch from 'node-fetch'; const proxyToken = process.env.CRABTRAP_TOKEN; const agent = new HttpsProxyAgent(`http://${proxyToken}:@localhost:8080`); // Fetch through CrabTrap const response = await fetch('https://api.github.com/repos/myorg/myrepo', { agent, headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }, });
LangChain / OpenAI SDK
import os import httpx from openai import OpenAI PROXY_TOKEN = os.environ["CRABTRAP_TOKEN"] # Route OpenAI calls through CrabTrap too (optional — lets you audit LLM calls) http_client = httpx.Client( proxies={"https://": f"http://{PROXY_TOKEN}:@localhost:8080"}, verify="./ca.crt", ) client = OpenAI( api_key=os.environ["OPENAI_API_KEY"], http_client=http_client, )
Writing Effective Policies
Policies are natural-language strings evaluated by the LLM judge. Be explicit about allowed and denied behaviors.
# Example policy for a GitHub PR review agent This agent reviews pull requests and posts review comments for the acme-corp GitHub organization. ALLOWED: - GET requests to api.github.com for any repository in the acme-corp organization - POST to https://api.github.com/repos/acme-corp/*/pulls/*/reviews (submit reviews) - POST to https://api.github.com/repos/acme-corp/*/issues/*/comments (post comments) DENIED: - Any requests outside of api.github.com - DELETE or PATCH requests to any endpoint - Accessing /orgs/acme-corp/members or any user/credential endpoints - Requests containing secrets, tokens, or API keys in the body - Requests to change repository settings, branch protection, or webhooks When in doubt, deny the request and explain why.
Policy writing tips:
- Start with what the agent is supposed to do (scope context for the LLM)
- List explicit ALLOWED patterns before DENIED
- Add a catch-all denial at the end
- Deny rules in static rules are evaluated before LLM — use them for hard limits
Policy Builder (Agentic Policy Generation)
CrabTrap can analyze observed traffic and draft a policy automatically:
# Trigger policy builder via admin API curl -X POST "http://localhost:8081/admin/users/agent-prod@example.com/policy/build" \ -H "Authorization: Bearer ${admin_token}" \ -d '{"sample_limit": 200}'
The builder runs an agentic loop, analyzes recent audit entries, and proposes a policy draft for review in the UI.
Eval System
Replay historical audit entries against a policy to measure accuracy before deploying:
# Run eval from CLI ./gateway eval \ --user-id agent-prod@example.com \ --policy-id <version-id> \ --limit 500 # Or via API curl -X POST "http://localhost:8081/admin/users/agent-prod@example.com/policy/eval" \ -H "Authorization: Bearer ${admin_token}" \ -d '{"policy_version_id": "<id>", "sample_limit": 500}'
Eval compares LLM judge decisions against the historical ground truth and reports accuracy, false positive rate, and false negative rate.
Troubleshooting
TLS Certificate Errors
# Agent gets SSL verification error # → Make sure the CA cert is trusted # For curl: curl --cacert ./ca.crt -x "http://${token}:@localhost:8080" https://example.com # For Python requests: export REQUESTS_CA_BUNDLE=./ca.crt # For Node.js: export NODE_EXTRA_CA_CERTS=./ca.crt # Regenerate certs if expired: docker compose exec crabtrap ./gateway gen-certs docker compose cp crabtrap:/app/certs/ca.crt ./ca.crt
All Requests Being Blocked (LLM Unavailable)
# In gateway.yaml — change fallback to passthrough during LLM outages llm_judge: fallback_mode: passthrough # default is "deny"
Or check circuit breaker status:
curl http://localhost:8081/admin/health \ -H "Authorization: Bearer ${admin_token}"
SSRF Blocks Legitimate Internal APIs
proxy: ssrf_allowlist: - "10.10.0.0/16" # your internal API subnet
Rate Limiting
proxy: rate_limit: requests_per_second: 200 # increase for high-throughput agents burst: 400
Debug Logging
log_level: debug audit: output: /var/log/crabtrap-debug.json
# Stream logs docker compose logs -f crabtrap # Query recent denials from audit log curl "http://localhost:8081/admin/audit?decision=deny&limit=10" \ -H "Authorization: Bearer ${admin_token}" | jq '.entries[].reason'
Database Connection Issues
# Check migration status docker compose exec crabtrap ./gateway migrate --dry-run # Force re-run migrations docker compose exec crabtrap ./gateway migrate --force
Development
# Clone and build git clone https://github.com/brexhq/CrabTrap cd CrabTrap make build # production binary (embeds web UI) make build-web # rebuild React UI only make test # lint + race-condition tests make fmt # format Go code make lint # go vet + staticcheck
Project Layout Quick Reference
| Path | Purpose |
|---|---|
| Entry point, admin API wiring |
| MITM proxy, TLS generation, SSRF, rate limiting |
| Static rules engine + orchestration |
| LLM prompt construction + response parsing |
| LLM adapters, circuit breaker |
| Agentic policy builder loop |
| Eval/replay system |
| Admin API routes and auth |
| Shared types (StaticRule, LLMPolicy, AuditEntry) |
| React + TypeScript admin UI |