Loading...
Loading...
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.
npx skill4agent add aradotso/trending-skills crabtrap-llm-proxySkill by ara.so — Daily 2026 Skills collection.
Agent → CrabTrap Proxy (:8080) → [Static Rules] → [LLM Judge] → External API
↓
Admin UI (:8081)
↓
PostgreSQL# 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# 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# 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 | errorDATABASE_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# 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 100Authorization: Bearer <admin_token># 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}"# 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"
}'prefixexactglob*denyallow# 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}"# 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}"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")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.jsimport { 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}` },
});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,
)# 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.# 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}'# 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}'# 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# In gateway.yaml — change fallback to passthrough during LLM outages
llm_judge:
fallback_mode: passthrough # default is "deny"curl http://localhost:8081/admin/health \
-H "Authorization: Bearer ${admin_token}"proxy:
ssrf_allowlist:
- "10.10.0.0/16" # your internal API subnetproxy:
rate_limit:
requests_per_second: 200 # increase for high-throughput agents
burst: 400log_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'# Check migration status
docker compose exec crabtrap ./gateway migrate --dry-run
# Force re-run migrations
docker compose exec crabtrap ./gateway migrate --force# 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| 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 |