Security guardrail proxy for AI agents in cloud environments.
Intercepts, validates, and enforces policies on tool calls between users and LLM agents. Designed for environments where the user is potentially adversarial (fail-closed by default).
OverviewArchitectureFeaturesHow It ComparesQuick StartConfiguration SummaryAdmin PortalDocumentationProject StructureDevelopmentLicense
Bulwark Gateway sits between your users/applications and your LLM backends (OpenAI, Ollama, vLLM, Azure, etc.). Every request passes through multiple security layers before reaching the backend:
Authentication— JWT/API key validation (fail-closed)** Input Guardrail**— Detects prompt injections, jailbreaks, encoding evasion** IOC Check**— Scans for malicious URLs/IPs/domains from threat intel feeds** Tool Policy**— RBAC enforcement per tenant/agent** Output Filter**— Redacts secrets/PII, detects indirect injection in responses** Rate Limiter**— Per-tenant request throttling via Redis
If any layer detects a threat, the request is blocked immediately (fail-closed).
┌──────────────────────────────────────────────┐
│ Bulwark Gateway │
│ │
User Request ─────► Auth ► Input Guardrail ► IOC Check │
X-Tenant-ID │ │ │
X-Agent-ID │ Agent Registry │
│ (multi-backend) │
│ │ │
│ Forward to backend │
│ │ │
│ Tool Policy ◄── Response ──► Output Filter │
└──────────────┼──────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
Backend A (RAG) Backend B (LLM) Backend C (Agent)
| Component | Port | Description |
|---|---|---|
| Proxy | ||
| 8080 | Security hot path — intercepts all LLM requests | |
| Admin Portal | ||
| 8090 | Web UI for configuration, monitoring, audit logs | |
| Redis | ||
| 6379 | Rate limiting, state, session management | |
| Prometheus | ||
| 9090 | Metrics collection | |
| Grafana | ||
| 3000 | Dashboards and visualization |
Full architecture details:
[docs/ARCHITECTURE.md]
Multi-tenant, multi-agent— Route requests to different backends per tenant/agent** Zero-LLM hot path**— Only regex + Pydantic + cache; p95 < 40ms overhead** 400+ detection patterns**— Prompt injection, jailbreak, encoding evasion, multilingual (ES/ZH/AR)** 4 threat intel feeds**— URLhaus, ThreatFox, AlienVault OTX, AbuseIPDB (+ MISP, OpenCTI, VirusTotal, Shodan)** Streaming tool call buffering**— Tool calls validated BEFORE yielding to client** Self-protection**— Blocks agents from modifying gateway config** Hot-reloadable**— Policies, IOCs, and agent registry reload without restart** Admin Portal**— Full web UI for managing all aspects of the gateway** SIEM integration**— Export to 13 platforms (Wazuh, Splunk, Elastic, QRadar, Datadog, etc.)** Notification channels**— Slack, Teams, Discord, PagerDuty, Opsgenie, Telegram, Email, Google Chat** Kubernetes-native**— Full K8s manifests with NetworkPolicies, HPA, PDB, Pod Security** Audit trail**— Immutable log of all administrative changes** Enterprise secrets**— Vault, AWS SM, Azure KV, GCP SM, CyberArk, SealedSecrets
Most LLM-security tools ship as a library/SDK you embed in your app code, or as a hosted SaaS you send your prompts to. Bulwark Gateway is a self-hosted, fail-closed proxy that sits in front of any OpenAI-compatible backend — no code changes in your app, no prompts leaving your network.
| Capability | Bulwark Gateway | LLM-security SDKs (LLM Guard, Guardrails AI, NeMo, Rebuff) | Hosted SaaS (Lakera, Prompt Security, etc.) |
|---|---|---|---|
| Deployment | |||
| Self-hosted proxy | Library in your app | Vendor cloud (API call) | |
| Data leaves your network | |||
| No | No | Yes (prompts sent to vendor) | |
| Code changes required | |||
| None (drop-in proxy) | Yes (wrap every call) | Yes (SDK/API) | |
| Deterministic hot path | |||
| Yes — regex only, no LLM | Varies (some call LLMs) | Vendor-side (opaque) | |
| Added latency | |||
| p95 < 40 ms (in-cluster) | Varies | Network round-trip to vendor | |
| Multi-tenant / multi-agent routing | |||
| Built-in | No | Vendor-dependent | |
| Tool-call / MCP RBAC | |||
| Yes (per-agent policies) | Rare | Vendor-dependent | |
| Secret / PII output redaction | |||
| Yes | Some | Yes | |
| SIEM export (ECS / Wazuh / Splunk / …) | |||
| Yes (13 platforms) | No | Limited / vendor dashboard | |
Standalone scan API (/v2/scan ) |
|||
| Yes | N/A (is the library) | Yes (is the API) |
Honest scope. Bulwark is a guardrail proxy, not a WAF and not a model-hosting platform. Classic SQLi/XSS on free-form chat input is not reliably matched by the input layer by design — those are enforced at the tool-argument layer where the payload actually reaches a DB/filesystem. See the published gap report for exactly what it does and does not catch. The hot path is pure regex (~446 input + ~150 output patterns), so detection is fast and auditable but not a substitute for a semantic classifier on every edge case — ML scanners are available as an optional layer.
Comparison reflects the common deployment model of each category; individual tools vary. Verify against each vendor's current capabilities.
- Python 3.11+
- Docker 24+
- Kubernetes 1.28+ (production) or Docker Compose (development)
- Redis 7+
Helm is the recommended path for managed clusters (AKS/EKS/GKE). Build and push the images to your registry, then point the chart at them.
<REGISTRY>
is your container registry path, e.g. myacr.azurecr.io
,
123456789012.dkr.ecr.eu-west-1.amazonaws.com
, or ghcr.io/my-org
.
docker build -t <REGISTRY>/bulwark-gateway-proxy:1.0.0 -f Dockerfile .
docker build -t <REGISTRY>/bulwark-gateway-admin:1.0.0 -f docker/Dockerfile.admin .
docker push <REGISTRY>/bulwark-gateway-proxy:1.0.0
docker push <REGISTRY>/bulwark-gateway-admin:1.0.0
kubectl create namespace bulwark-gateway
kubectl create secret docker-registry bulwark-registry \
--docker-server=<REGISTRY> \
--docker-username=<USERNAME> \
--docker-password=<PASSWORD> \
-n bulwark-gateway
helm install bulwark ./helm/bulwark-gateway \
--namespace bulwark-gateway --create-namespace \
--set backend.ip=<YOUR_LLM_BACKEND_IP> \
--set proxy.image.repository=<REGISTRY>/bulwark-gateway-proxy \
--set admin.image.repository=<REGISTRY>/bulwark-gateway-admin \
--set proxy.image.tag=1.0.0 \
--set admin.image.tag=1.0.0 \
--set 'imagePullSecrets[0].name=bulwark-registry' # omit for public registries
kubectl get pods -n bulwark-gateway
helm test bulwark -n bulwark-gateway
See docs/DEPLOYMENT.md for external Redis, TLS/ingress, and
full values.yaml
reference.
For local clusters, k8s/deploy.sh
builds images, loads them into the cluster, and applies the Kustomize manifests in one step:
./secrets/init.sh
./k8s/deploy.sh
kubectl get pods -n bulwark-gateway
./secrets/init.sh
docker compose up -d
kubectl port-forward svc/proxy 8080:8080 -n bulwark-gateway
kubectl port-forward svc/admin 8090:8090 -n bulwark-gateway
curl http://localhost:8080/health
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-H "X-Tenant-ID: default" \
-H "X-Agent-ID: support-bot" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]}'
Full deployment guide:
[docs/DEPLOYMENT.md]
| File | Purpose |
|---|---|
config/agents.yaml |
|
| Tenant → backend mapping, auth config | |
config/policies/*.yaml |
|
| Per-tenant security policies (RBAC) | |
config/notifications.yaml |
|
| Notification channel definitions | |
config/siem/*.yaml |
|
| SIEM platform templates | |
config/iocs.json |
|
| IOC database (auto-updated by feeds) |
| Variable | Description |
|---|---|
BULWARK_JWT_SECRET |
|
JWT signing key (or *_FILE variant) |
|
BULWARK_REDIS_URL |
|
| Redis connection URL | |
BULWARK_REDIS_PASSWORD |
|
Redis auth (or *_FILE variant) |
|
BULWARK_API_KEYS |
|
Comma-separated API keys (or *_FILE ) |
|
BULWARK_WEBHOOK_ALERT_URLS |
|
| Legacy notification webhooks | |
BULWARK_LOG_LEVEL |
|
| Logging level (INFO, DEBUG, etc.) |
All secrets support the ** _FILE pattern* — point an env var to a mounted file:
env:
- name: BULWARK_JWT_SECRET_FILE
value: /run/secrets/jwt-secret
tenants:
example-corp:
backend_url: "${BULWARK_BACKEND_URL:-http://ollama:11434}"
auth_token: "${BACKEND_AUTH_TOKEN}"
allowed_models: ["gpt-4", "gpt-3.5-turbo"]
rate_limit_rpm: 60
tenant_id: example-corp
tools:
allowed:
- web_search
- code_interpreter
blocked:
- file_system
- shell_exec
guardrails:
max_tokens: 4096
block_on_injection: true
Web-based management interface at /
(port 8090).
| Page | Function |
|---|---|
| Dashboard | Real-time metrics, recent blocks, sparklines |
| Policies | CRUD, validation, hot-reload |
| Guardrails | Pattern management, sandbox testing |
| SIEM Export | Transport configuration, connectivity testing |
| Notifications | |
| Alert channel management (Slack, Teams, Email, etc.) | |
| Audit Log | Immutable action history, export |
| Orchestrator | Automated security testing |
| Coverage Matrix | OWASP LLM Top 10 detection map |
| IOCs | Threat intel feed management |
| Tenants | Tenant registration and config |
| Agents | Backend health monitoring |
| Access Control | RBAC roles and permissions |
| Enrichment | Attack replay browser, evasion telemetry, regex-candidate review |
| Skills | Pre-deployment skill/MCP security scanner (SkillSpector) |
| Plugins | Plugin hub — install, enable, security audit |
| Evaluation | Red-team adversarial evaluation runner |
| Discovery | Agent / shadow-AI / MCP discovery and risk assessment |
| Status | System health (Redis, proxy, scanner, telemetry) |
| User | Role | Default Password | Secret Key |
|---|---|---|---|
admin |
|||
| Admin | bulwark-admin |
||
ADMIN_PASSWORD |
|||
security |
|||
| Security | bulwark-security |
||
SECURITY_PASSWORD |
|||
auditor |
|||
| Auditor | bulwark-auditor |
||
AUDITOR_PASSWORD |
Change these immediately in production via K8s secrets.
Detailed guides are in the docs/ directory:
| Document | Description |
|---|---|
| INDEX |
ArchitectureDeploymentOperationsTroubleshootingNotificationsSecurity HardeningAPI Reference
bulwark-gateway/
├── src/ # Proxy source code
│ ├── main.py # FastAPI app entry point
│ ├── models.py # Core data models (SecurityEvent, Verdict)
│ ├── guardrails/ # Detection engines
│ │ ├── input_guardrail.py # Input analysis (400+ patterns)
│ │ ├── output_filter.py # Output redaction
│ │ └── tool_policy.py # RBAC enforcement
│ ├── routes/ # API routes
│ │ ├── proxy.py # Main proxy flow (hot path)
│ │ └── health.py # Health/metrics endpoints
│ ├── telemetry/ # SIEM export + notifications
│ │ ├── exporter.py # Background batch exporter
│ │ ├── notifications.py # Multi-channel alert engine
│ │ ├── queue.py # Non-blocking event queue
│ │ └── transports/ # SIEM output adapters
│ └── services/ # Shared services (IOC, registry)
├── admin/ # Admin portal
│ ├── main.py # Admin FastAPI app
│ ├── routes/ # Admin API routes
│ ├── services/ # Auth, audit, user store
│ └── templates/ # Jinja2 HTML templates (UI)
├── config/ # Configuration
│ ├── agents.yaml # Agent/tenant registry
│ ├── policies/ # Security policy YAML files
│ ├── notifications.yaml # Notification channels
│ └── siem/ # SIEM platform templates
├── docs/ # Detailed documentation
├── k8s/ # Kubernetes manifests
│ ├── base/ # Core resources (deployments, services)
│ ├── secrets/ # Secret generation scripts
│ └── monitoring/ # Prometheus + Grafana
├── tests/ # Test suite (pytest, 1290+ tests)
├── Dockerfile # Proxy image
├── docker-compose.yml # Development environment
└── pyproject.toml # Python project metadata
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
python -m uvicorn src.main:app --reload --port 8080
pytest -v
ruff check src/ tests/
mypy src/
GPL-3.0-or-later — free to self-host, study, modify, and redistribute. See LICENSE.
Contributions are welcome under the Contributor License Agreement, which lets you keep your copyright while keeping the project sustainably licensed.
Licensing details and options:
LICENSING.md
.