# Architecture Overview

## Service deep-dives

- [Server](server.md) — Express API, agent loop, tool execution
- [Search](search.md) — BM25, HNSW, RRF, glossary, ConnectRPC
- [Platform](platform.md) — auth, tenancy, permissions, RBAC+ABAC
- [Sandbox](sandbox.md) — isolated V8 code execution, warm pool
- [Data model](data-model.md) — Prisma schema, relationships, indexes
- [Agent memory](agent-memory.md) — cross-session memory, observations, facts

---

## System overview

ows-coda is a standalone AI agent for the Abacus royalties platform. It answers questions about accounts, contracts, advances, revenue, and ledger balances by calling live backend services via Claude's native tool use on AWS Bedrock.

It runs as an Express server behind `ows-grass`, which handles JWT verification and injects identity headers. Trust proxy is enabled for correct client IP resolution behind HAProxy/ALB.

## Architecture diagram

```
Browser (React SPA)
    |
    |  POST /api/v1/chats/:id/stream (SSE)
    v
ows-grass (API gateway)  <-- JWT verification, identity header injection
    |
    v
ows-coda server (apps/server)
    |
    +---> AWS Bedrock (Claude)       <-- streaming tool-use conversation loop
    |
    +---> ows-coda search (apps/search) <-- ConnectRPC: hybrid BM25+HNSW+glossary search
    |       +---> HuggingFace ONNX   <-- embedding model (in-process)
    |       +---> S3                  <-- snapshot persistence
    |       +---> Snowflake           <-- schema metadata polling
    |       +---> GraphQL gateway     <-- schema introspection polling
    |
    +---> Redis                      <-- conversations (7-day TTL), rate limiting
    |
    +---> Snowflake                  <-- agent-consumable views (key-pair auth, read-only)
    |
    +---> MySQL (Aurora)             <-- persistent storage (chats, messages, users)
    |
    +---> Downstream services
            +-- ows-abacus-account   <-- accounts, payees, tax, payments
            +-- ows-royalties        <-- contracts, advances, terms
            +-- ows-moneyhub         <-- revenue, statement periods (Snowflake-backed)
            +-- ows-product          <-- products by UPC / ISRC
            +-- ows-ledger           <-- balances, payable amounts, adjustments
            +-- GraphQL gateway      <-- federated schema queries
```

## System design

