> ## Documentation Index
> Fetch the complete documentation index at: https://docs.igrisecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> How Igris components fit together — API server, frontend, database, cache, auth, and proxy flow.

# Architecture

Igris is a monorepo with two primary services, a shared database, and a Redis cache layer.

## System Overview

```mermaid theme={null}
flowchart TD
    MCP["MCP Clients\n(Claude Code, OpenCode, Cursor)"] -->|"JSON-RPC"| API["Igris API (Hono, port 3100)\n/v1/mcp/:slug\n/llm/:slug/*\n/api/v1/*\n/api/auth/*"]
    LLMClient["LLM Clients\n(OpenAI-compatible)"] -->|"chat/embeddings"| API
    API -->|"Forward tool calls"| Upstream["Upstream MCP Server"]
    API -->|"Forward LLM requests"| LLMProvider["Upstream LLM Provider\n(60 providers)"]
    API -->|"Read/Write"| DB["Neon PostgreSQL (Drizzle)\naudit_events, connections, policies,\nsessions, users"]
    API -->|"Archive expired events"| S3["S3 (Standard-IA)\nGzip NDJSON archives\nper org per day"]
    DB -->|"Rows deleted after archive"| S3
    API -->|"Cache & Pub/Sub"| Redis["Upstash Redis\nPolicy cache, rate limits,\nsession state, SSE pubsub"]
    Web["Next.js Web (port 3200)\nDashboard UI"] -->|"REST API"| API
```

## Components

### API Server (Hono)

