Skip to content

01. Features Design

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

Feature Overview

Feature Description
Branching Conversations Tree-based message storage enabling non-linear dialogue with sibling branches
Real-Time Streaming Server-Sent Events delivering text, reasoning tokens, and tool-call results incrementally
Chat Input Rich composition area with model/tool/knowledge-base selectors, slash commands, and file uploads
System Prompt Assembly Five-layer prompt stack combining user, project, assistant, knowledge-base, and file-access instructions
Projects Organisational workspaces grouping chats under a shared global prompt and optional knowledge base
Assistants Reusable AI persona presets with custom prompts, applicable per-chat or via @ mentions
Prompts Slash-command shortcuts that prepend saved instructions before the user's message
Knowledge Bases Hybrid vector + full-text search retrieval-augmented generation over uploaded documents
MCP Integration Connects external tool servers (stdio and HTTP) for AI tool-calling within chats
Artifacts Side-panel rendering of Markdown, HTML, spreadsheets, and Mermaid diagrams produced by the AI
Model Selection Per-message model choice from any user-configured OpenAI-compatible provider with capability gating
File Attachments Image, document, and spreadsheet uploads via S3-compatible object storage
Translation Standalone AI-powered text and document translation across 20 languages
Transform Agents Multi-step spreadsheet automation pipelines with human review gates
Search Global client-side search across chats, projects, and assistants with type faceting
Profile Management Account settings covering security, sessions, OAuth linking, and account deletion
User Settings App-level configuration including encrypted API key and global system prompt
Email Transactional email delivery via Postmark for authentication lifecycle events
State Management Zustand-based optimistic state with composable slices for chat and entity data
Markdown Rendering Rich text with syntax highlighting, KaTeX maths, and Mermaid diagram support

Chat System

Branching Conversations

Messages are stored as a tree rather than a flat list. Each message holds a reference to its parent, and the active conversation branch is tracked by a pointer to the deepest leaf node. Edits and regenerations create sibling nodes instead of overwriting existing messages, preserving the full conversation history.

See dedicated wiki page for details: Message Tree and Branching

Real-Time Streaming

AI responses are delivered as a Server-Sent Events stream. The client parses incoming events incrementally — text deltas, reasoning tokens, tool-call initiations, tool results, errors, and completion signals — updating the interface in real time as the model generates its response.

See dedicated wiki page for details: AI Responses and Streaming

Chat Input & Composition

The chat input is the primary interface for composing messages. It provides a rich set of composition tools accessible from a unified input area.

Text Input

The main textarea auto-expands as the user types, up to a configurable maximum height. It accepts plain text and supports paste, drag-and-drop, and keyboard shortcuts. Pressing Enter (without Shift) submits the message; Shift+Enter inserts a newline.

Slash Commands (/)

Typing / in the input triggers a command palette overlay. The palette searches two sources: user-created prompt shortcuts (filtered by title and shortcut keyword) and MCP prompts from enabled servers. Selecting a command inserts the prompt's content before the user's message text, separated by a double newline. The prompt ID is recorded in the message metadata so the system can trace which shortcut was used.

Assistant Mentions (@)

Typing @ opens a mention palette listing the user's assistants by name and description. Selecting an assistant binds it transiently to that single message — its system prompt is injected into the prompt stack for that request only. The @ mention system is disabled when the chat already has a permanently bound assistant (set at chat creation), since the bound assistant's prompt always applies.

File Attachments

Users can attach files by clicking the attachment button or dragging files onto the input area. Supported types and limits:

  • Images: up to 3 per message, maximum 2 MB each
  • Documents (PDF, plain text, Markdown): maximum 20 MB each
  • Spreadsheets (Excel): maximum 50 MB each
  • Total: up to 5 attachments per message

Documents have their text extracted client-side before sending. Spreadsheet files are uploaded to S3 and made available to MCP tools via presigned URLs.

Model Selector

A dropdown in the input toolbar allows the user to choose which AI model processes the current message. Selection is per-message, not per-chat — different messages within the same conversation can use different models. The selector displays model labels grouped by provider and visually indicates capability badges (vision, tool-calling). When editing or regenerating a message, the selector defaults to the model that was originally used.