```mermaid
graph TB
    subgraph internet["Internet"]
        browser["Browser<br/><i>React SPA + IndexedDB</i>"]
    end

    subgraph aws["AWS (us-east-1)"]
        subgraph edge["Edge / Ingress"]
            alb["ALB"]
            haproxy["HAProxy"]
        end

        subgraph grass_svc["ows-grass"]
            grass["API Gateway<br/><i>JWT verification</i><br/><i>Identity header injection</i>"]
        end

        subgraph ecs["ECS Cluster"]
            subgraph fargate["Fargate Service — ows-coda server"]
                task1["Task 1<br/><i>Express :8080</i>"]
                task2["Task 2<br/><i>Express :8080</i>"]
                taskN["Task N<br/><i>Express :8080</i>"]
            end
            subgraph search_svc["Fargate Service — ows-coda search"]
                search1["Task 1<br/><i>ConnectRPC :8081</i>"]
            end
        end

        subgraph data["Data Stores"]
            elasticache["ElastiCache<br/><i>Redis — conversations (7d TTL),<br/>rate limiting, sliding window</i>"]
            aurora["Aurora MySQL<br/><i>Prisma — users, chats,<br/>messages, feedback</i>"]
            s3["S3 Bucket<br/><i>Attachment blobs<br/>(pre-signed URLs)</i>"]
        end

        subgraph ai["AI"]
            bedrock["AWS Bedrock<br/><i>Claude Sonnet 4.6 (default)<br/>Streaming Converse API<br/>Prompt cache</i>"]
        end

        subgraph secrets["Secrets"]
            sm["Secrets Manager<br/><i>Snowflake private key,<br/>HMAC / AES keys</i>"]
        end

        ecr["ECR<br/><i>Docker images<br/>tagged by commit SHA</i>"]

        subgraph monitoring["Observability"]
            datadog["Datadog<br/><i>APM traces, metrics</i>"]
            sentry["Sentry<br/><i>Error tracking</i>"]
        end
    end

    subgraph external["External Services"]
        snowflake["Snowflake<br/><i>Key-pair auth,<br/>agent-consumable views,<br/>session-var RBAC</i>"]

        subgraph downstream["Downstream APIs"]
            abacus["ows-abacus-account"]
            royalties["ows-royalties"]
            moneyhub["ows-moneyhub"]
            product["ows-product"]
            ledger["ows-ledger"]
            graphql["GraphQL Gateway"]
        end

        auth0["Auth0<br/><i>JWT issuer</i>"]
    end

    subgraph ci["CI/CD"]
        jenkins["Jenkins<br/><i>Build, test, scan, deploy</i>"]
    end

    browser -- "HTTPS" --> alb
    alb --> haproxy
    haproxy --> grass
    grass -- "HTTP + identity headers" --> fargate
    task1 & task2 & taskN -- "Converse API (streaming)" --> bedrock
    task1 & task2 & taskN -- "ConnectRPC" --> search1
    task1 & task2 & taskN -- "ioredis" --> elasticache
    task1 & task2 & taskN -- "Prisma" --> aurora
    task1 & task2 & taskN -- "HTTPS (tool calls)" --> downstream
    task1 & task2 & taskN -- "Key-pair auth" --> snowflake
    task1 & task2 & taskN -- "dd-trace" --> datadog
    task1 & task2 & taskN -- "SDK" --> sentry
    task1 & task2 & taskN -. "read at startup" .-> sm
    search1 -- "Key-pair auth" --> snowflake
    search1 -- "Introspection" --> graphql
    search1 -- "Snapshots" --> s3
    browser -- "pre-signed URL" --> s3
    grass -- "JWKS" --> auth0
    jenkins -- "push image" --> ecr
    jenkins -- "deploy" --> fargate

    style aws fill:#f8f9fa,stroke:#232f3e,stroke-width:2px
    style ecs fill:#eef,stroke:#336,stroke-width:1px
    style fargate fill:#ddf,stroke:#336,stroke-width:1px
    style data fill:#efe,stroke:#363,stroke-width:1px
    style edge fill:#ffe,stroke:#663,stroke-width:1px
    style monitoring fill:#fef,stroke:#636,stroke-width:1px
    style external fill:#fff5ee,stroke:#996,stroke-width:1px
    style search_svc fill:#dde,stroke:#336,stroke-width:1px
    style ci fill:#f5f5f5,stroke:#666,stroke-width:1px
```

### Core infrastructure

```mermaid
graph LR
    subgraph fargate["ECS Fargate"]
        task["ows-coda<br/><i>Express :8080</i><br/><i>Stateless, N tasks</i>"]
    end

    subgraph redis["ElastiCache"]
        cache["Redis<br/><i>Conversations (7d TTL)</i><br/><i>Rate limiting</i>"]
    end

    subgraph mysql["Aurora"]
        db["MySQL<br/><i>Users, chats, messages</i><br/><i>Feedback, tool calls</i>"]
    end

    subgraph storage["S3"]
        bucket["Attachment Bucket<br/><i>Blobs (pre-signed URLs)</i><br/><i>Private, CORS-enabled</i>"]
    end

    task -- "ioredis<br/>read/write per request<br/>Lua atomic append" --> cache
    task -- "Prisma<br/>write on turn complete<br/>read on cache miss" --> db
    task -- "pre-signed URL generation<br/>(HMAC, no network call)" --> bucket
    browser["Browser"] -- "pre-signed GET<br/>direct download" --> bucket

    style fargate fill:#ddf,stroke:#336,stroke-width:1px
    style redis fill:#fde,stroke:#633,stroke-width:1px
    style mysql fill:#efe,stroke:#363,stroke-width:1px
    style storage fill:#ffe,stroke:#663,stroke-width:1px
```

### Scaling characteristics