The backend runs on [Hono](https://hono.dev) and listens on port **3100**. It handles:

* **MCP Gateway** (`/v1/mcp/:slug`) — intercept MCP tool calls, evaluate policies, forward to upstream
* **LLM Gateway** (`/llm/:slug/*`) — OpenAI-compatible API proxy for 60 LLM providers (see [LLM Gateway](#llm-gateway) below)
* **REST API** (`/api/v1/*`) — CRUD for policies, servers, sessions, audit events, billing
* **Auth** (`/api/auth/*`) — Better Auth endpoints for session management
* **SSE** (`/api/v1/events`) — real-time event stream for dashboard updates

### Web Frontend (Next.js)

The dashboard runs on [Next.js](https://nextjs.org) on port **3200** and provides:

* Governance management (servers, policies, sessions)
* Real-time observe dashboard with risk heat maps
* Organization settings, member management, billing

### Database (Neon PostgreSQL)

All persistent state lives in [Neon](https://neon.tech) PostgreSQL. The schema is managed by [Drizzle ORM](https://orm.drizzle.team) with auto-migrations on startup.

Key tables:

| Table                        | Purpose                                                                       |
| ---------------------------- | ----------------------------------------------------------------------------- |
| `user`, `account`, `session` | Better Auth identity                                                          |
| `organization`, `member`     | Multi-tenancy                                                                 |
| `connections`                | Connection configs (HTTP MCP / LLM gateway, encrypted credentials)            |
| `policies`                   | Governance rules per connection                                               |
| `agent_sessions`             | Active gateway sessions                                                       |
| `audit_events`               | Gateway audit trail (mutable — rows are archived to S3 and deleted on expiry) |

### Cache (Upstash Redis)

[Upstash Redis](https://upstash.com) handles:

* **Policy cache** — hot policies cached with TTL to avoid DB lookups on every tool call
* **Rate limiting** — sliding window counters for rate-limit policy rules
* **SSE pub/sub** — event fan-out to connected dashboard clients
* **Session state** — fast lookup for kill-switch status

### Authentication (Better Auth)

[Better Auth](https://better-auth.com) provides:

* Email/password and OAuth (GitHub, Google) login
* Organization-scoped sessions with RBAC (operational)
* API key generation for programmatic access (proxy)
* Session tokens stored as `igris.session_token` cookies (or `__Secure-igris.session_token` in production)

## LLM Gateway

The LLM Gateway is an OpenAI-compatible API proxy mounted at `/llm/:slug/*`. It routes requests through Igris connections so every LLM call is governed, audited, and cost-tracked alongside your MCP traffic.

### Route shape

```
/llm/{connection-slug}/v1/chat/completions   → chat.completions (transformed)
/llm/{connection-slug}/v1/embeddings         → embeddings (transformed)
/llm/{connection-slug}/**                    → passthrough to upstream
```

The `{connection-slug}` identifies the Igris connection that holds the upstream provider credential. No model-prefix routing is used in the URL — the provider is determined by the `provider` field on the connection record.

### Authentication

Bearer API key only (`Authorization: Bearer ig_...`). Wildcard CORS is applied (`*`) so browser-based clients can call the gateway directly. Session cookies are not accepted on gateway routes.

### What the gateway does on each request

1. Authenticates the API key and resolves the connection (slug → provider + encrypted credential)
2. Evaluates policies (allow / deny / alert / redact) against the model and endpoint
3. Checks pre-forward token guards and content guards
4. Checks rate limits (requests / tokens / dollars, Redis sliding window)
5. Forwards the request to the upstream provider with credential injection
6. Streams the response to the client via a single-pass tee (client stream + accumulator)
7. Writes an audit event with token counts, cost, and model after the response completes
8. Updates the LLM anomaly detector asynchronously (cost spike, token burn, error rate, model shift, response length)

### Provider support

60 providers are registered, including OpenAI, Anthropic, Google Gemini, Groq, Mistral, DeepSeek, Cohere, and self-hosted models via the `openai-compatible` provider (requires `customBaseUrl`).

### Enablement

The LLM Gateway is feature-flagged. Set `LLM_GATEWAY_ENABLED=false` to disable it; requests to `/llm/*` will return `404`.

## S3 Audit Archive

Audit events are **mutable** — they are retained in Postgres up to the plan's retention limit, then archived to S3 Standard-IA and deleted from Postgres.

### Archive job

A daily cron runs at UTC `ARCHIVE_CRON_HOUR_UTC` (default 03:00). For each organization it:

1. Finds days in `audit_events` older than `retentionDays + 1` buffer days
2. Serializes each day's events as gzip NDJSON
3. Writes the file to S3 with a deterministic key (`{orgId}/{YYYY-MM}/{YYYY-MM-DD}.ndjson.gz`) — idempotent overwrite
4. In a single Postgres transaction: inserts a manifest row into `audit_archives`, then deletes the archived rows from `audit_events`

Rows that were restored from an archive (`restored_from_archive_id IS NOT NULL`) are excluded from future archiving.

### Key implications

* `audit_events` does not grow unboundedly in Postgres
* Restored rows are marked with `restored_from_archive_id` so they are not re-archived
* The audit trail is complete but not immutable — rows are deleted from Postgres after archiving

## Proxy Flow

When an MCP client calls a tool through the Igris proxy:

1. **Request arrives** at `/v1/mcp/:slug` with the tool name and arguments
2. **Auth check** — validate API key or session cookie
3. **Org resolution** — determine which organization owns this connection
4. **Policy evaluation** — load rules from cache (or DB on cache miss), evaluate first-match against the tool name with conditions
5. **Action execution**:
   * **allow** → forward to upstream, log the event
   * **deny** → return error to client, log the denial
   * **alert** → forward to upstream, log + emit anomaly event via SSE
6. **Anomaly check** — evaluate rate spike and destructive pattern detectors
7. **Audit write** — persist the event to `audit_events` with timing, result, and metadata
8. **SSE broadcast** — push real-time event to connected dashboard clients

## Deployment

Igris deploys to **EC2** via GitHub Actions with a systemd service:

* API server runs as a systemd unit on EC2 (Ubuntu)
* Next.js frontend deployed to Vercel (or a separate EC2 instance)
* Neon for managed PostgreSQL (serverless, auto-scaling)
* Upstash for managed Redis (serverless, per-request pricing)
* Migrations applied automatically at API startup via `drizzle-orm/postgres-js/migrator` (programmatic — not `drizzle-kit`). The API refuses to start if any migration fails.

Igris is a fully managed cloud service — no self-hosted deployment is required.