Tool Picker

The tool picker button opens a dialog listing all enabled MCP servers, their discovered tools, and built-in tools (artifact creation, knowledge-base search). Users can toggle individual tools on or off per message. A separate knowledge-base picker allows attaching one or more knowledge bases whose content the AI can query during generation.

Bottom Toolbar Layout

The input toolbar sits beneath the textarea and contains (left to right): the attachment menu, model selector, tool picker, knowledge-base picker, and send/stop button. On mobile, the toolbar condenses into a drawer-based layout.

flowchart LR
    A[User composes message] --> B[Select model / tools / KB / assistant]
    B --> C[Hit Send]
    C --> D[Client Streaming Logic]
    D --> E[Persist user message to DB]
    E --> F[Upload attachments to S3]
    F --> G[Reconstruct thread from message tree]
    G --> H["POST /api/chat with history"]
    H --> I[SSE stream begins]
    I --> J["Parse events: text, reasoning, tool-call, tool-result, error, done"]
    J --> K[Update Zustand store optimistically]
    K --> L[Persist assistant message to DB]
Loading

System Prompt Assembly

The system prompt sent to the AI model is assembled from up to five layers, each optional. The layers are concatenated in order with double-newline separators. If no layers produce content, a default fallback instruction is used.

Layer Source Purpose
1. Global User Prompt User settings Baseline instructions that apply to every conversation across all projects and assistants
2. Project Prompt Project record Context-specific instructions shared by all chats within a project
3. Assistant Prompt Assistant record Persona and behavioural instructions for the selected or mentioned assistant
4. Knowledge Base Instruction Automatic (if KB active) Instructs the AI to use the knowledge-base search tool for document-related questions
5. Spreadsheet Access Instructions Automatic (if spreadsheets attached) Lists the presigned URLs of attached spreadsheet files and instructs the AI to use MCP tools to read them
flowchart TB
    subgraph System Prompt Assembly
        direction TB
        L1["Layer 1: Global User Prompt\n(from User Settings)"]
        L2["Layer 2: Project Prompt\n(from Project record)"]
        L3["Layer 3: Assistant Prompt\n(from Assistant record)"]
        L4["Layer 4: Knowledge Base Instruction\n(auto-injected if KB active)"]
        L5["Layer 5: Spreadsheet File Access\n(auto-injected if spreadsheets attached)"]
        L1 --> L2 --> L3 --> L4 --> L5
    end
    L5 --> FINAL["Final System Message\n(all layers joined with \\n\\n)"]
Loading

The knowledge-base instruction layer tells the AI to call a dedicated search tool when the user's question relates to their uploaded documents. The spreadsheet instruction layer provides the exact file paths or URLs so the AI can pass them directly to Excel MCP tools without asking the user.

Chat API Pipeline

The chat endpoint processes each request through seven sequential phases before returning a streaming response.

Phase 1 — Authentication & Validation. The request is authenticated via a Better Auth session check. If the session is missing or expired, a 401 response is returned immediately. The request body is validated against a strict Zod schema covering the chat ID, message history, model selection, and MCP tool/server selections.

Phase 2 — Context Resolution. The pipeline fetches the chat record and verifies ownership. If the chat belongs to a project, the project's global prompt and knowledge-base association are loaded. If an assistant is bound to the chat or mentioned via @, the assistant's system prompt is fetched. The active knowledge-base ID is resolved by checking (in priority order): the per-message selection, the chat-level setting, and the project-level setting.

Phase 3 — AI Provider Initialisation. The system resolves which OpenAI-compatible provider owns the requested model, retrieves the user's encrypted API key for that provider, decrypts it, and creates a configured AI provider instance. If no key is found or decryption fails, the request is rejected with a descriptive error (see wiki/18.-Provider-Registry-&-Model-Management.md).

Phase 4 — MCP Tool Registration. Enabled MCP servers — both user-owned and public community servers — are fetched from the database, filtered by the user's per-message selection, and connected in parallel. Tool definitions are merged into a single registry. Built-in tools (artifact management, knowledge-base search) are added if selected. A cleanup function is prepared for post-stream teardown.

