An LLM-as-a-judge HTTP proxy to secure agents in production
See the code
An HTTP/HTTPS proxy that sits between AI agents and external APIs, evaluating every outbound request against security policies before it reaches the internet.
If you run AI agents that call external services — Slack, Gmail, GitHub, or anything else — CrabTrap gives you guardrails. It intercepts every outbound HTTP/HTTPS request, checks it against deterministic rules and an LLM-based policy judge, and either forwards it or blocks it with a reason. Every request and decision is logged to PostgreSQL for a complete audit trail.
CrabTrap runs as a Docker container alongside PostgreSQL. See QUICKSTART.md for the full walkthrough — the short version:
docker compose up -d # start CrabTrap + Postgres
docker compose cp crabtrap:/app/certs/ca.crt ./ca.crt # copy the generated CA cert
# create test-admin admin user and store their web_token in a variable
admin_token=$(docker compose exec -it crabtrap ./gateway create-admin-user test-admin \
| tail -n1 | cut -d" " -f2)
token=$(curl -X POST http://localhost:8081/admin/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${admin_token}" \
-d '{"id": "[email protected]", "is_admin": false}' \
| jq -r '.channels[] | select(.channel_type == "gateway_auth") | .gateway_auth_token')
# test with
curl -x http://${token}:@localhost:8080 \
--cacert ca.crt https://httpbin.org/get
The proxy listens on localhost:8080, the admin UI is at localhost:8081 and you can login to it with the $admin_token.
HTTP_PROXY and HTTPS_PROXY to point at CrabTrap| Section | Key Settings |
|---|---|
proxy | Port (default 8080), timeouts, rate limits, SSRF CIDR allowlist |
tls | CA cert/key paths, certificate cache size (default 10,000) |
approval | Mode: llm or passthrough, timeout (default 30s) |
llm_judge | Provider, model IDs, fallback mode (deny/passthrough), circuit breaker |
database | PostgreSQL connection URL (supports ${DATABASE_URL} expansion) |
audit | Output destination: stderr (default), stdout, or a file path |
alerting | Enable denial notifications, cooldown duration |
log_level | debug, info (default), warn, error |
See config/gateway.yaml.example for the full reference with inline comments.
crabtrap/
├── cmd/gateway/ # Entry point, admin API wiring, web UI serving
├── internal/
│ ├── proxy/ # MITM proxy, TLS cert generation, SSRF protection, rate limiting
│ ├── approval/ # Static rules engine + approval orchestration
│ ├── judge/ # LLM judge prompt construction + response parsing
│ ├── llm/ # LLM adapters, circuit breaker, concurrency control
│ ├── builder/ # Policy agent (agentic loop with tools)
│ ├── eval/ # Eval system (replay audit entries against policies)
│ ├── admin/ # Admin API routes, auth, user/audit stores
│ ├── alerting/ # Denial alerting, notification channels, sender interface
│ ├── llmpolicy/ # Policy storage and versioning
│ ├── audit/ # Structured JSON logging + event dispatch
│ ├── config/ # YAML config loading, validation, defaults
│ ├── db/ # PostgreSQL connection pool + migrations
│ └── notifications/ # SSE channel + event dispatcher
├── pkg/types/ # Shared types (StaticRule, LLMPolicy, AuditEntry, etc.)
├── web/src/ # React + TypeScript admin UI (Vite)
├── config/ # YAML configuration files
├── certs/ # Generated TLS certificates (not committed)
└── scripts/ # Certificate generation, database migrations
CrabTrap ships an optional OpenTelemetry metric surface, exposed in Prometheus scrape format on a dedicated HTTP listener. Disabled by default.
Enable it in config/gateway.yaml:
observability:
metrics:
enabled: true # default: false
listen: "127.0.0.1:9090" # bind address (default loopback)
The metrics listener is separate from the admin and proxy ports. It serves only /metrics and requires no auth — Prometheus scrapers do not need admin credentials. Network exposure is controlled by the listen bind address: the default 127.0.0.1:9090 keeps the surface on loopback so no operator action is required to keep it private. To scrape from another host, change listen to a private interface address (e.g. 10.0.1.42:9090) and reach it from inside the trust boundary.
Minimal Prometheus scrape config (loopback bind, scraper on the same host):
scrape_configs:
- job_name: crabtrap
static_configs:
- targets: ['127.0.0.1:9090']
metrics_path: /metrics
scrape_interval: 15s
The current metric surface includes counters for rate-limit hits and approval decisions, a gauge per LLM provider for circuit-breaker state, histograms for judge and approval latency, and a crabtrap_build_info gauge that lets operators correlate metric anomalies with deployments. See docs/observability.md for the full metric catalog, alert suggestions, and cardinality notes.
make test # lint (go vet + staticcheck) then tests with -race
make fmt # format Go code
make lint # go vet + staticcheck
make build # production binary with embedded web UI
make build-web # rebuild web UI only
See CONTRIBUTING.md for the full development workflow, PR guidelines, and coding conventions.
Releases are automated with GoReleaser via GitHub Actions. Tag a commit on main and push:
git tag v1.2.3
git push origin v1.2.3
This builds cross-platform binaries (linux/darwin, amd64/arm64), creates a GitHub Release with a changelog, and pushes multi-arch Docker images to quay.io/brexhq/crabtrap.
See CONTRIBUTING.md for release notes and commit message conventions.
This project is licensed under the MIT License.
We welcome contributions! Please read CONTRIBUTING.md for guidelines on getting started, running tests, and submitting pull requests.
(top 24 of 25)
598 followers · starred Apr 2026
1,481 followers · starred Apr 2026
371 followers · starred Apr 2026
60 followers · starred Apr 2026
An LLM-as-a-judge HTTP proxy to secure agents in production
See the code
An HTTP/HTTPS proxy that sits between AI agents and external APIs, evaluating every outbound request against security policies before it reaches the internet.
If you run AI agents that call external services — Slack, Gmail, GitHub, or anything else — CrabTrap gives you guardrails. It intercepts every outbound HTTP/HTTPS request, checks it against deterministic rules and an LLM-based policy judge, and either forwards it or blocks it with a reason. Every request and decision is logged to PostgreSQL for a complete audit trail.
CrabTrap runs as a Docker container alongside PostgreSQL. See QUICKSTART.md for the full walkthrough — the short version:
docker compose up -d # start CrabTrap + Postgres
docker compose cp crabtrap:/app/certs/ca.crt ./ca.crt # copy the generated CA cert
# create test-admin admin user and store their web_token in a variable
admin_token=$(docker compose exec -it crabtrap ./gateway create-admin-user test-admin \
| tail -n1 | cut -d" " -f2)
token=$(curl -X POST http://localhost:8081/admin/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${admin_token}" \
-d '{"id": "[email protected]", "is_admin": false}' \
| jq -r '.channels[] | select(.channel_type == "gateway_auth") | .gateway_auth_token')
# test with
curl -x http://${token}:@localhost:8080 \
--cacert ca.crt https://httpbin.org/get
The proxy listens on localhost:8080, the admin UI is at localhost:8081 and you can login to it with the $admin_token.
HTTP_PROXY and HTTPS_PROXY to point at CrabTrap| Section | Key Settings |
|---|---|
proxy | Port (default 8080), timeouts, rate limits, SSRF CIDR allowlist |
tls | CA cert/key paths, certificate cache size (default 10,000) |
approval | Mode: llm or passthrough, timeout (default 30s) |
llm_judge | Provider, model IDs, fallback mode (deny/passthrough), circuit breaker |
database | PostgreSQL connection URL (supports ${DATABASE_URL} expansion) |
audit | Output destination: stderr (default), stdout, or a file path |
alerting | Enable denial notifications, cooldown duration |
log_level | debug, info (default), warn, error |
See config/gateway.yaml.example for the full reference with inline comments.
crabtrap/
├── cmd/gateway/ # Entry point, admin API wiring, web UI serving
├── internal/
│ ├── proxy/ # MITM proxy, TLS cert generation, SSRF protection, rate limiting
│ ├── approval/ # Static rules engine + approval orchestration
│ ├── judge/ # LLM judge prompt construction + response parsing
│ ├── llm/ # LLM adapters, circuit breaker, concurrency control
│ ├── builder/ # Policy agent (agentic loop with tools)
│ ├── eval/ # Eval system (replay audit entries against policies)
│ ├── admin/ # Admin API routes, auth, user/audit stores
│ ├── alerting/ # Denial alerting, notification channels, sender interface
│ ├── llmpolicy/ # Policy storage and versioning
│ ├── audit/ # Structured JSON logging + event dispatch
│ ├── config/ # YAML config loading, validation, defaults
│ ├── db/ # PostgreSQL connection pool + migrations
│ └── notifications/ # SSE channel + event dispatcher
├── pkg/types/ # Shared types (StaticRule, LLMPolicy, AuditEntry, etc.)
├── web/src/ # React + TypeScript admin UI (Vite)
├── config/ # YAML configuration files
├── certs/ # Generated TLS certificates (not committed)
└── scripts/ # Certificate generation, database migrations
CrabTrap ships an optional OpenTelemetry metric surface, exposed in Prometheus scrape format on a dedicated HTTP listener. Disabled by default.
Enable it in config/gateway.yaml:
observability:
metrics:
enabled: true # default: false
listen: "127.0.0.1:9090" # bind address (default loopback)
The metrics listener is separate from the admin and proxy ports. It serves only /metrics and requires no auth — Prometheus scrapers do not need admin credentials. Network exposure is controlled by the listen bind address: the default 127.0.0.1:9090 keeps the surface on loopback so no operator action is required to keep it private. To scrape from another host, change listen to a private interface address (e.g. 10.0.1.42:9090) and reach it from inside the trust boundary.
Minimal Prometheus scrape config (loopback bind, scraper on the same host):
scrape_configs:
- job_name: crabtrap
static_configs:
- targets: ['127.0.0.1:9090']
metrics_path: /metrics
scrape_interval: 15s
The current metric surface includes counters for rate-limit hits and approval decisions, a gauge per LLM provider for circuit-breaker state, histograms for judge and approval latency, and a crabtrap_build_info gauge that lets operators correlate metric anomalies with deployments. See docs/observability.md for the full metric catalog, alert suggestions, and cardinality notes.
make test # lint (go vet + staticcheck) then tests with -race
make fmt # format Go code
make lint # go vet + staticcheck
make build # production binary with embedded web UI
make build-web # rebuild web UI only
See CONTRIBUTING.md for the full development workflow, PR guidelines, and coding conventions.
Releases are automated with GoReleaser via GitHub Actions. Tag a commit on main and push:
git tag v1.2.3
git push origin v1.2.3
This builds cross-platform binaries (linux/darwin, amd64/arm64), creates a GitHub Release with a changelog, and pushes multi-arch Docker images to quay.io/brexhq/crabtrap.
See CONTRIBUTING.md for release notes and commit message conventions.
This project is licensed under the MIT License.
We welcome contributions! Please read CONTRIBUTING.md for guidelines on getting started, running tests, and submitting pull requests.
(top 24 of 25)
598 followers · starred Apr 2026
1,481 followers · starred Apr 2026
371 followers · starred Apr 2026
60 followers · starred Apr 2026