| Resource                 | Scaling model                             | Notes                                                                    |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------------------ |
| **Server Fargate tasks** | Horizontal (ECS service auto-scaling)     | Stateless — Redis shares conversation state across instances             |
| **Search Fargate tasks** | Horizontal (ECS service auto-scaling)     | CPU-bound (embedding); each task holds in-memory indexes                 |
| **ElastiCache**          | Vertical (node type)                      | Single-node sufficient at current scale; cluster mode is an upgrade path |
| **Aurora MySQL**         | Vertical (instance class) + read replicas | Write volume is low (one write per completed turn)                       |
| **Bedrock**              | Managed by AWS                            | Subject to per-model throughput quotas (check Service Quotas)            |
| **Snowflake**            | Warehouse auto-suspend/resume             | `AGENT_ANALYTICS_SVC` warehouse; 30s statement timeout                   |
| **S3**                   | Managed by AWS                            | Pre-signed URLs + search snapshots                                       |
| **ECR**                  | Managed by AWS                            | Images tagged by commit SHA; lifecycle policy for cleanup                |

## Database schema

```mermaid
erDiagram
    User ||--o{ Chat : "owns"
    Chat ||--o{ Message : "contains"
    Model ||--o{ Message : "generates"
    Message ||--o{ MessageToolCall : "triggers"
    Message ||--o{ MessageThought : "produces"
    Message ||--o{ MessageSource : "references"
    Message ||--o{ MessageAttachment : "attaches"
    Message ||--o| MessageFeedback : "receives"

    User {
        varchar(36) id PK
        varchar(64) identity_hash UK "HMAC-SHA256 lookup key"
        varchar(512) identity_encrypted "AES-256-GCM reversible"
        datetime created_at
        datetime updated_at
    }

    Chat {
        varchar(36) id PK
        varchar(36) user_id FK
        varchar(36) root_message_id "tree anchor"
        varchar(500) title "auto-generated"
        boolean starred
        datetime created_at
        datetime updated_at
        datetime deleted_at "soft delete"
    }

    Message {
        varchar(36) id PK
        varchar(36) chat_id FK
        varchar(36) parent_message_id "NULL for root"
        varchar(36) active_child_message_id "selected branch"
        varchar(36) active_leaf_message_id "deepest active descendant"
        varchar(36) model_id FK "NULL for user msgs"
        enum role "user | assistant"
        text content
        int depth "0-based tree depth"
        int sibling_index "branch index"
        int input_tokens
        int output_tokens
        int latency_ms
    }

    Model {
        varchar(36) id PK
        varchar(255) external_id "provider model ID"
        varchar(50) provider "e.g. bedrock"
        varchar(255) display_name
        datetime deprecated_at
        datetime sunset_at
    }

    MessageToolCall {
        varchar(36) id PK
        varchar(36) message_id FK
        varchar(255) tool_use_id "Anthropic protocol ID"
        varchar(255) name "e.g. search_accounts"
        json input
        json result_content
        varchar(50) result_status
        datetime started_at
        datetime completed_at
    }

    MessageThought {
        varchar(36) id PK
        varchar(36) message_id FK
        text reason_text
        int step_order "display ordering"
    }

    MessageSource {
        varchar(36) id PK
        varchar(36) message_id FK
        varchar(2048) href "platform deep-link"
        varchar(500) title "display label"
    }

    MessageAttachment {
        varchar(36) id PK
        varchar(36) message_id FK
        varchar(50) type "image | document | file"
        varchar(255) media_type "MIME type"
        varchar(500) filename
        varchar(1024) storage_key "S3 key"
        bigint size_bytes
        datetime deleted_at "soft delete"
    }

    MessageFeedback {
        varchar(36) id PK
        varchar(36) message_id UK
        boolean rating "true=up false=down"
        varchar(1000) comment
    }
```

## Monorepo structure

The workspace follows an `apps/` + `packages/` convention. Apps are deployable services; packages are shared libraries.

### Apps (deployable)