Phase 5 — Message Preparation. The message history is filtered based on model capabilities — image attachments are stripped for non-vision models. Messages are assembled into the format expected by the AI SDK. The system prompt is built from the five-layer stack. Spreadsheet attachments have presigned S3 URLs generated for tool-based access.

Phase 6 — Streaming. The AI SDK's streaming function is invoked with the assembled messages, registered tools (if the model supports tool-calling), a maximum of 10 tool-use steps, and a 60-second timeout. The response is wrapped in a ReadableStream that emits SSE-encoded events for text deltas, reasoning tokens, tool calls, and tool results. Each event is sent to the client as it occurs.

Phase 7 — Post-Stream Cleanup. After the stream completes, the assistant's response is persisted to the database with metadata (model, tool calls, tool results). The chat's current leaf pointer is updated to the new assistant message. MCP server connections are torn down. Any errors during the stream are caught and sent as SSE error events before closing.

sequenceDiagram
    participant Client
    participant API as POST /api/chat
    participant Auth as Better Auth
    participant DB as PostgreSQL
    participant Provider as OpenRouter
    participant MCP as MCP Servers

    Client->>API: POST request (chatId, messages, model, tools)
    API->>Auth: Validate session
    Auth-->>API: Session (userId)
    API->>API: Zod schema validation

    API->>DB: Fetch chat, project, assistant, KB
    DB-->>API: Context records

    API->>DB: Fetch user's encrypted API key
    DB-->>API: Encrypted key
    API->>API: Decrypt key → init provider

    API->>MCP: Connect servers in parallel
    MCP-->>API: Tool registries
    API->>API: Merge tools + add built-ins

    API->>API: Assemble system prompt (5 layers)
    API->>API: Filter history by model capabilities

    API->>Provider: streamText(model, messages, tools)
    Provider-->>API: Stream chunks

    loop For each chunk
        API-->>Client: SSE event (text / reasoning / tool-call / tool-result)
    end

    API->>DB: Persist assistant message + metadata
    API->>DB: Update chat.currentLeafId
    API->>MCP: Disconnect servers
    API-->>Client: SSE done event
Loading

Projects

Purpose

Projects are organisational workspaces that group related chats under a shared context. They provide a way to maintain a consistent AI instruction set across multiple conversations on the same topic — for example, a software project, a research area, or a client engagement.

User Interface Flow

The projects listing page displays all user-created projects as a searchable card grid. Each card shows the project name, description, pin status, and timestamps. Clicking a card navigates to the project detail page.

The detail page uses a tabbed layout with the following sections:

Tab Content
Chats Lists chats scoped to this project with a "New Chat" button
Knowledge Selector to associate a knowledge base with the project
Prompt Auto-expanding textarea for the project's global system prompt (up to 10,000 characters)
Settings Editable name and description fields
Tools MCP tool picker for pre-selecting which tools are available in this project's chats
Danger Zone Project deletion with confirmation dialog

A create dialog accessible from the listing page collects the project name and optional description.

Chat Scoping

When a chat is created within a project, the project ID is stored on the chat record. Project chats are routed under the project's URL namespace and are excluded from the sidebar's "Recent Chats" list — they are accessed exclusively through the project detail page. This two-tier organisation keeps the main sidebar focused on standalone chats while project-related conversations remain grouped.

Chats can be moved between projects (or removed from a project) via the "Move to Project" dialog, which updates the project reference on the chat record.

Global Prompt

Each project has an optional global prompt field (up to 10,000 characters). This prompt becomes the second layer in the system prompt assembly, injected after the user's global prompt and before any assistant prompt. It applies uniformly to every chat within the project, ensuring a consistent behavioural baseline.

Knowledge Base Association

A project can be linked to a single knowledge base. When set, this association acts as the fallback if no knowledge base is selected at the individual chat or message level. The resolution chain is: per-message selection → chat-level setting → project-level setting. This means a project knowledge base provides default context for all chats without requiring explicit selection each time.

Pinning

Projects can be pinned to appear at the top of the listing page. Pinned projects are sorted before unpinned ones, with both groups ordered by last-updated timestamp. Pinning is a simple boolean toggle with no limit on the number of pinned projects.

