100 lines
7.5 KiB
Markdown
100 lines
7.5 KiB
Markdown
# 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.
|