AI-native LLM security gateway / enforcer for applications and agentic systems — policy-as-code (CEL), human approval for high-risk tool calls, and tamper-evident Ed25519 audit trails between your app and any LLM provider.
AEGIS sits between your application and any LLM provider, enforcing defense-in-depth against prompt injection, jailbreaks, data exfiltration, tool/MCP abuse, and supply-chain tampering — with full tamper-evident audit trails.
Live site: defenseaegis.org — gateway-first landing; hosted advisory Q&A is an applied example on the same stack, not the headline product.
# Try the gateway locally (mock model — no paid API key required)
./scripts/demo.shdefenseaegis.org also hosts an advisory infrastructure Q&A surface (inventory + plain-language answers + curated CVE context; optional paid walkthroughs). That path is secondary — built on the same policy and audit primitives. Autonomous action-taking / “digital employee” marketing stays shelved until a real competence bar passes.
| Surface | URL | Audience |
|---|---|---|
| Live site (gateway landing + Q&A example) | defenseaegis.org | Operators evaluating the gateway; optional Q&A trial |
| CVE checklist (legacy SEO) | /guides/smb-cve-exposure-checklist | Small-business CVE readers — not the gateway buyer |
| Open-source platform | this repo | Developers integrating the gateway |
Implementation: smb-copilot/ (FastAPI backend), smb-portal/ (React customer UI). See each service README for ports, env vars, and smoke tests.
- Defense-in-depth, not a single filter — input defense, policy-as-code (CEL), output defense, and agent-gate tool authorization in one pipeline
- OpenAI-compatible gateway — drop-in
base_urlfor existing SDKs - Human approval for high-risk tools — agent-gate blocks irreversible actions until a reviewer allows them
- Tamper-evident audit — Ed25519-signed receipts in Postgres
- Built for operators — Docker Compose deploy, dashboard, continuous red-team harness
Application → [SDK / Reverse Proxy] → Gateway (Go)
├── Input Defense (Python)
├── Policy Engine (Go + CEL)
├── Model Router (Go)
├── Output Defense (Python)
├── Agent Gate (Go)
└── Audit (Go + Postgres)
See ARCHITECTURE.md for the full system design.
Phase 2 evidence: Adaptive red-team campaigns and detector ablation are summarized in RESULTS.md. Report R1 BR and Adapt BR separately (see the campaign methodology note) — not a single blended “overall bypass” headline. See COMPARISON.md for how AEGIS differs from NeMo Guardrails, Guardrails AI, and LLM Guard, and ARCHITECTURE.md for which layers are load-bearing for content vs tool-misuse outcomes.
Governed agent loop: the harness/ package is a shipped starter loop + 7-tool library that will not execute a tool without an agent-gate allow decision — see harness/README.md.
| Path | Language | Purpose | Stage |
|---|---|---|---|
shared/ |
Protobuf + JSON Schema | Cross-service schemas and codegen | 0 |
gateway/ |
Go | HTTP orchestration (defended chat pipeline) | H4 |
input-defense/ |
Python | Input-side detectors + fusion | 2 |
policy-engine/ |
Go | CEL policy evaluation | 3 |
model-router/ |
Go | Provider-agnostic LLM routing | 4 |
output-defense/ |
Python | Output-side detectors + LLM judge | 5 |
agent-gate/ |
Go | Tool/MCP permission + taint tracking | 6 |
redteam/ |
Python | Continuous adversarial testing | 7 |
audit/ |
Go | Ed25519-signed audit receipts | 8 |
dashboard/ |
React + TS | Operations UI | 9 |
smb-copilot/ |
Python | SMB tenant onboarding, Q&A, usage | — |
smb-portal/ |
React + TS | Customer-facing SMB Copilot UI | — |
corp-orchestrator/ |
Python | Multi-department agent corporation mesh (Phase 12) | — |
sdk/ |
Python + TS | Drop-in SDK wrappers | 10 |
examples/ |
Mixed | Reference integrations | 11 |
harness/ |
Python | Governed multi-step agent loop + 7-tool starter library, all 4 risk tiers (operator-platform phases 1-2) | 12 |
deploy/ |
Helm + SQL | Production deployment | 0 |
# 1. Generate credentials — there is no static default password or API key
# anywhere in this repo. This creates .env (from .env.example) and fills
# in a random dashboard password and gateway API key.
chmod +x scripts/generate-credentials.sh
./scripts/generate-credentials.sh
# Edit .env further if you want to set LLM provider keys, ports, etc.
# 2. Install dev dependencies and generate protobuf code
chmod +x scripts/*.sh
./scripts/dev-setup.sh
# 3. Start the full local stack
docker compose up -d --build
# 4. Run smoke tests
make test-integrationUse docker compose --env-file .env up -d if your shell does not auto-load .env.
If you skip step 1, the gateway and dashboard each generate a one-time
random credential at container startup and print it once to their logs
(docker compose logs gateway / docker compose logs dashboard) — it
changes on every restart until you persist one via .env. Default username
admin; the dashboard password is generated by ./scripts/generate-credentials.sh
or emitted once on container startup — there is no static changeme default.
Every request to the gateway other than /health and /ready requires an
API key, sent as Authorization: Bearer <key> (OpenAI-SDK compatible) or
X-API-Key: <key>:
curl -H "Authorization: Bearer $AEGIS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"mock-model","messages":[{"role":"user","content":"Hello"}]}' \
http://localhost:8080/v1/chat/completionsOr skip straight to seeing it work: ./scripts/demo.sh generates
credentials if needed, starts the gateway, and sends a benign request and a
prompt-injection attempt side by side so you can see AEGIS catch the
difference — no test fixtures, no reading code first.
| Service | Port | Health | Docs |
|---|---|---|---|
| Gateway | 8080 | /health |
requires Authorization: Bearer <key> on all routes except /health and /ready — see Quick start |
| Policy Engine | 8081 | /health |
policy-engine/README.md |
| Model Router | 8082 | /health |
model-router/README.md |
| Agent Gate | 8083 | /health |
agent-gate/README.md |
| Audit | 8084 | /health |
audit/README.md |
| Input Defense | 8090 | /health |
input-defense/README.md |
| Output Defense | 8091 | /health |
output-defense/README.md |
| Red Team | 8092 | /health |
redteam/README.md |
| Dashboard | 3000 | / (UI) |
dashboard/README.md |
| SMB Copilot | 8093 | /healthz |
smb-copilot/README.md |
| Corp Orchestrator | 8094 | /healthz |
corp-orchestrator/README.md |
| SMB Portal | 3001 | / (UI) |
smb-portal/README.md |
| SDK Proxy (gateway) | 8080 | /v1/chat/completions |
sdk/README.md |
make proto # Lint + generate from shared/proto
make lint # Go + Python linters
make test # Unit tests (Go + Python)
make test-integration # Docker smoke tests
make chaos # Fault-injection tests against FAILURE_MODES.md's contract
make backup # Encrypted postgres snapshot (see DR-RUNBOOK.md)
make bench # Load test the defended chat-completion pipeline (stub backends)docker run --rm -v "$(pwd)/model-router:/app" -w /app golang:1.22-alpine go test ./...
docker run --rm -v "$(pwd)/policy-engine:/app" -w /app golang:1.22-alpine go test ./...cd input-defense && pip install -e '.[dev]' && pytest
cd output-defense && pip install -e '.[dev]' && pytestTwo load-testing scripts, both built on vegeta:
make bench/scripts/benchmark.sh-- CI-safe profile. Runs against whatever stack is already up (CI runs it with stub detector backends, same as the rest of the suite, so it measures the pipeline's own overhead -- auth, defense calls, policy evaluation, JSON marshaling -- not real-model inference latency). Writesbenchmark-results/gateway-chat.json(vegeta report) plus a text summary.scripts/load-test-ml.sh-- real-ML-backend profile. Not run in CI (CI never has the ML overlay up). Run this manually against a box runningdocker-compose.demo-ml.yml, withAEGIS_INTERNAL_TOKENandAEGIS_API_KEYSset. It load-tests input-defense and output-defense directly (isolating each detector's real inference cost) as well as the full gateway pipeline, writingbenchmark-results/ml-*.json.
Tracked for future work: neither script currently gates CI on any
latency/throughput/success-rate threshold -- these numbers are informational
only for now. There's no real baseline yet to set a sane threshold against;
once a few real runs (especially from load-test-ml.sh) establish what
"normal" looks like, this should be revisited and the CI-safe profile wired
up to fail the build on a real regression.
scripts/chaos-test.sh automates the manual procedure described in
FAILURE_MODES.md's own "Verification" section: it stops
one real dependency at a time against the running stack and asserts the
documented contract actually holds, restoring and health-checking before
moving to the next one.
- Stopping
input-defense,output-defense,policy-engine, ormodel-routermust make the gateway's/v1/chat/completionsfail closed (502/500) -- no response is ever released with a decision dependency down. Stoppingpolicy-engineadditionally checks that agent-gate's/v1/evaluatefails closed too, neverAPPROVED. - Stopping
auditmust NOT break anything -- the gateway chat pipeline keeps succeeding (fail-open), perFAILURE_MODES.md's explicit fail-open table.
Unlike the load-testing scripts above, this is pass/fail correctness against
a contract this repo already publishes, not a numeric measurement -- it runs
in CI (a dedicated chaos job) and a failure here fails the build. Run it
locally with ./scripts/chaos-test.sh against an already-running stack.
See DR-RUNBOOK.md for the full procedure. Short version:
docker-compose.yml has exactly one persistent volume (postgres_data --
the audit trail and the redteam's learned attack corpus); everything else is
reproducible from GitHub/GHCR. scripts/backup-postgres.sh runs daily via a
cron job installed by deploy/oracle/setup.sh, encrypting a snapshot with
the same SOPS+age setup as the credential backup (Stage B.1).
scripts/restore-postgres.sh restores it. Off-box redundancy (surviving a
total VM loss, not just data corruption on a live box) is a deliberate
manual step, not automated -- see the runbook for why.
| Stage | Component | Status |
|---|---|---|
| 0 | Scaffold, shared schemas, CI, docker-compose | Done |
| 2 | Input defense | Done |
| 3 | Policy engine | Done |
| 4 | Model router | Done |
| 5 | Output defense | Done |
| 6 | Agent gate | Done |
| 7 | Red-team engine | Done |
| 8 | Audit service | Done |
| 9 | Dashboard | Done |
| 10 | SDKs | Done |
| 11 | Example apps | Done |
| H4 | Go gateway restored as the HTTP orchestrator (was Python) | Done |
| 12 | Harness: governed multi-step agent loop + starter tool library (operator-platform phases 1-2, see ARCHITECTURE.md) | Done |
A second pass, after the initial build order above, closing real gaps found by direct investigation of the running system rather than a pre-written checklist — see ARCHITECTURE.md and each linked doc for the full detail on any of these.
| Stage | Component | Status |
|---|---|---|
| A.1 | Internal service-to-service auth (AEGIS_INTERNAL_TOKEN) |
Done |
| A.2 | Model-router auth enforcement | Done |
| B.1 | Encrypted, opt-in credentials backup (SOPS + age) | Done |
| B.2 | Audit signing-key rotation with historical-key verification | Done |
| B.3 | Audit cursor-pagination fix (composite keyset, no dropped/duplicated rows) | Done |
| C.1 | Domain + HTTPS via Cloudflare — see Domain + HTTPS via Cloudflare | Done |
| C.2 | Dormant self-hosted WAF scaffold (Coraza + CRS, CrowdSec) — not wired into the default deploy, see deploy/oracle/waf/README.md | Done (dormant, opt-in) |
| D.1 | Load testing (CI-safe + manual real-ML profile), see Load testing | Done |
| D.2 | Chaos / fault-injection testing against FAILURE_MODES.md's contract, see Chaos / fault-injection testing |
Done |
| D.3 | Disaster recovery: encrypted postgres backup/restore + daily cron, see Disaster recovery | Done |
| E.1 | Fixed a real credential-leak bypass in taint tracking (server-detected credentials now always block) | Done |
| E.2 | Agent-identity fingerprinting + spoofing-detection script (scripts/asi07-identity-consistency-query.py) |
Done |
See .env.example for the full list. Key variables by service:
| Variable | Service | Purpose |
|---|---|---|
XAI_API_KEY |
model-router | xAI Grok API key (not GROK_API_KEY) — set only in .env; never export in shell |
AEGIS_DASHBOARD_USER / AEGIS_DASHBOARD_PASSWORD |
dashboard | HTTP basic auth. No static default — generated at container startup if unset; use scripts/generate-credentials.sh to persist one |
AEGIS_API_KEYS |
gateway | Comma-separated API keys accepted by the gateway. No static default — generated at container startup if unset; use scripts/generate-credentials.sh to persist one |
AEGIS_INTERNAL_TOKEN |
policy-engine, audit, input-defense, output-defense, model-router (enforce) + gateway, agent-gate, redteam, dashboard, SDK embedded mode (send) | Shared internal service-to-service token. The five "enforce" services refuse to start without it — no default, no ephemeral fallback (it's shared across processes, so a per-process generated value would just disagree with everyone else's copy). Set via scripts/generate-credentials.sh |
OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY |
model-router | Cloud LLM providers |
AEGIS_MODEL_ROUTER_CONFIG |
model-router | Path to providers.yaml |
AEGIS_POLICY_DIR |
policy-engine | YAML policy pack directory |
AEGIS_INPUT_DEFENSE_PORT |
input-defense | HTTP port (default 8090) |
AEGIS_OUTPUT_DEFENSE_PORT |
output-defense | HTTP port (default 8091) |
AEGIS_POLICY_ENGINE_URL |
agent-gate | Policy-engine base URL |
AEGIS_APPROVAL_TTL_HOURS |
agent-gate | Pending approval TTL |
AEGIS_REDTEAM_INPUT_DEFENSE_URL |
redteam | Input defense base URL for campaigns |
AEGIS_REDTEAM_OUTPUT_DEFENSE_URL |
redteam | Output defense base URL for campaigns |
AEGIS_AUDIT_SIGNING_KEY |
audit | Ed25519 signing key (PEM or base64 seed) |
AEGIS_AUDIT_SIGNING_KEY_ID |
audit | Signer key identifier on receipts |
AEGIS_AUDIT_SIGNING_KEYS_HISTORY |
audit | Retired keys' public halves (keyID:base64pub, comma-separated), so receipts signed before a rotation stay verifiable. Maintained automatically by scripts/generate-credentials.sh on rotation — see "Rotating the audit signing key" below |
AEGIS_INPUT_DEFENSE_URL |
sdk-proxy / gateway | Input defense URL for SDK pipeline |
AEGIS_MODEL_ROUTER_URL |
sdk-proxy / gateway | Model router URL for SDK pipeline |
OPENAI_BASE_URL |
your app | Set to http://localhost:8080/v1 for reverse-proxy mode |
DATABASE_URL |
redteam, audit | Postgres connection |
Every audit receipt is signed and records which key signed it
(AEGIS_AUDIT_SIGNING_KEY_ID). Rotating AEGIS_AUDIT_SIGNING_KEY used to
break verification of every receipt signed under the old key — the audit
service only ever held one public key to check against. It no longer does:
scripts/generate-credentials.sh --rotate (or a backfill run that happens
to catch the key still on its public dev default) now derives the outgoing
key's public half and appends it to AEGIS_AUDIT_SIGNING_KEYS_HISTORY
before generating a new key and a new AEGIS_AUDIT_SIGNING_KEY_ID — old
receipts keep verifying, new receipts sign under the new key. This is
automatic; you don't need to do anything beyond running the script.
Redeploy afterward so the audit service picks up the new environment.
.env is gitignored and exists only on the box that generated it — losing
that disk means losing every credential in it, with no history. Optional,
opt-in encrypted backup via SOPS +
age: see .sops.yaml
for one-time setup, scripts/generate-credentials.sh for the (automatic,
once configured) backup step, and scripts/decrypt-credentials.sh for
recovery. Skipped entirely if you haven't set it up — nothing changes
about the default flow.
Published under ghcr.io/hamidmatiny (compose pulls ghcr.io/hamidmatiny/aegis-*:${AEGIS_IMAGE_TAG:-latest}):
| Image | Service |
|---|---|
aegis-gateway |
Gateway |
aegis-policy-engine |
Policy engine |
aegis-model-router |
Model router |
aegis-agent-gate |
Agent gate |
aegis-audit |
Audit |
aegis-input-defense |
Input defense |
aegis-output-defense |
Output defense |
aegis-redteam |
Red team |
aegis-smb-copilot |
SMB Copilot API |
aegis-smb-portal |
SMB portal UI |
aegis-dashboard |
Ops dashboard |
Local builds: docker compose build (see Quick start). Production Oracle deploy: deploy/oracle/SMB-DEPLOY.md.
Apache 2.0 — see LICENSE.