Deletion

Deleting a project unbinds all associated chats by clearing their project reference — the chats themselves are preserved as standalone conversations. The project record, its settings, and its tool selections are permanently removed.

Key Design Decisions

  • Projects do not have their own dedicated chat interface; they are purely organisational containers.
  • The global prompt is the primary mechanism for project-level context, rather than configuration files or structured settings.
  • Knowledge-base association at the project level avoids repetitive per-chat setup for document-heavy workflows.

Projects vs Assistants

Aspect Projects Assistants
Purpose Organise chats by topic or domain Define an AI persona with specific behaviour
Prompt layer 2nd (project prompt) 3rd (assistant prompt)
Card click Navigates to detail page Creates a new chat bound to the assistant
Knowledge base link Yes (project-level default) No
Pinning Yes No
In-chat mention No Yes (@ mention for transient use)
Sidebar visibility Project chats hidden from main list Chats appear in "Recent Chats" normally
Deletion effect Chats unbound but preserved Chats unbound but preserved

Assistants

Purpose

Assistants are reusable AI persona presets. Each assistant defines a name, optional description, a system prompt, and an optional avatar. They allow users to create consistent AI personalities — such as a code reviewer, a creative writer, or a domain expert — that can be applied across multiple conversations.

User Interface Flow

The assistants listing page displays all user-created assistants as a searchable card grid. Each card shows the assistant's name, description, avatar, and timestamps.

Clicking an assistant card creates a new chat bound to that assistant and immediately navigates to the chat interface. This design prioritises the most common action — starting a conversation — over configuration browsing.

The assistant detail page is accessible via a dedicated route and uses a tabbed layout:

Tab Content
Chats Lists chats using this assistant
Prompt Editable system prompt for the assistant's persona
Settings Editable name, description, and avatar
Tools MCP tool picker for pre-selecting tools
Danger Zone Assistant deletion with confirmation dialog

Chat Binding

Assistants relate to chats in two modes:

  • Permanent binding: When a chat is created via an assistant card click, the assistant ID is stored on the chat record. The assistant's prompt always applies as the third system-prompt layer for every message in that chat. This binding persists for the lifetime of the chat.
  • Transient mention (@): In a chat without a permanently bound assistant, typing @ opens the mention palette only for the very first message. Once the first message has been sent, the mention palette is disabled even in unbound chats. Selecting an assistant applies its prompt for that single message only — it is not stored on the chat record. The mentioned assistant ID is passed in the request metadata.

The @ Mention System

The mention system activates when the user types @ in the chat input. It searches across all assistants by name and description. Selecting an assistant from the palette records the selection in the message metadata.

The @ mention is disabled when the chat already has a permanently bound assistant. This prevents conflicting persona instructions — the bound assistant always takes precedence.

Prompt Assembly

The assistant's system prompt occupies the third layer in the five-layer prompt stack. It is injected after the global user prompt and project prompt, but before the knowledge-base and spreadsheet instructions. Both permanently bound and transiently mentioned assistants are resolved identically at the API layer — the assistant prompt is fetched from the database in either case.

Deletion

Deleting an assistant unbinds all associated chats by clearing their assistant reference. The chats themselves are preserved. Deletion is confirmed via a destructive-action dialog.

Key Design Decisions

  • The card-click-to-create-chat pattern reflects the assumption that users create assistants to use them, not to configure them repeatedly.
  • The mutual exclusivity of permanent binding and @ mentions avoids ambiguity about which persona applies.
  • Assistants deliberately do not support knowledge-base associations — that responsibility belongs to projects and per-message selections.

Prompts & Slash Commands

Prompts are user-defined shortcuts invoked by typing / followed by the shortcut keyword in the chat input. They prepend saved instruction text before the user's message, enabling reusable prompt templates for common tasks such as summarisation, code review, or translation.

See dedicated wiki page for details: Prompts and System Prompts


Knowledge Bases (RAG)

Knowledge bases provide retrieval-augmented generation over uploaded documents. Documents are chunked, embedded into 2,048-dimensional vectors, and stored alongside a full-text search index. At query time, hybrid search (vector cosine similarity combined with PostgreSQL FTS) uses Reciprocal Rank Fusion to return the most relevant chunks, which are injected into the AI's context.