| Package                  | Path             | Description                                                                                                                                                                                              |
| ------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@coda/server-app`       | `apps/server/`   | Express backend — AI orchestration, tool execution, SSE streaming, Redis caching, Snowflake/GraphQL integration                                                                                          |
| `@coda/client-app`       | `apps/client/`   | React frontend built with Vite. Chat UI with SSE streaming, file attachments, disambiguation flows.                                                                                                      |
| `@coda/search-service`   | `apps/search/`   | Semantic search microservice — hybrid BM25 + HNSW + glossary search over GraphQL schema and Snowflake metadata. Uses IndexEngine/IndexStrategy architecture for composable data sources. ConnectRPC API. |
| `@coda/runner-service`   | `apps/runner/`   | Datasource runner service — executes datasource sync jobs.                                                                                                                                               |
| `@coda/platform-service` | `apps/platform/` | Platform service — auth, tenancy, permissions, RBAC+ABAC.                                                                                                                                                |

### Packages (shared libraries)

| Package                 | Path                        | Description                                                                                                                               |
| ----------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `@coda/async`           | `packages/async/`           | Async patterns: circuit breaker, retry, semaphore, throttlers, single-flight.                                                             |
| `@coda/data-structures` | `packages/data-structures/` | Data structures (heaps, lists, queues, stacks, trees, sorted arrays, ring buffers) and algorithms (binary search, topk, string distance). |
| `@coda/search`          | `packages/search/`          | Search engine: BM25, HNSW, trie, hybrid search, RRF/weighted-sum fusion, Porter stemmer, glossary matching, graph signals.                |
| `@coda/common`          | `packages/common/`          | Storage abstractions, redis utilities, crypto (identity hashing/encryption), LRU cache, Snowflake helpers.                                |
| `@coda/memory`          | `packages/memory/`          | Agent memory: observation log, fact consolidation, decay strategies, prompt formatting.                                                   |
| `@coda/sandbox`         | `packages/sandbox/`         | V8 isolate execution engine for LLM-generated JavaScript. Memory-gapped sandbox with message-passing bridge protocol.                     |
| `@coda/api-common`      | `packages/api-common/`      | Shared pagination protos, RPC utilities (callRpc, createTransport, RpcResult, mapConnectError).                                           |
| `@coda/core-api`        | `packages/core-api/`        | REST types, HTTP client, proto-generated ConnectRPC services. Dual ESM/CJS output built with tsdown.                                      |
| `@coda/admin-api`       | `packages/admin-api/`       | Admin ConnectRPC services (access, identity, tenancy, RBAC).                                                                              |
| `@coda/search-api`      | `packages/search-api/`      | Search ConnectRPC client + proto definitions.                                                                                             |
| `@coda/runner-api`      | `packages/runner-api/`      | Runner ConnectRPC client + proto definitions.                                                                                             |
| `@coda/tools-api`       | `packages/tools-api/`       | Tool service proto definitions.                                                                                                           |
| `@coda/datasources-api` | `packages/datasources-api/` | Datasource service proto definitions.                                                                                                     |
| `@coda/dashboards-api`  | `packages/dashboards-api/`  | Dashboard service proto definitions.                                                                                                      |
| `@coda/db`              | `packages/db/`              | Prisma schema, migrations, client, types, and crypto. Shared database infrastructure for all services.                                    |
| `@coda/extensions`      | `packages/extensions/`      | Domain glossary data — GraphQL and Snowflake glossary JSON files with typed loaders and Snowflake schema resolvers.                       |

See [search.md](search.md) for search algorithms and ranking details.

Build order: `packages/db/` must build first (Prisma generate + compile), then `packages/core-api/` and `packages/search-api/` (shared types), then `apps/server/`, `apps/client/`, and `apps/search/` can build independently.

## Request flow

1. **Client** sends `POST /api/v1/chats/:id/stream` with a user query (and optional attachments).
2. **ows-grass** verifies the JWT and injects `orchard-identity-id` + `orchard-user-name` headers, then proxies to ows-coda.
3. **Route registry** (`routes/index.ts`) applies request logging, auth verification, rate limiting, and Snowflake identity injection.
4. **Chat routes** (`routes/chat-routes.ts`) wire handlers to Express routes. **Conversation handlers** (`routes/conversation-handlers.ts`) manage CRUD, **message handlers** (`routes/message-handlers.ts`) handle message listing and feedback, and the **stream handler** (`routes/stream-handler.ts`) validates the request via Zod schemas, resolves the thinking budget by query intent, parses attachments (`routes/attachment-parser.ts`), and loads conversation history from Redis (falling back to Coda DB if configured). SSE event formatting is centralized in `routes/sse-utils.ts`.
5. **Orchestrator** (`ai/orchestrator.ts`) sends the conversation to Claude via the provider abstraction layer. The system prompt and tool definitions are cache-pointed for cost/latency savings.
6. **Tool use** — when Claude requests tool calls, the tool registry (`ai/tools/registry.ts`) dispatches them concurrently to downstream services via authenticated HTTP calls. Results are appended to the conversation and sent back to Claude for the next turn.
7. **Skills** — high-level skill tools aggregate multiple service calls (e.g. `account_overview_skill` fetches account details, contracts, and revenue in one call), reducing round trips.
8. **Streaming** — text tokens, thinking steps, reasoning, source links, generated files, and suggestions are streamed to the client as SSE events in real time.
9. **Persistence** — the final conversation state is saved to Redis (7-day TTL) and optionally to Coda DB. On first exchange, a conversation title is auto-generated using a lightweight model.

## Key design decisions

### Native tool use

Claude drives multi-turn tool calls via the Bedrock Converse API. There is no orchestration framework — the conversation loop in `ai/orchestrator.ts` sends messages to Claude, handles tool call responses by dispatching to handlers, appends results, and loops until Claude produces a final text response.

### Streaming SSE over WebSockets

Server-Sent Events were chosen over WebSockets because:

- Communication is one-directional (server to client) — the client only sends the initial HTTP request.
- SSE works natively over HTTP/2 without connection upgrade negotiation.
- Simpler infrastructure: no sticky sessions, standard load balancer support, automatic reconnection built into the browser EventSource API.

### Skill tools

High-level "skill" tools aggregate multiple service calls into a single tool for broad questions. For example, `account_overview_skill` fetches account details, payment terms, tax info, activity, and contracts in one call. There are 5 skills: account overview, contract overview, revenue overview, Snowflake explore, and GraphQL explore. Individual tools handle targeted follow-ups.

### Handler-owned availability

Tools declare their own runtime availability via an `enabled()` predicate on the handler object. This allows optional infrastructure (Snowflake pool, GraphQL gateway) to gracefully degrade — tools are simply omitted from the tool list when their backing service is unavailable. No external registry needed.

### Provider abstraction

The AI provider layer (`ai/providers/`) abstracts over the LLM backend. Currently only Bedrock is implemented, but the interface supports future multi-provider scenarios. Each provider tracks warm-up state for readiness probes.

### Redis for conversation history

- **TTL-based expiry** — conversations expire after 7 days with no manual cleanup.
- **Shared across instances** — Fargate tasks share conversation state without sticky routing.
- **Atomic operations** — Lua scripts for atomic conversation append (no read-modify-write races).
- **Graceful degradation** — when Redis is unavailable, the service runs stateless with in-memory rate limiting.

### Prompt cache warm-up

On startup, the server fires a minimal Bedrock `ConverseCommand` to seed the prompt cache with the system prompt and tool definitions. The `/health/ready` readiness probe returns `503` until warm-up completes, preventing the load balancer from routing traffic to cold instances.

### Intent-based thinking budgets

Rather than a fixed thinking token budget for every query, the server classifies query intent via keyword patterns (account, contract, revenue, etc.) and assigns a per-intent budget. This keeps simple lookups fast while allowing complex analytical queries more reasoning room, within the `BEDROCK_THINKING_BUDGET` ceiling.

### Snowflake via RBAC

ows-coda connects to Snowflake as its own service user (`AGENT_ANALYTICS_SVC_USER`) with key-pair auth. Access to source-team views is granted via standard Snowflake database roles — no central governance database. Session variables carry end-user identity for optional source-team row-level filtering. See the [Snowflake Views Playbook](../snowflake-views.md) for the full pattern.

### `@coda/db` package

Shared database infrastructure (Prisma schema, migrations, client, types, crypto) lives in `packages/db/` as a workspace package. This enables future services to share the same database without duplicating schema management. Server-specific access patterns (services, orchestration) stay in `apps/server/src/db/coda/`. See the [TRD](../decisions/trds/db-package-extraction.md) for rationale and structure.

### dd-trace via `--import` flag

`dd-trace` (Datadog APM) is loaded at process startup via `node --import dd-trace/initialize.mjs` in the Dockerfile CMD. This ensures dd-trace instruments all modules before they load. The server and all shared packages compile to ESM (`.mjs` output via tsdown).

### pnpm workspaces

- **Strict dependency resolution** — no phantom dependencies (unlike npm/yarn hoisting).
- **Fast installs** — content-addressable storage with hard links.
- **Clean package boundaries** — workspace packages are consumed as real dependencies with their own builds, ensuring type contracts are explicit.

### Standalone search service

The search service (`apps/search/`) runs as a separate ConnectRPC microservice rather than being embedded in the main server. This enables independent scaling (search is CPU-bound during embedding), independent deployment, and a clean separation between the AI agent loop and the search infrastructure. The server communicates with the search service via the `@coda/search-api` generated client. Proto definitions live in `packages/search-api/proto/` and are compiled via `buf generate`.

Internally, the service uses a composable **engine/strategy** architecture. `IndexEngine<TDoc>` owns mechanical lifecycle concerns (polling, snapshots, ready/degraded flags, abort coordination) while `IndexStrategy<TDoc>` implementations (`GraphQLStrategy`, `SnowflakeStrategy`) provide domain-specific logic. Adding a new searchable data source means implementing `IndexStrategy` and wiring a new engine in `index.ts`. See [search.md](search.md) for full details.

### Shared library packages

Reusable code is split across focused packages rather than a single monolith:

- **`@coda/common`** — storage abstractions (`KeyValueStore`, `BlobStore`, null objects), redis utilities, crypto (identity hashing/encryption), LRU cache, Snowflake helpers
- **`@coda/data-structures`** — generic collections (heaps, lists, queues, stacks, trees, sorted arrays, ring buffers) and algorithms (binary search, topk, string distance)
- **`@coda/async`** — async patterns: circuit breaker, retry, semaphore, single-flight, throttlers
- **`@coda/search`** — search engine: BM25, HNSW, trie, hybrid search, RRF/weighted-sum fusion, Porter stemmer, glossary matching, graph signals

All are build-time dependencies — no runtime services or I/O.

## Tech stack

| Layer           | Technology                                                                    |
| --------------- | ----------------------------------------------------------------------------- |
| Runtime         | Node.js 24, TypeScript (strict mode, ESM output via tsdown)                   |
| Framework       | Express (server), ConnectRPC (search)                                         |
| AI              | AWS Bedrock Converse API (Claude Sonnet 4.6 default)                          |
| Search          | In-process hybrid BM25 + HNSW + glossary, HuggingFace ONNX embeddings         |
| Cache           | Redis (ioredis) — Elasticache in QA/prod                                      |
| Database        | MySQL (Aurora) via Prisma, Snowflake (reader pool for agent-consumable views) |
| Auth            | Auth0 JWT (via ows-grass upstream)                                            |
| Observability   | Datadog (dd-trace), Sentry                                                    |
| Client          | React 19 + Vite + TypeScript                                                  |
| Package manager | pnpm (workspace monorepo)                                                     |
| CI/CD           | Jenkins → Docker → ECR → Fargate                                              |

## Deployment

### Docker multi-stage build

```
prod-deps --> dev-deps --> lint-and-test       (CI: runs in parallel)
                       --> build-artifacts     (API + server compilation)
                       --> client-build        (Vite production build)
                                           --> deploy-server  (server only, no static files)
                                           --> deploy-local   (server + Docker-built client)
                                           --> deploy         (server + CI-built client)
