# Scene 11 — Audit Trail: Every Decision Is Logged **Endpoint:** `GET /api/audit/recent` **Query filters:** `limit`, `from`, `to`, `tool`, `agentId`, `decision` **Expected result:** Full audit trail of all previous demo scenes --- ## Presentation Rule > **Language:** All AI narration and communication during scenes must be in **English**. > > The AI must narrate every interaction with the gateway in detail. **Before** calling the gateway, explain what it is about to do, which endpoint it will call, what payload it will send, and what it expects to happen. **After** receiving the response, explain what the gateway returned, what the decision means, and why it matters. The goal is to make the audience understand exactly what is happening between the AI and the gateway at every step. --- ## Communication Flow ```mermaid sequenceDiagram participant AI as AI Agent participant GW as Gateway participant AS as Audit Store AI->>GW: GET /api/audit/recent?limit=20 GW->>GW: Validate JWT GW->>AS: Query audit entries (tags filter) AS-->>GW: Matching entries [{timestamp, tags, fields}, ...] GW-->>AI: 200 OK [{timestamp, tags, fields}, ...] AI->>GW: GET /api/audit/recent?decision=DENY GW->>AS: Query entries where tags.decision=DENY AS-->>GW: Filtered entries GW-->>AI: 200 OK [denied entries...] AI->>GW: GET /api/audit/recent?tool=jenkins.deploy GW->>AS: Query entries where tags.tool=jenkins.deploy AS-->>GW: Filtered entries GW-->>AI: 200 OK [jenkins entries...] ``` ## Demo Script > **Presenter says:** "We've run 10 scenarios — allows, denials, fraud alerts. Every single one was logged. The audit store exposes TSDB-style queries — time range, tool, decision. The storage implementation can evolve without changing the interface." ### Action 1 — Full trail Query the endpoint: > "Show me the audit trail from the gateway — the last 20 entries" ```bash TOKEN=$(jq -r '.access_token' /tmp/gateway-token.json) curl -s "http://localhost:8080/api/audit/recent?limit=20" \ -H "Authorization: Bearer $TOKEN" | jq . ``` ### Action 2 — Filter by decision (only denials) > "Show me only the denied requests" ```bash curl -s "http://localhost:8080/api/audit/recent?decision=DENY" \ -H "Authorization: Bearer $TOKEN" | jq . ``` ### Action 3 — Filter by tool > "Show me all audit entries for jenkins.deploy" ```bash curl -s "http://localhost:8080/api/audit/recent?tool=jenkins.deploy" \ -H "Authorization: Bearer $TOKEN" | jq . ``` ### Action 4 — Filter by time range > "Show me what happened in the last 10 minutes" ```bash curl -s "http://localhost:8080/api/audit/recent?from=2026-03-26T14:20:00%2B01:00" \ -H "Authorization: Bearer $TOKEN" | jq . ``` ### Action 5 — Filter by policyRuleId (fraud entries) > "Show me only the fraud alerts" ```bash curl -s "http://localhost:8080/api/audit/recent?policyRuleId=fraud:sql-injection" \ -H "Authorization: Bearer $TOKEN" | jq . ``` ### Expected Response (single entry) Each entry follows the TSDB data model — `tags` (indexed, low-cardinality) and `fields` (payload): ```json { "timestamp": "2026-03-26T14:35:00+01:00", "tags": { "agentId": "claude-mcp-agent", "tool": "jira.createTicket", "action": "create", "decision": "DENY", "policyRuleId": "restrict-jira-projects" }, "fields": { "requestId": "req-abc-123", "reason": "Project not in allowlist", "arguments": "{\"project\":\"FINANCE\",\"summary\":\"...\"}", "scopes": "[\"jira.read\",\"jira.write\",\"jenkins.deploy\",\"db.read\",\"db.migrate\"]" } } ``` --- ## What This Proves - **TSDB data model** — explicit separation of `tags` (indexed dimensions: who, what, decision) and `fields` (payload: arguments, reason). Swap NDJSON for TimescaleDB = map tags → indexed columns, fields → JSONB, timestamp → hypertable time column - **Query by tags only** — filters work on `tool`, `agentId`, `decision`, `policyRuleId` (indexed). Fields like `arguments` are never filtered (high-cardinality payload) - **Complete observability** — every request, every decision, every reason, timestamped - Fraud alerts are filterable via `policyRuleId=fraud:*` - The endpoint itself is **authenticated** — you need a valid JWT to read audit data --- ## Query Capabilities | Filter | Type | Example | Use case | |--------|------|---------|----------| | `limit` | — | `?limit=50` | Last N entries (default 20, max 100) | | `from` / `to` | time | `?from=...&to=...` | Time range (ISO 8601 OffsetDateTime) | | `tool` | tag | `?tool=jenkins.deploy` | Filter by tool name | | `agentId` | tag | `?agentId=claude-mcp-agent` | Filter by agent | | `decision` | tag | `?decision=DENY` | ALLOW, DENY, or REQUIRE_APPROVAL | | `policyRuleId` | tag | `?policyRuleId=fraud:sql-injection` | Filter by rule or fraud detector | All tag filters are combinable: `?decision=DENY&tool=db.executeScript&limit=10` --- ## Key Message > "The audit store uses the same interface whether backed by a file or a time-series database. Right now it's NDJSON — append-only and easy to inspect with jq. If you later move to a TSDB, the gateway contract stays the same." --- ## Closing > "Authentication, authorization, fraud detection, policy engine, audit. Five layers. The AI is a requesting client — the gateway decides, and every decision is recorded."