See dedicated wiki page for details: RAG Knowledge Base


MCP Integration

The application connects to external Model Context Protocol servers (supporting both stdio and Streamable HTTP transports) to provide AI tool-calling capabilities. Connections are per-request, run in parallel, and enforce a 10-second timeout. Tool IDs follow a namespaced format and are filtered by the user's per-message selection. SSRF protection blocks requests to private network addresses.

See dedicated wiki page for details: MCP Integration


Artifacts & Canvas

The artifact panel renders structured outputs produced by the AI in a dedicated side panel. Supported formats include Markdown (rendered via BlockNote), HTML (sandboxed iframe), spreadsheets (interactive grid with styling), and Mermaid diagrams. Artifacts are created via a built-in tool that the AI invokes when the user requests a document, table, diagram, or interactive layout.

See dedicated wiki page for details: Artifact Panel


Model Selection & AI Provider

Per-Message Model Selection

Model selection in AI Client operates at the message level, not the chat level. Each message independently records which model was used for generation in its metadata. This allows users to switch models mid-conversation — for example, using a vision model for an image-related question and a tool-calling model for data analysis within the same chat.

Available Models

All models are served through OpenRouter's free tier. The application defines a central model registry with capability metadata:

Model Provider Context Window Vision Tool Calling
GPT OSS 120B OpenAI 128,000 No Yes
Gemma 4 31B Google 32,768 No Yes
Nemotron Nano VL NVIDIA 128,000 Yes No
Nemotron 3 Super NVIDIA 128,000 No Yes

Capability Gating

The application uses capability metadata to gate feature availability per model:

  • Vision: Image attachments are only included in the request payload when the selected model supports vision. For non-vision models, image attachments are silently stripped from the message history before sending.
  • Tool calling: MCP tools and built-in tools are only passed to the AI when the selected model supports tool-calling. If a user selects tools but chooses a non-tool-calling model, the tools are omitted with a logged warning.

A fallback heuristic handles unknown models by inferring capabilities from their identifier prefix (e.g., model IDs containing "vl" or "vision" are assumed to support vision).

Bring Your Own Key (BYOK)

AI Client requires each user to provide credentials for one or more OpenAI-compatible AI providers (OpenRouter, Ollama, Groq, Azure, etc.). Credentials are entered in the app settings page and are encrypted at rest using AES-256 before being stored in the database. At request time, the credentials are decrypted server-side and used to initialise the AI provider. Keys are never exposed to the client — when fetched for display, they are masked (e.g., sk-or-****...****).

If no provider credentials are configured, all AI operations fail with a descriptive error message and a direct link to the settings page.

See dedicated wiki pages for details: Provider Registry & Model Management · AI Provider Key Encryption

Model Persistence

When a message is generated, the model ID is stored in the message's metadata JSON field. This serves two purposes:

  • Display: The UI can show which model produced each response.
  • Inheritance: When editing or regenerating a message, the input defaults to the model that was originally used, so the user does not need to reselect it.

File Attachments

File attachments are uploaded to S3-compatible object storage (MinIO in development) with presigned URLs for secure access. The client-side pipeline handles validation, text extraction (for documents), and metadata preparation before upload. Attachments are linked to messages or transform runs in the database.

See dedicated wiki page for details: Client-Side Attachment Pipeline and Object Storage


Workflows

Workflows are standalone, purpose-built tools that operate outside the chat interface. They are accessible via a dedicated sidebar section with their own navigation.

Translation

Purpose

The translation workflow provides AI-powered text translation as a focused, single-purpose tool. Unlike the general chat interface, it is optimised for quick translation tasks without the overhead of conversation management, message persistence, or streaming.

User Interface

The page uses a split-pane layout:

  • Left pane (Source): A text area for entering or pasting source text (up to 5,000 characters), with a file attachment button and a character counter.
  • Right pane (Target): A read-only text area displaying the translated output, with a copy-to-clipboard button.
  • Control bar: Sits between the header and the panes, containing a source language selector, a swap button, and a target language selector. All selectors are searchable comboboxes.
  • Header row: Displays the workflow title and a model selector.