```

The `deploy` target contains only production dependencies and compiled artifacts. `GITHUB_NPM_TOKEN` is passed as a Docker build secret for private npm registry access. In CI, the client is pre-built via `suiteAppBuild` and passed in as an additional Docker build context.

### CI/CD (Jenkins)

On every PR (parallel): compliance checks, SAST, SonarQube scan, Docker-based lint + typecheck + unit tests.

On merge to `master`: client built via `suiteAppBuild`, Docker image built and pushed to ECR (tagged with commit SHA), vulnerability scan (non-blocking), auto-deploy to QA via Fargate.

### Runtime (AWS Fargate)

- Port 8080 behind ows-grass
- `trust proxy` enabled for correct client IP behind HAProxy/ALB
- Datadog APM tracing initialized before all imports
- Sentry for error tracking
- Scales horizontally — Redis ensures conversation state is shared across instances

## Project structure

```
apps/
├── server/                              # @coda/server-app — Express backend
│   └── src/
│       ├── index.ts                     # Entry point (dd-trace init, starts server)
│       ├── server.ts                    # Express app factory
│       ├── instrument.ts                # Sentry initialization
│       ├── app-locals.ts                # Express app.locals typing
│       ├── types/                       # TypeScript types (domain, bedrock)
│       ├── config/
│       │   └── load-config.ts           # Zod-validated env config
│       ├── routes/
│       │   ├── index.ts                 # Route registry, health + readiness probes
│       │   ├── chat-routes.ts           # Router — wires handlers to Express routes
│       │   ├── conversation-handlers.ts # Conversation CRUD (list, create, delete, star)
│       │   ├── message-handlers.ts      # Message endpoints (list, feedback)
│       │   ├── stream-handler.ts        # SSE streaming endpoint
│       │   ├── sse-utils.ts             # SSE event formatting helpers
│       │   └── attachment-parser.ts     # Multipart attachment extraction
│       ├── middleware/
│       │   ├── auth.ts                  # JWT verification + identity headers
│       │   ├── error-handler.ts         # Global error handler + Sentry
│       │   ├── rate-limit.ts            # Rate limiting (Redis / in-memory)
│       │   ├── request-logger.ts        # Structured request logging
│       │   ├── security-headers.ts      # CSP, HSTS, X-Frame-Options
│       │   ├── snowflake.ts             # Identity-scoped Snowflake pool injection
│       │   └── validate-uuid.ts         # UUID route param validation
│       ├── ai/
│       │   ├── orchestrator.ts          # Streaming conversation loop with tool use
│       │   ├── system-prompt.md         # Claude system prompt
│       │   ├── providers/               # LLM provider abstraction
│       │   │   ├── registry.ts          # Provider factory + warm-up tracking
│       │   │   └── bedrock/             # AWS Bedrock Converse API implementation
│       │   ├── tools/
│       │   │   ├── registry.ts          # Tool executor + handler dispatch
│       │   │   ├── handler-utils.ts     # Shared handler utilities + availability gating
│       │   │   ├── catalog.ts           # Tool catalog for search_tools
│       │   │   ├── deferred.ts          # Deferred tool loading config
│       │   │   ├── account/             # Account tool definitions + handlers
│       │   │   ├── royalties/           # Royalties tool definitions + handlers
│       │   │   ├── moneyhub/            # Moneyhub tool definitions + handlers
│       │   │   ├── product/             # Product tool definitions + handlers
│       │   │   ├── ledger/              # Ledger tool definitions + handlers
│       │   │   ├── file/                # Excel + PDF generation
│       │   │   ├── snowflake/           # Snowflake schema + query tools
│       │   │   ├── graphql/             # GraphQL schema + query tools
│       │   │   └── search/              # Search service client + tool discovery
│       │   └── skills/
│       │       ├── account-overview/    # Account aggregation skill
│       │       ├── contract-overview/   # Contract aggregation skill
│       │       ├── revenue-overview/    # Revenue aggregation skill
│       │       ├── snowflake-explore/   # Snowflake discovery + query skill
│       │       └── graphql-explore/     # GraphQL discovery + query skill
│       ├── cache/
│       │   ├── conversation-cache.ts    # Conversation CRUD + atomic Lua append
│       │   ├── redis-store.ts           # Redis store (ioredis)
│       │   └── memory-store.ts          # In-memory fallback store
│       ├── db/
│       │   ├── snowflake/               # Snowflake reader pool (key-pair auth)
│       │   └── coda/                    # Server-specific DB access (services, persister, loader)
│       ├── search/                      # Search service ConnectRPC client wrapper
│       ├── services/                    # HTTP client for downstream services
│       └── utils/                       # Logger, time, env parsing, errors, JSON
├── client/                              # @coda/client-app — React frontend (Vite + TypeScript)
│   └── src/
│       ├── components/                  # UI components
│       ├── hooks/                       # React hooks (orchestrator, streams, mutations)
│       ├── providers/                   # Context providers (chat, auth, theme)
│       ├── data/                        # Data layer (API calls, data transforms)
│       ├── offline/                     # IndexedDB store, sync engine, mutation queue
│       └── lib/                         # Client utilities (stream session, etc.)
└── search/                              # @coda/search — Search microservice
    └── src/
        ├── server.ts                    # ConnectRPC server + Express host
        ├── config/                      # Zod-validated env config
        ├── engine/                      # IndexEngine<TDoc> lifecycle + IndexStrategy<TDoc> interface
        ├── handlers/                    # RPC handlers (searchGraphQL, searchSnowflake)
        ├── pipeline/                    # Search pipeline (hybrid → rerank → graph augment)
        ├── graphql/                     # GraphQLStrategy + introspection + search adapter
        ├── snowflake/                   # SnowflakeStrategy + schema loader + search adapter
        ├── embedding/                   # HuggingFace ONNX embedding provider (shared base class)
        ├── snapshot/                    # S3 snapshot persistence
        └── utils/                       # Logging, health checks
