Skip to content

02. Architecture Overview

Maruf Bepary edited this page Jun 17, 2026 · 2 revisions

Introduction

AI Client is a full-stack Next.js 16 AI chat application built around three core ideas:

  • Branching conversations — messages form a tree, not a flat list, enabling non-destructive edits and explorations.
  • Real-time streaming — Server-Sent Events deliver text, reasoning tokens, and tool-call results as they arrive.
  • Extensible tool use — the Model Context Protocol (MCP) allows external tool servers to be plugged in per-message.

The application supports multi-format artefact rendering (Markdown, HTML, spreadsheets, Mermaid), retrieval-augmented generation via hybrid vector + full-text search, and five authentication methods backed by PostgreSQL.

Purpose of this document: provide the high-level architectural overview that connects all specialised wiki pages. Each section summarises a subsystem and links to its dedicated deep-dive page. No section here duplicates the depth of those pages. For a complete feature catalogue, see the Features wiki page.


System Topology

graph TB
    subgraph Client
        Browser["Browser<br/>(React 19 SPA)"]
    end

    subgraph "Next.js Server"
        AppRouter["App Router<br/>API Routes + Server Actions"]
    end

    subgraph "Data Stores"
        PG["PostgreSQL 17<br/>+ pgvector"]
        S3["MinIO / S3<br/>(Object Storage)"]
    end

    subgraph "External Services"
        OR["User's AI Provider<br/>(OpenAI-Compatible)"]
        PM["Postmark<br/>(Transactional Email)"]
    end

    subgraph "Tool Servers"
        MCP_HTTP1["MCP Server 1<br/>(Streamable HTTP)"]
        MCP_HTTP2["MCP Server 2<br/>(Streamable HTTP)"]
    end

    Browser -->|"HTTP / SSE"| AppRouter
    AppRouter -->|"SQL (Drizzle ORM)"| PG
    AppRouter -->|"S3 API"| S3
    AppRouter -->|"HTTPS (AI SDK)"| OR
    AppRouter -->|"HTTPS (SMTP API)"| PM
    AppRouter -->|"Streamable HTTP<br/>(MCP Protocol)"| MCP_HTTP1
    AppRouter -->|"Streamable HTTP<br/>(MCP Protocol)"| MCP_HTTP2
    Browser -->|"Presigned URL"| S3
Loading

Communication Protocols

Path Protocol Notes
Browser ↔ Next.js HTTP/1.1 + SSE SSE for chat streaming; standard fetch for mutations
Next.js → PostgreSQL TCP (SQL) Via Drizzle ORM + pg driver
Next.js → MinIO/S3 HTTPS (S3 API) AWS SDK v3; presigned URLs for direct browser downloads
Next.js → AI Provider HTTPS Vercel AI SDK with provider's base URL and API key
Next.js → MCP Servers Streamable HTTP MCP Protocol, SSRF-protected
Next.js → Postmark HTTPS REST API for transactional email
Browser → S3 HTTPS Direct download via presigned URLs

Architectural Pattern