Language Support

The workflow supports 19 languages plus an auto-detect option for the source language:

Languages
English, Spanish, French, German, Italian, Portuguese, Russian, Chinese (Simplified), Chinese (Traditional), Japanese, Korean, Arabic, Hindi, Bengali, Turkish, Dutch, Polish, Vietnamese, Thai

The auto-detect option sends the text without a specified source language, allowing the AI to identify it automatically. The swap button exchanges source and target languages (disabled when auto-detect is active) and also swaps the current text content between panes.

File Support

Users can attach a single file to the source pane:

  • Documents (PDF, plain text, Markdown): Text is extracted client-side and placed into the source text area, truncated to 5,000 characters.
  • Images: Sent directly to the AI with an OCR instruction. A warning badge appears if the selected model does not support vision, indicating that OCR may fail.

Non-Streaming Design

The translation workflow uses a non-streaming server action rather than the SSE-based chat pipeline. The entire translation completes server-side before the result is returned to the client. This design was chosen because:

  • Translations are typically short, making incremental display unnecessary.
  • The simpler request/response pattern avoids the complexity of stream parsing, message persistence, and abort handling.
  • The result is not persisted to the database — it exists only in the client's local state.

Data Flow

The client collects the source text, language selections, model, and optional attachment, then calls a server action. The server action authenticates the user, validates the input against a Zod schema, initialises the AI provider with the user's API key, constructs a translation-specific prompt, and invokes the AI SDK's non-streaming text generation. The translated text is returned directly to the client.

Transform Agents (Spreadsheet Automation)

Transform agents are multi-step document transformation pipelines that process spreadsheet files through a series of AI-driven steps with optional human review gates. They support dry-run mode, step-level configuration, and input/output attachment tracking.

See dedicated wiki page for details: Step-by-Step Automations


Search

Purpose

The global search page provides a unified interface for finding entities across the application. It aggregates chats, projects, and assistants into a single searchable view, allowing users to quickly locate any resource without navigating to individual listing pages.

Searchable Entities

Entity Searched Fields
Chats Title
Projects Name
Assistants Name

How It Works

The search page is a server component that fetches all user-owned chats, projects, and assistants concurrently on page load. These are passed to a client component that performs client-side filtering — there is no server-side search query. As the user types in the search box, the displayed results are filtered in real time by matching the query against the relevant text fields (case-insensitive substring match).

Type Faceting

A dropdown filter allows the user to narrow results by entity type:

  • All Types (default): Shows chats, projects, and assistants together.
  • Chats: Shows only chat results.
  • Projects: Shows only project results.
  • Assistants: Shows only assistant results.

Results are displayed in sectioned groups (Projects → Assistants → Chats), each with a count badge. Within each section, items are rendered using the same card components used on their respective listing pages, providing visual consistency.

Design Decisions

  • Client-side filtering was chosen because the total number of user-owned entities is expected to remain manageable (hundreds, not thousands). This avoids additional API endpoints and database queries for search, keeping the implementation simple.
  • No full-text search is used for this feature — it relies on simple substring matching. Full-text search is reserved for the knowledge-base RAG system, where document retrieval demands it.
  • The search page is accessible from the sidebar header, making it a quick-access feature.

Profile & Account Management

The profile section provides a comprehensive suite of account management tools, accessible from a dedicated sidebar with five sections.

Section Features
General Display name update; email shown as read-only
Security Password management, TOTP 2FA (QR code + backup codes), passkey management (WebAuthn)
Sessions Active session list with device metadata, current session identification, bulk revocation
Linked Accounts OAuth provider linking and unlinking (GitHub, Discord)
Danger Zone Permanent account deletion with email confirmation

General

The general settings page displays a profile update form pre-populated with the user's current name and email. The name field is editable; the email field is read-only (email changes are not supported through this interface). Updates are submitted via a server action that validates the input and persists the change.

Security