packages/
├── async/                               # @coda/async — circuit breaker, retry, semaphore, throttlers
├── data-structures/                     # @coda/data-structures — heaps, lists, queues, trees, algorithms
├── search/                              # @coda/search — BM25, HNSW, RRF fusion, glossary, graph signals
├── common/                              # @coda/common — storage, redis, crypto, LRU cache
│   └── src/
│       ├── cache/                       # LRU cache
│       ├── crypto/                      # Identity hashing/encryption
│       ├── redis/                       # Redis client utilities
│       ├── storage/                     # KeyValueStore, BlobStore, null objects
│       └── utils/                       # Error helpers, math, iterables
├── memory/                              # @coda/memory — agent memory (observations, facts, decay)
├── sandbox/                             # @coda/sandbox — V8 isolate execution engine
├── db/                                  # @coda/db — Prisma schema, migrations, client, types
│   ├── prisma/
│   │   ├── schema.prisma                # Prisma schema
│   │   ├── migrations/                  # Database migrations
│   │   └── seed/                        # Seed scripts
│   └── src/
│       ├── index.ts                     # Barrel: types, client, crypto
│       ├── client.ts                    # PrismaClient factory
│       ├── types.ts                     # Shared DB types
│       └── crypto.ts                    # Identity hashing/encryption
├── extensions/                          # @coda/extensions — domain glossary data
│   ├── graphql/                         # GraphQL glossary JSON
│   ├── snowflake/                       # Snowflake glossary JSON + schema resolver
│   └── src/                             # Typed glossary loaders
├── core-api/                            # @coda/core-api — REST types/client + proto services
├── admin-api/                           # @coda/admin-api — platform ConnectRPC services
├── search-api/                          # @coda/search-api — search ConnectRPC proto + client
├── runner-api/                          # @coda/runner-api — runner ConnectRPC proto + client
├── api-common/                          # @coda/api-common — shared pagination, RPC utilities
├── tools-api/                           # @coda/tools-api — tool service proto definitions
├── datasources-api/                     # @coda/datasources-api — datasource service protos
└── dashboards-api/                      # @coda/dashboards-api — dashboard service protos
```
