# Architectural Facts > Companion document to README.md — covers the *why* behind the architecture, not the *how*. ## Core Principle: AI as Requesting Client The gateway treats AI agents the same way secure backends treat any client: you cannot stop someone from sending a `curl` request, but you can control whether it **succeeds**. Credentials are not exposed, access is scoped, and every action must pass through policy-enforced backend services. **The objective is not to "control the AI". The objective is: no meaningful action succeeds unless it passes through controlled, policy-enforced backend systems.** This aligns with OWASP guidance for AI agents (least privilege, separation of untrusted input from trusted instructions) and MCP security expectations. ## Module Architecture The project is a **modular monolith** — 17 Gradle modules organized by bounded context, deployed as a single Spring Boot application. ``` app-monolith # Composition root main-infrastructure # Shared kernel (BaseModel, exceptions, value objects) main-service # Application services main-contract-subjects # Cross-module contracts adapter-http-security-infra # JWT auth, Spring Security config adapter-http-gateway # HTTP orchestration layer gateway-module-policy/ # Policy engine (api, shared, contract-subjects, domain, model-and-data) gateway-module-authorization/ # Authorization (api, shared, contract-subjects, domain, model-and-data) gateway-module-audit/ # Audit trail (api, contract-subjects, domain) gateway-module-fraud-detection/ # Fraud detection (api, domain) gateway-module-tool-registry/ # Tool registry (api, contract-subjects, domain) gateway-providers/ # Tool implementations (jira, jenkins, database) ``` Each module follows a consistent sub-module split: `api` (interfaces), `domain` (logic), `model-and-data` (persistence/config), `contract-subjects` (cross-module DTOs), `shared` (module-internal utilities). ## Policy Engine: Deny-Overrides Strategy All policy evaluation uses **deny-overrides**: any single rule can block an action, regardless of other rules that might allow it. The default decision is `deny`. Six typed rule evaluators handle distinct concerns: | Rule Type | Purpose | |-----------|---------| | `scope` | Verifies the agent holds the required scope for the tool+action | | `environment` | Per-environment decisions (allow staging, require approval for prod) | | `argument_allowlist` | Validates specific arguments against an allowlist | | `time_window` | Restricts execution to business hours or defined windows | | `rate_limit` | Caps requests per agent per tool within a time window | | `block` | Unconditional deny for decommissioned or dangerous tools | **Design choice:** Typed evaluators over a generic DSL (like OPA/Rego). Each rule type is self-documenting and domain-specific. This trades extensibility for clarity, which is appropriate for the current scope of the project. **Future evolution:** If rule types grow beyond ~10 or policies require complex cross-rule logic (e.g., "allow if scope matches AND time is within window AND the last 3 requests were not denied"), migrate to **Open Policy Agent (OPA)** with Rego policies. OPA provides a mature ecosystem: policy bundles, decision logging, partial evaluation, and a test framework. The migration path is straightforward — the `PolicyEvaluator` interface stays, the typed evaluators are replaced by an OPA client that sends the request context as input and receives allow/deny decisions. Policy configuration moves from `policies.yaml` to `.rego` files, gaining full logical expressiveness. Policy configuration lives in `files/config/policies.yaml` with a Kubernetes-style manifest format (`apiVersion`, `kind`, `metadata`, `spec`). Rules support `$data` references for shared values (allowlists, rate limits, time windows). ## Tool Registry: Autodiscovery Tool providers register via `@Component` implementing `ToolProvider`. The gateway discovers them at startup through Spring's component scanning — no central registration file. Adding a new tool provider is a single-class operation. Current providers: **Jira** (read/create), **Jenkins** (deploy), **Database** (query, migrate, execute). ## Audit: Non-Optional, TSDB-Ready Every gateway request produces an `AuditEntry` — both allowed and denied actions are logged. The data model separates **tags** (indexed dimensions: agentId, tool, action, decision) from **fields** (payload: arguments, reason, scopes, policyRuleId), making it ready for time-series database ingestion. Current storage: NDJSON append-only file. The tag/field separation allows migration to a TSDB without changing the domain model. **Future evolution:** Replace the NDJSON file with a time-series database such as **Prometheus** (for metrics-oriented queries and alerting integration) or **InfluxDB** (for high-cardinality event storage with native retention policies). The existing tags/fields data model maps directly to TSDB concepts — tags become indexed labels, fields become measurement values. This swap is a storage-layer change only; the `AuditStore` interface and domain model remain untouched. ## Fraud Detection A dedicated module analyzes request patterns for anomalies: SQL injection attempts in arguments, prompt injection patterns, and repeated denial sequences (an agent hammering a denied tool). Fraud signals are recorded in audit entries alongside policy decisions. **Future evolution:** Current detection uses deterministic pattern matching (regex, counters, heuristics). A natural next step is integrating **LLM-based verification** — feeding suspicious requests to a language model that can assess intent with semantic understanding rather than pattern matching. This enables detection of obfuscated injection attempts, novel attack vectors, and context-dependent anomalies that rule-based systems miss. The `FraudDetector` interface already supports this: add an LLM-backed implementation alongside the existing rule-based one, and compose them (rule-based as fast first pass, LLM as deeper analysis for flagged requests). ## Security Model - **JWT authentication** with an RSA key pair generated at startup - **Stateless sessions** — no server-side session storage - **Credentials never exposed to tools** — the gateway holds credentials; providers receive pre-authenticated clients - **All policy enforcement is server-side** — clients stay thin and contain no policy logic ## Client Authentication Client credentials should be supplied through environment variables or another secret source outside the repository. The important property is simple: clients authenticate, receive scoped JWTs, and never embed downstream system credentials. This keeps the trust boundary where it belongs: - The client proves identity to the gateway - The gateway issues scoped tokens - All protected operations still require server-side evaluation ## Client Integration Any HTTP-capable client can integrate with the gateway: an AI assistant, a CLI, a backend worker, or a small frontend. The integration model is intentionally simple: authenticate, obtain a JWT, call `/api/gateway/execute`, and handle one of three outcomes: `ALLOW`, `DENY`, or `REQUIRE_APPROVAL`. Demo scenarios in `scenes/` cover the full policy matrix: allowed operations, scope mismatches, blocked tools, rate limits, time windows, argument validation, fraud detection (SQL injection, prompt injection, repeated denials), audit trail queries, and hot-reload of policies.