The security page aggregates three credential management areas:

  • Password: If the user has a credential-based account (email + password), a change-password form is shown. If the user signed up via OAuth and has no password, a "Set Password" button triggers a password-reset email to establish one.
  • Two-Factor Authentication (TOTP): Users can enable 2FA by entering their current password. The system generates a TOTP secret and displays a QR code for scanning with an authenticator app. After verification with a 6-digit code, 2FA is activated and backup codes are displayed for recovery. Disabling 2FA requires the current password.
  • Passkeys (WebAuthn): Users can register hardware security keys or platform authenticators as passwordless login credentials. Each passkey has a user-assigned name. Existing passkeys are listed with their creation date and can be individually deleted.

Sessions

The sessions page lists all active sessions for the user's account, showing device metadata (IP address, user agent). The current session is visually identified. Users can revoke all other sessions in bulk, forcing re-authentication on those devices.

Linked Accounts

The linked accounts page shows the status of each supported OAuth provider (GitHub, Discord). For each provider, the interface displays either a "Link" button (to initiate the OAuth flow) or the linked account details with an "Unlink" button. Linking redirects the user through the provider's OAuth consent screen and returns to the profile page. Unlinking removes the provider association after confirmation.

Danger Zone

The danger zone page contains a single destructive action: permanent account deletion. Clicking the delete button triggers an email confirmation flow — a verification link is sent to the user's email address. The account is only deleted after the user clicks the confirmation link (which expires after 24 hours). An "Are you sure?" confirmation dialog is shown before initiating the flow.


User Settings

The settings section manages app-level configuration that applies globally across all chats and workflows.

App Settings

The app settings page contains two primary fields:

  • OpenRouter API Key: The user's personal API key for accessing AI models through OpenRouter. Keys with the standard OpenRouter prefix (sk-or-) are encrypted at rest using AES-256 before database storage. The field uses a password-type input with a show/hide toggle. When retrieved for display, the key is decrypted and masked (only the first and last few characters are shown). Submitting a masked value is a no-op — only raw keys trigger re-encryption.
  • Global System Prompt: A free-text field (up to 5,000 characters) that defines baseline instructions prepended to every AI request. This is the first layer in the system prompt assembly, appearing before project and assistant prompts. The textarea auto-expands as content grows. An empty global prompt is valid and simply omits the layer.

Changes are saved via a server action that validates the input, handles encryption for the API key, and upserts the settings record (one row per user).

Related Management Pages

The settings sidebar also provides navigation to:

  • Prompt Management: CRUD interface for slash-command prompt shortcuts. See the Prompts and System Prompts wiki page for details.
  • MCP Server Management: CRUD interface for configuring MCP tool servers. See the MCP Integration wiki page for details.

App-Level vs Entity-Level Settings

Setting Level Scope Examples
App-level (User Settings) Applies to all conversations API key, global system prompt
Project-level Applies to all chats within a project Project prompt, project KB, project tools
Assistant-level Applies when assistant is active Assistant prompt, assistant tools
Message-level Applies to a single message Model selection, per-message tool selection, per-message KB

Settings are resolved hierarchically — message-level overrides take precedence, falling back through assistant, project, and finally app-level defaults.


Email System

Provider

All transactional emails are sent through Postmark, a dedicated transactional email service. The integration uses Postmark's server client, initialised with a server token from environment variables. A single sender address (configured via environment) is used for all outbound messages.

Email Types

Type Trigger Purpose Link Expiry
Welcome Successful user registration Greets the user and confirms account creation N/A (no action link)
Verification Sign-up (automatic) Activates the user's account via a verification link 24 hours
Password Reset Forgot-password request Provides a link to set a new password 24 hours
Account Deletion Delete-account request Confirms permanent account deletion via a confirmation link 24 hours

Auth Integration

All email dispatch is hooked into Better Auth's lifecycle events:

  • The welcome email fires in an after hook on user creation.
  • The verification email fires via the sendVerificationEmail hook, which is triggered automatically on sign-up (with sendOnSignUp: true).
  • The password reset email fires via the sendResetPassword hook when a user requests a password reset.
  • The account deletion email fires via the sendDeleteAccountVerification hook when a user initiates account deletion.

Each email is sent in both HTML and plain-text formats for client compatibility. The emails use inline CSS styling with a maximum width of 600 pixels for consistent rendering across email clients.