AI Client is a full-stack monolith — a single Next.js application that serves both the React frontend and all backend logic.

  • Server-side: API Route handlers for streaming responses; Server Actions for all data mutations. There is no middleware.ts — all request-level protection happens inside handlers and actions.
  • Client-side: React 19 with a single Zustand store for global state. Optimistic updates are applied immediately; the server is the source of truth.
  • No microservices, no message queues, no separate backend. External integrations (user's selected AI provider, MCP servers, Postmark) are called directly from the Next.js server process.
  • Database-first: PostgreSQL is the single source of truth for all persistent state. pgvector provides vector similarity search for RAG.

Layer Architecture

graph TB
    subgraph "Presentation Layer"
        RC["React Components"]
        ZS["Zustand Store<br/>(ChatSlice + EntitySlice)"]
        HK["Custom Hooks"]
    end

    subgraph "API Layer"
        AR["Route Handlers<br/>/api/chat · /api/rag/ingest/[documentId]<br/>/api/transform/run · /api/auth/[...all]"]
        SA["Server Actions<br/>chats · projects · assistants<br/>prompts · knowledgebases · mcp-servers"]
    end

    subgraph "Business Logic Layer"
        CP["Chat Pipeline<br/>System prompt · Tool registration · streamText"]
        RP["RAG Pipeline<br/>Ingestion · Hybrid search · RRF"]
        TP["Transform Pipeline<br/>Multi-step execution · Review gates"]
        MO["MCP Orchestration<br/>Connection pool · Tool filtering"]
    end

    subgraph "Data Access Layer"
        DR["Drizzle ORM"]
        S3C["S3 Client"]
    end

    subgraph "Infrastructure"
        PG["PostgreSQL 17 + pgvector"]
        MIN["MinIO / S3"]
        ORA["OpenRouter"]
        MCPS["MCP Servers"]
        PMK["Postmark"]
    end

    RC --> HK
    HK --> ZS
    RC --> AR
    RC --> SA
    AR --> CP
    AR --> RP
    AR --> TP
    CP --> MO
    CP --> DR
    RP --> DR
    TP --> MO
    SA --> DR
    SA --> S3C
    DR --> PG
    S3C --> MIN
    CP --> ORA
    MO --> MCPS
    SA --> PMK
Loading

Each layer has a single responsibility:

  • Presentation — rendering, local state, user interaction.
  • API — authentication, validation, request routing.
  • Business Logic — domain rules, orchestration, pipeline coordination.
  • Data Access — query construction, storage I/O.
  • Infrastructure — external services and databases.

For the complete schema reference, see the Database Design wiki page.


Routing Architecture

The application uses Next.js App Router with route groups to separate concerns and apply distinct layouts.

graph TB
    RL["RootLayout<br/>(app/layout.tsx)"]
    
    RL --> MAIN["(main)/layout.tsx<br/>AuthenticatedLayout + AppSidebar"]
    RL --> PROFILE["profile/layout.tsx<br/>AuthenticatedLayout + ProfileSidebar"]
    RL --> SETTINGS["settings/layout.tsx<br/>AuthenticatedLayout + SettingsSidebar"]
    RL --> WORKFLOWS["workflows/layout.tsx<br/>AuthenticatedLayout + WorkflowSidebar"]
    RL --> AUTH["auth/<br/>No layout wrapper (public)"]

    AUTH --> AUTH_LOGIN["login/<br/>Sign-in and sign-up"]
    AUTH --> AUTH_RESET["reset-password/<br/>Password reset flow"]
    AUTH --> AUTH_2FA["2fa/<br/>Two-factor authentication challenge"]

    MAIN --> CHATS["chats/[chatId]"]
    MAIN --> PROJECTS["projects/[id]"]
    MAIN --> ASSISTANTS["assistants/[id]"]
    MAIN --> KBS["knowledgebases/[id]"]
    MAIN --> SEARCH["search/"]

    PROFILE --> PG["general/"]
    PROFILE --> PS["security/"]
    PROFILE --> PA["accounts/"]
    PROFILE --> PSS["sessions/"]
    PROFILE --> PD["danger/"]

    SETTINGS --> ST["tools/"]
    SETTINGS --> SP["prompts/"]
    SETTINGS --> SAP["app/"]

    WORKFLOWS --> WT["transform/[id]/runs/[runId]"]
    WORKFLOWS --> WTR["translation/"]
Loading

Key conventions

  • Route groups ((main)) influence layout nesting but do not appear in URLs.
  • Dynamic segments ([id], [chatId], [runId]) for entity detail pages.
  • Private _components/ folders co-locate route-specific components within route directories.
  • Error boundaries (error.tsx) and not-found pages (not-found.tsx) at the (main) group level.
  • Each authenticated group wraps its content with AuthenticatedLayout, which performs a session check and redirects unauthenticated users.

Authentication & Security

AI Client uses Better Auth v1.6.8 with five authentication methods:

Method Provider Storage
Email + Password Built-in (scrypt hash) account table
GitHub OAuth GitHub account table
Discord OAuth Discord account table
WebAuthn Passkeys FIDO2 passkey table
TOTP 2FA Built-in two_factor table

Session Model

  • JWT tokens signed with HS256, stored in HTTP-only cookies.
  • 7-day expiry with 24-hour silent refresh.
  • Device metadata (IP, user agent) recorded for session auditing.

Three-Layer Protection Model

  1. Server Actions — requireSession() guard at the top of every protected action. Throws "Unauthorized" if no valid session.
  2. API Routes — auth.api.getSession() called per-handler for streaming endpoints.
  3. Client — AuthenticatedLayout performs a session check on mount and redirects to the sign-in page if unauthenticated.

There is no middleware.ts — all protection is application-layer.

Auth Flow

sequenceDiagram
    participant B as Browser
    participant A as Auth API<br/>(/api/auth/[...all])
    participant DB as PostgreSQL
    participant C as Cookie Store

    B->>A: POST /api/auth/sign-in (credentials)
    A->>DB: Verify user + hash
    DB-->>A: User record
    A->>C: Set HTTP-only JWT cookie<br/>(HS256, 7-day expiry)
    A-->>B: 200 OK + session data

    Note over B: Subsequent requests

    B->>A: POST /api/chat<br/>(with cookie)
    A->>A: auth.api.getSession()<br/>Validate JWT
    A->>A: Proceed with handler
    A-->>B: SSE stream

    B->>A: Server Action call<br/>(with cookie)
    A->>A: requireSession()
    A->>DB: Execute mutation
    A-->>B: Action result
Loading

Security Controls

  • SSRF protection — isBlockedUrl() blocks RFC 1918, localhost, link-local, and cloud metadata endpoints for all MCP HTTP URLs and provider base URLs.
  • BYOK model — users configure OpenAI-compatible providers (OpenRouter, Ollama, Groq, etc.) with encrypted credentials at rest (AES-256-GCM). See wiki/18.-Provider-Registry-&-Model-Management.md and wiki/17.-Per‐User-AI-Provider-&-API-Key-Encryption.md.
  • Zod validation — applied at every system boundary (API routes, Server Actions, environment variables).
  • No secrets in client bundles — all sensitive operations happen server-side.

📖 Deep dive: Authentication Architecture · AI Provider Key Encryption · Provider Registry & Model Management


AI Chat Pipeline

The chat pipeline is the core feature of the application. Every AI response follows the same ten-step lifecycle.

sequenceDiagram
    participant U as User (Browser)
    participant Z as Zustand Store
    participant S3 as MinIO/S3
    participant API as POST /api/chat
    participant Auth as Auth Layer
    participant SP as System Prompt<br/>Assembly
    participant MCP as MCP Servers
    participant AI as OpenRouter<br/>(via AI SDK)
    participant DB as PostgreSQL

    U->>Z: Compose message<br/>(text, attachments, model, tools)
    U->>S3: Upload attachments<br/>(via Server Action)
    S3-->>U: Attachment keys

    U->>Z: Optimistic insert<br/>(user + placeholder assistant message)
    U->>API: POST /api/chat<br/>(message, metadata, selected tools)

    API->>Auth: Authenticate + validate (Zod)
    Auth-->>API: Session + user ID

    API->>DB: Fetch user settings<br/>(global system prompt, model defaults)
    API->>API: Resolve provider for selected model<br/>(decrypt provider credentials — BYOK)

    API->>DB: Verify chat ownership
    API->>SP: Assemble system prompt<br/>(user global → project → assistant → KB → spreadsheet)

    API->>MCP: Connect selected servers<br/>(parallel, 10s timeout)
    MCP-->>API: Tool registries

    API->>API: Register tools<br/>(MCP tools + built-ins)

    API->>AI: streamText() via selected provider<br/>(max 10 tool steps, 60s timeout)

    loop SSE Stream
        AI-->>API: Token / tool-call / reasoning
        API-->>U: SSE event<br/>(text | reasoning | tool-call | tool-result | error)
        U->>Z: Accumulate streaming content
    end

    AI-->>API: Stream complete
    API->>DB: Persist user + assistant messages
    API->>MCP: Close connections
    API-->>U: SSE "done" event<br/>(final metadata)
    U->>Z: Commit final state
Loading

Key Design Decisions

  • Per-message model selection — the model is chosen each time the user sends a message, not set globally. Stored in message metadata.
  • Multi-provider architecture — users configure any OpenAI-compatible providers; the system dynamically resolves and routes requests based on model selection (see wiki/18.-Provider-Registry-&-Model-Management.md).
  • 5-layer system prompt — assembled from the user's custom global system prompt, project prompt, assistant prompt, knowledge base instructions, and spreadsheet URLs.
  • Tool step limit — streamText() allows up to 10 tool-use rounds per response, preventing runaway tool loops.
  • Optimistic rendering — the client inserts placeholder messages before the server responds, updating them as SSE events arrive.

📖 Deep dive: AI Responses and Streaming · MCP Integration · Prompts and System Prompts · Client-Side Attachment Pipeline


Message Tree Model

Messages are stored as an adjacency-list tree — each message has a parentId linking it to its parent. This is analogous to Git's branching model.

graph TD
    S["System Prompt<br/>(root)"]
    U1["User: Hello"]
    A1["Assistant: Hi there!"]
    U2["User: Tell me about X"]
    A2["Assistant: X is..."]
    U2b["User: Tell me about Y<br/>(edit → sibling)"]
    A2b["Assistant: Y is..."]
    A2c["Assistant: Actually, Y...<br/>(regen → sibling)"]
    U3["User: Thanks"]
    A3["Assistant: You're welcome!"]

    S --> U1
    U1 --> A1
    A1 --> U2
    A1 --> U2b
    U2 --> A2
    U2b --> A2b
    U2b --> A2c
    A2 --> U3
    U3 --> A3

    style S fill:#4a5568,color:#fff
    style U1 fill:#3182ce,color:#fff
    style A1 fill:#38a169,color:#fff
    style U2 fill:#3182ce,color:#fff
    style A2 fill:#38a169,color:#fff
    style U3 fill:#3182ce,color:#fff
    style A3 fill:#38a169,color:#fff
    style U2b fill:#805ad5,color:#fff
    style A2b fill:#805ad5,color:#fff
    style A2c fill:#d69e2e,color:#fff
Loading

In the diagram above, blue = user messages on the original branch, green = assistant replies on the original branch, purple = an edit branch (sibling), gold = a regeneration (sibling assistant node).

Core Mechanics

  • parentId links each message to its parent — stored in the database.
  • childrenIds are computed at runtime during hydration — never persisted.
  • currentLeafId on the chat record tracks which branch is active.
  • Edits create a sibling user node under the same parent.
  • Regenerations create a sibling assistant node under the same parent.
  • Branch navigation walks the tree from root to currentLeafId to reconstruct the visible thread.
  • Two-pass hydration — DB rows are loaded into an in-memory hash map, then children are computed in a second pass.

📖 Deep dive: Message Tree and Branching


State Management

The application uses a single Zustand v5 store composed from two slices:

Slice Contents Update Pattern
ChatSlice Normalised hash map of messages, active chat ID, current leaf, tree operations Optimistic: snapshot → mutate → rollback on failure
EntitySlice Flat arrays for projects, assistants, prompts, MCP servers, knowledge bases, transform agents Load-after-mutate: Server Action → reload from DB

Design Principles

  • No persistence middleware — all state is ephemeral. The store is rehydrated from PostgreSQL on navigation.
  • Optimistic updates for chat — user and placeholder assistant messages are inserted into the store immediately. If the server persist fails, the store rolls back to the pre-mutation snapshot.
  • Load-after-mutate for entities — after a Server Action completes (create, update, delete), the full entity list is reloaded from the database to ensure consistency.
  • Streaming accumulation — during SSE streaming, content is accumulated in React local state (not Zustand) to avoid excessive re-renders. The final content is committed to the Zustand store when the done event arrives.
  • Selector-based access — components subscribe to specific slices of state via selectors, minimising unnecessary re-renders.

📖 Deep dive: State Management


MCP Integration

The Model Context Protocol enables AI Client to call external tool servers during chat responses.

graph LR
    subgraph "Chat API Handler"
        REQ["Incoming Request"]
        TR["Tool Registration"]
        ST["streamText()"]
    end

    subgraph "MCP Connection Pool"
        C1["Connection 1<br/>(HTTP)"]
        C2["Connection 2<br/>(HTTP)"]
        C3["Connection 3<br/>(HTTP)"]
    end

    subgraph "MCP Servers"
        S1["File System Server"]
        S2["Database Server"]
        S3["Web Search Server"]
    end

    subgraph "Built-in Tools"
        BT1["manage_artifact"]
        BT2["search_knowledge_base"]
    end

    REQ --> TR
    TR -->|"Promise.allSettled<br/>(parallel, 10s timeout)"| C1
    TR --> C2
    TR --> C3
    C1 --> S1
    C2 --> S2
    C3 --> S3
    TR --> BT1
    TR --> BT2
    TR --> ST
    ST -->|"Tool calls"| C1
    ST -->|"Tool calls"| C2
    ST -->|"Tool calls"| C3
    ST -->|"Tool calls"| BT1
    ST -->|"Tool calls"| BT2
Loading

Key Characteristics

  • Streamable HTTP transport — connections use the Streamable HTTP protocol exclusively.
  • Per-request lifecycle — connections are created fresh at the start of each API call and torn down after the stream ends. No persistent connection pool.
  • Parallel connection — Promise.allSettled ensures a failed server never blocks others.
  • 10-second timeout on all MCP operations.
  • Namespaced tool IDs — serverId:tool:toolName prevents collisions across servers.
  • Per-message tool selection — users choose which servers and tools to activate for each message. Validation limits: 20 servers, 100 tools.
  • Built-in tools — manage_artifact (creates/updates artefacts in the side panel) and search_knowledge_base (triggers RAG retrieval).
  • SSRF protection — all HTTP MCP server URLs pass through isBlockedUrl().

📖 Deep dive: MCP Integration


RAG Pipeline

AI Client uses an agentic RAG design — the LLM autonomously decides when to search the knowledge base by calling the search_knowledge_base built-in tool.

graph TB
    subgraph "Ingestion Pipeline"
        UP["Upload Document"]
        S3I["Store in S3"]
        EXT["Text Extraction"]
        CHK["Chunking<br/>(~400 tokens, 200-char overlap)"]
        EMB["Embedding<br/>(2048-dim vectors)"]
        PGI["Upsert to PostgreSQL<br/>(pgvector)"]
    end

    subgraph "Retrieval Pipeline"
        QR["User Query<br/>(via search_knowledge_base tool)"]
        VEC["Vector Cosine<br/>Similarity"]
        FTS["PostgreSQL<br/>Full-Text Search"]
        RRF["Reciprocal Rank Fusion<br/>(k=60)"]
        TOP["Top 5 Results"]
    end

    UP --> S3I --> EXT --> CHK --> EMB --> PGI

    QR --> VEC
    QR --> FTS
    VEC --> RRF
    FTS --> RRF
    RRF --> TOP
Loading

Key Details

  • Embedding model — nvidia/llama-nemotron-embed-vl-1b-v2:free via OpenRouter, producing 2048-dimensional vectors.
  • Chunking — ~1,600 characters (~400 tokens) per chunk with 200-character overlap.
  • Hybrid search — vector cosine similarity + PostgreSQL FTS, fused with Reciprocal Rank Fusion (k=60), returning the top 5 results.
  • Knowledge base resolution cascade — per-message selection → chat-level default → project-level default.
  • Security — the kbId filter is enforced on every retrieval query to prevent cross-user data leakage.

📖 Deep dive: RAG Knowledge Base


Object Storage

AI Client uses S3-compatible object storage (MinIO in development, AWS S3 in production).

Design

  • Single bucket with key prefix conventions to separate subsystems (chat attachments, KB documents, transform I/O).
  • Presigned URLs for downloads — files are never streamed through the application server.
  • AWS SDK v3 for all S3 operations.
  • Bucket auto-creation on startup via ensureBucket().

Integration Points

Subsystem Operation Key Pattern
Chat attachments Upload + download User-scoped prefix
KB documents Upload + ingestion source KB-scoped prefix
Transform I/O Input + output files Run-scoped prefix
FileBridge Temporary local staging for spreadsheets Temporary local staging

📖 Deep dive: Object Storage


Artefact Rendering

The artefact panel is a side panel for rendering AI-generated structured content.

Supported Types

Type Renderer Interaction
Markdown BlockNote editor Editable rich text
Spreadsheet TanStack Table Tabular display
HTML Sandboxed <iframe> Isolated rendering
Mermaid Mermaid.js Diagram visualisation

Mechanics

  • Artefacts are created via the manage_artifact built-in tool or implicit Mermaid fence detection in assistant responses.
  • Stored in message metadata — there is no dedicated artefact table.
  • The artefact panel opens alongside the chat when an artefact is detected.
  • Markdown artefacts use BlockNote for WYSIWYG editing; HTML artefacts render in a sandboxed iframe for security isolation.

📖 Deep dive: Artefact Panel


Workflow System

Transform Agents are multi-step AI pipelines for document automation.

Run State Machine

stateDiagram-v2
    [*] --> Pending
    Pending --> Running : Start execution
    Running --> AwaitingReview : Review gate reached
    Running --> Completed : All steps done
    Running --> Failed : Error
    AwaitingReview --> Running : User approves
    AwaitingReview --> Failed : User rejects
    Completed --> [*]
    Failed --> [*]
Loading

Key Characteristics

  • Sequential step execution — each step runs in order, with the output of one step feeding the next.
  • Human review gates — steps can pause execution and wait for user approval before continuing.
  • Shared MCP connection pool — all steps within a run share the same MCP connections.
  • Steps defined as JSON — the step sequence is stored as a JSON string in the transform_agent table.
  • Input/output via S3 — input and output attachment IDs reference files in object storage.

📖 Deep dive: Step-by-Step Automations


API Contract Summary

Route Method Auth Purpose Max Duration
/api/auth/[...all] GET, POST Public Better Auth catch-all handler Default
/api/chat POST Session AI chat streaming (SSE) 60s
/api/rag/ingest/[documentId] POST Session RAG document ingestion 120s
/api/transform/run POST Session Transform agent execution 300s
/api/logs POST Optional Client-side log relay Default

All authenticated routes validate the session via auth.api.getSession() and return 401 Unauthorized on failure. Request bodies are validated with Zod; malformed payloads return 400 Bad Request.


SSE Event Protocol

The chat and transform streaming endpoints emit the following Server-Sent Event types:

Event Type Source Payload Description
text Chat, Transform Delta string Incremental text content
reasoning Chat Delta string Model reasoning/thinking tokens
tool-call Chat, Transform Tool name, args Tool invocation notification
tool-result Chat, Transform Tool name, result Tool execution result
transform-start Transform Run metadata Transform run initialisation
transform-step-start Transform Step index, name Individual step begins
transform-step-complete Transform Step result Individual step finishes
transform-complete Transform Final result Transform run completion
error Chat, Transform Error message Stream error
done Chat, Transform Final metadata Stream completion signal

Client Processing

  • Events are parsed by an async generator (parseSseStream) that yields typed SseEvent objects.
  • text and reasoning deltas are accumulated in React local state during streaming.
  • tool-call and tool-result events are displayed inline within the message bubble.
  • The done event triggers final state commit to Zustand and metadata persistence.
  • error events halt streaming and display an error state in the UI.

Data Validation Strategy

Validation is enforced at every system boundary using Zod.

Validation Layers

Layer Mechanism Failure Mode
Environment variables Zod schema at startup Process exits with descriptive error
API route bodies .safeParse() in handler 400 Bad Request with structured errors
Server Action inputs .safeParse() in action Thrown error or structured return
Client-side forms React Hook Form + Zod resolver Inline field-level validation errors
Message metadata messageMetadataSchema Malformed metadata rejected

Shared Fields

Common validators are defined once and reused across schemas:

  • emailField — valid email format
  • passwordField — minimum length and complexity
  • nameField — non-empty, length-bounded
  • descriptionField — optional, length-bounded
  • contentField — non-empty text content
  • sixDigitCodeField() — TOTP and backup code format

Cross-Cutting Concerns

Error Handling

  • API routes return structured JSON errors with appropriate HTTP status codes (401, 400, 404, 500).
  • SSE streaming uses error events to signal mid-stream failures without breaking the connection protocol.
  • Next.js error.tsx boundaries catch rendering errors at the (main) route group level.
  • Zustand rollback — failed persistence after optimistic updates triggers a snapshot restore.
  • MCP failures are isolated — Promise.allSettled ensures one failing server does not affect others.

Email

  • Postmark handles all transactional emails: welcome, verification, password reset, and account deletion.
  • Email templates are server-side only; no email credentials are exposed to the client.

Markdown Rendering

  • Built on the unified ecosystem: react-markdown with remark-gfm, remark-math, rehype-katex, and rehype-highlight.
  • KaTeX for mathematical notation; Mermaid for diagrams; GFM for tables and task lists.
  • Dual rendering paths:
    • react-markdown for chat message bubbles (read-only).
    • BlockNote for artefact panel editing (WYSIWYG).
  • Syntax highlighting for code blocks with language detection.

📖 Deep dive: Markdown Rendering


Technology Stack Summary

Category Technology Version
Framework Next.js (App Router, Turbopack) ^16.2.4
Language TypeScript (strict mode) ^5
UI Library React ^19.2.5
Database PostgreSQL + pgvector 17
ORM Drizzle ORM + drizzle-kit ^0.45.2 / ^0.31.10
AI Vercel AI SDK ^6.0.168
AI Provider OpenRouter (via @ai-sdk/openai) ^3.0.53
MCP @ai-sdk/mcp + @modelcontextprotocol/sdk ^1.0.36 / ^1.29.0
Auth Better Auth ^1.6.8
UI Components Base UI, Radix UI, shadcn/ui ^1.4.1
Styling Tailwind CSS v4, CVA, tailwind-merge v4
Rich Text BlockNote ^0.49.0
State Zustand ^5.0.12
Forms React Hook Form + Zod ^7.73.1 / ^4.3.6
Storage AWS SDK v3 (S3-compatible) v3
Email Postmark ^4.0.7
Testing Vitest + Testing Library + jsdom ^4.1.5
Dev Infra Docker (pgvector, MinIO) —

References

  1. Next.js App Router Documentation
  2. Vercel AI SDK Documentation
  3. Better Auth Documentation
  4. Drizzle ORM Documentation
  5. Model Context Protocol Specification
  6. OpenRouter API Documentation
  7. Zustand Documentation
  8. PostgreSQL pgvector Extension
  9. Base UI Documentation
  10. Zod Documentation
  11. MinIO Documentation
  12. React Hook Form Documentation
  13. Tailwind CSS v4 Documentation
  14. BlockNote Documentation
  15. Postmark Documentation

Clone this wiki locally