Protective Dome for AI Agents — MCP security gateway proxy (Rust)
Thunder Dome
Protective Firewall for AI Agents -- MCP Security Gateway
If you find Thunder Dome useful, consider giving it a star -- it helps others discover the project!
Architecture · Example Policy · Changelog
Thunder Dome sits between your AI agent and any MCP server, intercepting every JSON-RPC message on the wire. It enforces authentication, authorization, rate limiting, and injection detection -- without modifying either side. Think of it as a firewall for AI tool calls.
┌──────────┐ ┌──────────────┐ ┌────────────┐
│ AI Agent │ ──MCP──> │ Thunder Dome │ ──MCP──> │ MCP Server │
│ (Client) │ <──MCP── │ Gateway │ <──MCP── │ (Tools) │
└──────────┘ └──────────────┘ └────────────┘
│
┌────┴────┐
│ Policy │
│ TOML │
└─────────┘
Why Thunder Dome?
AI agents are getting access to powerful tools -- file systems, databases, APIs, code execution. MCP is the protocol connecting them. But there's no security layer in the middle. Thunder Dome fixes that:
- Default-deny policy engine -- TOML rules with time-window conditions and day-of-week filtering, hot-reloadable via file watcher or SIGHUP
- Injection detection -- Regex patterns with Unicode normalization (NFKC, homoglyph transliteration, zero-width stripping), recursive JSON scanning, and heuristic analysis (entropy, Base64, length)
- Schema pinning -- Canonical SHA-256 hashes of tool definitions detect and block rug pulls and tool shadowing; pins persist across restarts at
~/.thunder-dome/schema-pins.json - Hash-chained audit logs -- Tamper-evident NDJSON logging with SHA-256 chain linking, full inbound + outbound coverage
- Token-bucket rate limiting -- Global, per-identity, and per-tool limits with LRU eviction and TTL-based cleanup
- Budget tracking -- Per-identity spend limits with rolling windows, enabled via
--enable-budget,--budget-cap, and--budget-window-secsCLI flags (or TOML config) - Multiple auth methods -- Argon2id-hashed PSKs, API key authentication, with timing-safe verification and automatic credential stripping (OAuth2 scaffolded but not yet functional)
- HTTP+SSE transport -- Feature-gated HTTP transport with Server-Sent Events, session management, restricted CORS, and 256KB body limits -- selectable via
--transport http - Bounded I/O -- 10MB max message size, 5-minute read timeouts, 30-second write timeouts to prevent resource exhaustion
- Graceful shutdown -- SIGTERM/SIGINT signal handling with clean child process termination (SIGTERM -> wait -> SIGKILL fallback), upstream command validation before spawn
- Full method coverage -- All MCP methods are guarded (not just
tools/call), with method-specific resource extraction (tool names, resource URIs, prompt names) for fine-grained policy rules - Outbound scanning -- Server responses are scanned for injection patterns before reaching the AI agent
- CLI toolbox --
validate,verify-log,hash-schema,keygensubcommands plus unified--configfile support - 0.2ms overhead -- Rust performance, single binary, zero config to start
Install
# From crates.io (published as mcpdome)
cargo install mcpdome
# From source
git clone https://github.com/Orellius/thunder-dome.git
cd thunder-dome
cargo build --release
Quick Start
Wrap any stdio MCP server -- zero config, transparent mode:
thunder-dome proxy --upstream "npx -y @modelcontextprotocol/server-filesystem /tmp"
Enable security features progressively:
# Injection detection
thunder-dome proxy --upstream "..." --enable-ward
# Schema pinning (detect tool definition changes)
thunder-dome proxy --upstream "..." --enable-schema-pin
# Rate limiting
thunder-dome proxy --upstream "..." --enable-rate-limit
# Budget tracking (per-identity spend limits)
thunder-dome proxy --upstream "..." --enable-budget --budget-cap 10.0 --budget-window-secs 3600
# Everything with a config file
thunder-dome proxy --upstream "..." --config thunder-dome.toml
# HTTP+SSE transport (instead of stdio)
thunder-dome proxy --upstream "..." --transport http --bind-addr 127.0.0.1:3100
CLI Tools
# Validate a policy file
thunder-dome validate policy.toml
# Verify audit log integrity
thunder-dome verify-log audit.ndjson
# Pre-compute schema pin hashes
thunder-dome hash-schema tools.json
# Generate a pre-shared key
thunder-dome keygen
What It Catches
| Threat | How Thunder Dome Stops It |
|--------|--------------------------|
| Prompt injection in tool args | Ward scans with Unicode normalization, recursive JSON extraction, and heuristic analysis |
| Unicode/encoding evasion | NFKC normalization, homoglyph transliteration, zero-width character stripping |
| Secret leakage (AWS keys, PATs) | Policy deny_regex with recursive argument inspection (catches nested payloads) |
| Tool rug pulls | Schema pinning with canonical JSON hashing blocks critical drift (not just warns) |
| Data exfiltration | Ward detects exfil patterns; outbound scanning catches malicious server responses |
| Unauthorized tool access | Default-deny policy on all MCP methods, not just tool calls |
| Pre-initialize attacks | Session enforcement blocks all requests before authenticated initialize |
| Abuse / runaway agents | Global + per-identity + per-tool rate limiting with LRU eviction |
| Credential leakage | Argon2id PSK hashing, automatic credential stripping before forwarding |
| Tampering with audit trail | SHA-256 hash chain with full inbound + outbound audit coverage |
Policy Example
# Block secret patterns everywhere (highest priority)
[[rules]]
id = "block-secrets"
priority = 1
effect = "deny"
identities = "*"
tools = "*"
arguments = [
{ param = "*", deny_regex = ["AKIA[A-Z0-9]{16}", "ghp_[a-zA-Z0-9]{36}"] },
]
# Developers can read, not delete
[[rules]]
id = "dev-read-tools"
priority = 100
effect = "allow"
identities = { labels = ["role:developer"] }
tools = ["read_file", "grep", "git_status"]
# Write only to safe paths
[[rules]]
id = "dev-write-safe"
priority = 110
effect = "allow"
identities = { labels = ["role:developer"] }
tools = ["write_file"]
arguments = [
{ param = "path", allow_glob = ["/tmp/**"], deny_regex = [".*\\.env$"] },
]
Time-window conditions:
[[rules]]
id = "business-hours-only"
priority = 50
effect = "allow"
identities = "*"
tools = ["write_file", "delete_file"]
conditions = [
{ type = "time_window", after = "09:00", before = "17:00", timezone = "UTC" },
{ type = "day_of_week", days = ["Mon", "Tue", "Wed", "Thu", "Fri"] },
]
See thunder-dome.example.toml for a complete policy file.
Architecture
Rust workspace of focused crates, each with a single responsibility:
thunder-dome (binary)
├── dome-core Shared types & error taxonomy
├── dome-transport MCP wire protocol (stdio, HTTP+SSE)
├── dome-gate Interceptor chain orchestration
├── dome-sentinel Authentication & identity resolution
├── dome-policy TOML policy engine (default-deny)
├── dome-ledger Hash-chained audit logging
├── dome-throttle Token-bucket rate limiting & budget tracking
└── dome-ward Injection detection & schema pinning
Interceptor chain order (inbound):
sentinel → throttle → ward → policy → ledger → upstream server
Ward runs before policy so injection detection cannot be bypassed by overly permissive authorization rules.
See ARCHITECTURE.md for the full deep dive.
Test Suite
245 tests covering every security component:
dome-core 10 tests (message parsing, error mapping, resource/prompt extraction)
dome-gate 23 tests (config defaults, interceptor chain, audit recording, Debug impl, constants)
dome-sentinel 30 tests (PSK auth, API keys, Argon2id, timing-safe verification, chain resolution)
dome-policy 39 tests (rules, priority, recursive args, time-windows, hot-reload, concurrent reads)
dome-throttle 22 tests (token bucket, rate limits, budgets, LRU eviction, global limits, TOCTOU safety)
dome-ward 66 tests (injection patterns, Unicode normalization, recursive scanning, schema pins, persistence, heuristics)
dome-ledger 21 tests (hash chain, tamper detection, file rotation, chain integrity validation)
thunder-dome 22 tests (CLI subcommands, upstream validation, verify-log, hash-schema, keygen)
dome-transport 5 tests (HTTP+SSE: connection, roundtrip, CORS, session cleanup, 404)
integration 7 tests (full binary proxy end-to-end)
cargo test --workspace
Roadmap
| Phase | What Ships | Status | |-------|-----------|--------| | 1 | Transparent stdio proxy, audit logging | Done | | 2 | TOML policy engine, PSK authentication, default-deny | Done | | 3 | Injection detection, schema pinning, rate limiting | Done | | 4 | HTTP+SSE transport, API key auth, time-window policies, config hot-reload, CLI tools | Done | | 5 | OAuth 2.0 (functional), mTLS, dashboard UI, remote policy fetching | Next |
License
Apache-2.0
Part of the Thunder ecosystem