Sidebar & Navigation

The application uses a context-sensitive sidebar that adapts based on the current route group. Four sidebar variants exist: the main app sidebar, the profile sidebar, the settings sidebar, and the workflow sidebar.

Main App Sidebar Structure

The main sidebar serves authenticated routes and contains three zones:

Header Zone:

  • New Chat button — creates a standalone chat and navigates to it
  • Search link — navigates to the global search page

Content Zone:

  • Navigation links — Projects, Assistants, Knowledgebases, Workflows
  • Recent Chats — a collapsible list showing the 20 most recently updated standalone chats (excluding project-bound chats), sorted by last update time. Each entry has a context menu for rename, move-to-project, and delete actions.

Footer Zone:

  • User dropdown — shows avatar, name, and email. Contains links to Profile, Settings, and a Sign Out action.
flowchart TB
    subgraph Sidebar
        direction TB
        H["Header"]
        H1["New Chat"]
        H2["Search"]
        H --> H1
        H --> H2

        N["Navigation"]
        N1["Projects"]
        N2["Assistants"]
        N3["Knowledgebases"]
        N4["Workflows"]
        N --> N1
        N --> N2
        N --> N3
        N --> N4

        R["Recent Chats (max 20)"]
        R --> RC1["Chat 1 — context menu"]
        R --> RC2["Chat 2 — context menu"]
        R --> RCn["..."]

        F["Footer — User Dropdown"]
        F --> F1["Profile"]
        F --> F2["Settings"]
        F --> F3["Sign Out"]
    end
Loading

Two-Tier Chat Organisation

Chats are organised into two tiers:

  • Standalone chats appear in the sidebar's "Recent Chats" section and on the main chats listing page.
  • Project chats are excluded from the sidebar and the main chats listing. They are accessed exclusively through their parent project's detail page, under the "Chats" tab.

This separation keeps the sidebar focused and prevents project-related conversations from cluttering the general chat list.

Optimistic Data Flow

The sidebar reads chat data from the Zustand store, which is hydrated on mount by fetching from the server. All sidebar actions (rename, delete, move) update the store optimistically — the UI reflects changes immediately while server actions execute in the background. If a server action fails, a toast notification alerts the user.

Per-Chat Context Menu

Each chat entry in the sidebar and on listing pages has a context menu (triggered by a "more" icon button) offering:

  • Rename — opens an inline rename dialog
  • Move to Project — opens a searchable project picker dialog
  • Delete — opens a destructive-action confirmation dialog

Profile and Workflow Sidebars

The profile sidebar replaces the main sidebar on profile routes. It displays the user's avatar, name, and email, and provides navigation to the five profile sections (General, Security, Sessions, Linked Accounts, Danger Zone). A "Back to Home" button returns to the main interface.

The settings sidebar replaces the main sidebar on settings routes. It navigates between General (app settings), Tools (MCP servers), and Prompts (slash-command shortcuts), with a "Back to Home" button.

The workflow sidebar replaces the main sidebar on workflow routes. It lists the available workflows (All Workflows, Translation, Spreadsheets Automation) and includes a "Back to Home" button and the same user dropdown footer as the main sidebar.


State Management

The application uses a single Zustand store composed of two slices — a chat slice managing messages and conversation state, and an entity slice managing projects, assistants, prompts, knowledge bases, and MCP servers. All mutations follow an optimistic update pattern for responsive UI.

See dedicated wiki page for details: State Management


Markdown Rendering

AI responses are rendered as rich Markdown with support for GitHub Flavoured Markdown tables, fenced code blocks with syntax highlighting, KaTeX mathematical notation (inline and block), and Mermaid diagram rendering. Code blocks include a copy-to-clipboard button and language labels.

See dedicated wiki page for details: Markdown Rendering


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. Postmark Documentation
  9. WebAuthn / FIDO2 Specification
  10. Server-Sent Events Specification
  11. MinIO Documentation
  12. pgvector Documentation
  13. Tailwind CSS Documentation
  14. Base UI Documentation
  15. BlockNote Documentation

Clone this wiki locally