1
0
simple-ai-gateway-tool/ARCHITECTURAL-FACTS.md
Nilton Constantino d48172539a
first commit
2026-04-22 14:55:58 +01:00

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.