ChatForge

Multi-provider LLM chat interface with per-user conversation history, server-synced chat folders, per-user model catalogs, streaming responses, and server-side API proxying.

UI tour: Jump to screenshots.

Features

  • Multi-provider chat with Anthropic, OpenAI, Gemini, and VertexAI backends.
  • Server-side LLM proxy with streaming and non-streaming modes, plus provider-specific request shaping.
  • Per-user authentication and RBAC (user/admin), JWT cookie sessions, bcrypt password hashing, TOTP/recovery-code 2FA, passkey (WebAuthn) sign-in, and self password change.
  • Native auth token flow (access/refresh/session) with password and passkey login paths.
  • Admin user management (create/update/delete/reset password) with last-admin protection.
  • Per-user conversation storage with folders, pin/collapse state, drag-and-drop move/reorder, and persistent ordering.
  • Anonymous chats that are never written to the database and are cleared on refresh.
  • Chat search across titles/context metadata and message content.
  • Message-content full text search backed by SQLite FTS5 (messages_fts) with triggers and rebuild support.
  • Message editing/regeneration workflow (edit prior user prompt, trim later turns, regenerate).
  • Markdown rendering with syntax highlighting and KaTeX math rendering.
  • Attachments in messages (text/audio/video) with limits and optional media playback disable toggle.
  • Artifact store (CRUD), file/folder upload, path filtering, and @path mentions in prompts.
  • Memory store (CRUD + search) with SQLite FTS5-backed indexing and tool-assisted memory writes.
  • Slash commands for context control and generation flow (/compact, /clear, /retry, /restore).
  • Context compaction with summary persistence (context_summary and context_cutoff_count) and context-window limiting.
  • Per-user provider/model catalogs, default provider/model selection, and saved per-user preferences.
  • Provider reasoning controls (OpenAI effort/summary/web-search context, Anthropic thinking budget, Gemini/VertexAI thinking level/budget/include-thoughts).
  • Tool calling with per-tool allowlist, auto-approval vs manual approval gates, and explicit tool execution traces.
  • Local agent process support for tool execution (workspace-scoped FS/shell/web tools, bearer auth, CORS, timeout controls).
  • Client-side encrypted provider-config sync to server (PBKDF2 + AES-GCM; password-derived key).
  • Admin system operations (optional self-update pipeline, service restart, and service/git status checks).
  • Admin database operations (state inspector, table previews, VACUUM, FTS rebuild, parameterized SQL query runner).
  • Hardened API surface with CSRF origin enforcement, CSP/security headers, login rate limiting, and private-endpoint blocking controls.
  • SQLite persistence with WAL mode and per-user isolation for chats, messages, folders, preferences, models, memories, artifacts, and encrypted sync payloads.

Quick Start

npm install
cp .env.example .env        # edit CHATFORGE_SECRET
npm run user:create -- mattia hunter2 admin
npm run dev                  # starts both Vite (5173) + Express (3069)

Open http://localhost:5173

User Management

npm run user:create -- <username> <password> [user|admin]
npm run user:list
npm run user:delete -- <username>

Admin users can open the in-app admin panel to:

  • inspect full user profiles (preferences, model catalog, memories, conversations, messages)
  • create/update/delete users

Production

npm run build                # builds React into dist/
NODE_ENV=production CHATFORGE_SECRET=your-secret node server/server.js

The Express server serves the built frontend from dist/ and the API on the same port (3069). Set NODE_ENV in systemd/OpenRC (or inline as above), not in .env files consumed by Vite.

Optional: Admin Self-Update Button

You can expose an admin-only update action in the UI that runs:

  1. stash local changes
  2. git pull --ff-only
  3. install/update dependencies
  4. rebuild
  5. unstash
  6. restart service

Enable it with env vars in your runtime service:

CHATFORGE_ENABLE_SELF_UPDATE=true
CHATFORGE_UPDATE_REPO_DIR=/path/to/chatforge
CHATFORGE_UPDATE_BUILD_CMD="npm run build"
CHATFORGE_UPDATE_INSTALL_DEPS=true
CHATFORGE_UPDATE_INSTALL_CMD="npm ci --include=dev"
CHATFORGE_UPDATE_INSTALL_FALLBACK_CMD="npm install --include=dev"
CHATFORGE_UPDATE_INSTALL_POLICY="if-needed"
CHATFORGE_UPDATE_INSTALL_TRIGGER_FILES="package.json,package-lock.json,npm-shrinkwrap.json,yarn.lock,pnpm-lock.yaml,bun.lock,bun.lockb"
CHATFORGE_UPDATE_RESTART_CMD="rc-service chatforge restart"
CHATFORGE_SYSTEM_STATUS_CMD="sudo /usr/bin/systemctl status chatforge.service"

If any step fails, the process stops and restart is not executed.

Backend via Docker Compose

Use the hardened backend-only compose stack:

# set a strong secret first (or place it in .env)
export CHATFORGE_SECRET='replace-with-a-long-random-secret'

docker compose -f docker-compose.backend.yml up -d --build
docker compose -f docker-compose.backend.yml logs -f --tail=100

Create the first user inside the containerized backend:

docker compose -f docker-compose.backend.yml exec chatforge-backend \
  node server/cli.js create-user <username> <password> [user|admin]

Notes:

  • Backend binds to 127.0.0.1:3069 by default.
  • SQLite data and tool sandbox are persisted in Docker volumes (chatforge_data, chatforge_sandbox).

Architecture

  • Frontend: React SPA (Vite)
  • Backend: Express + better-sqlite3
  • Auth: bcrypt passwords + optional TOTP/recovery codes + passkeys (WebAuthn), JWT in httpOnly cookie
  • LLM Proxy: Server-side streaming proxy to Anthropic/OpenAI/Gemini/VertexAI — API keys never reach the browser
  • Storage: SQLite with WAL mode, single file at ./data/chatforge.db

Native Passkey API (Flutter/Native Clients)

  • POST /api/auth/native/passkeys/authenticate/options
    • Input: optional username
    • Output: WebAuthn authentication options + challenge_id
  • POST /api/auth/native/passkeys/authenticate/verify
    • Input: challenge_id, WebAuthn response, and native device metadata (platform, device_name, etc.)
    • Output: native auth token bundle (access_token, refresh_token, session) matching /api/auth/native/login

Existing authenticated passkey management endpoints (/api/auth/passkeys list/register/delete) also work with Bearer access tokens from native clients.

Live Sync Policy API (Native + Web)

Authenticated clients can control granular live WebSocket synchronization categories:

  • GET /api/sync/live-policy
    • Output: { policy }
  • PUT /api/sync/live-policy
    • Input: partial or full policy object (can also send the object directly as request body)
    • Output: { ok: true, policy, updated_at }

Policy shape:

  • enabled: global on/off for live WebSocket updates
  • folders.state: synchronize folder open/closed state
  • folders.order: synchronize folder ordering and pin state
  • folders.names: synchronize folder rename changes
  • folders.lifecycle: synchronize folder create/delete
  • chats.order: synchronize chat ordering and pin state
  • chats.names: synchronize chat rename changes
  • chats.lifecycle: synchronize chat create/delete
  • chats.context: synchronize chat context metadata changes (context_summary, context_cutoff_count)
  • chats.messages: synchronize chat message create/update/delete events
  • data.memories: synchronize memory create/update/delete
  • data.artifacts: synchronize artifact create/update/delete
  • app.ui: synchronize UI preference changes
  • app.settings: synchronize non-UI settings changes
  • app.modelCatalog: synchronize model catalog changes
  • app.encryptedSecrets: synchronize encrypted provider/agent sync metadata changes
  • app.security: synchronize account security state changes (auth devices, passkeys, TOTP)

Per-User Model Catalogs

  • Each account has its own provider/model catalog (Anthropic, OpenAI, Gemini, VertexAI).
  • Default model lists are preloaded with current model families and can be customized in Settings.
  • Add/remove model IDs in the UI; changes are persisted server-side for that user.
  • Each account can also save a default provider and default model, applied automatically at login.

Provider Reasoning Controls

  • OpenAI: configurable reasoning effort (used for Chat Completions / Responses where supported).
  • OpenAI Responses API: configurable reasoning summary mode (auto, concise, detailed) and request-level store: false to avoid provider-side response storage.
  • Anthropic: configurable thinking token budget (thinking.budget_tokens).
  • Gemini / VertexAI: configurable thinking level and thinking budget (generationConfig.thinkingConfig).
  • Conversation UI: optional reasoning-step visibility for providers that return reasoning/thought traces.

Encrypted Provider Config Sync

  • API keys and provider endpoints can be synced to the server as a client-side encrypted blob.
  • Encryption/decryption uses PBKDF2 (password-derived key) + AES-GCM in the browser.
  • On interactive login, ChatForge attempts to load and decrypt synced provider values with the entered password and hydrate local storage.
  • In Settings, use Sync Values To Server and Load Values From Server to push/pull while already logged in.

nginx Reverse Proxy

server {
    listen 443 ssl;
    server_name chat.n9x.co;

    location / {
        proxy_pass http://127.0.0.1:3069;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;            # important for SSE streaming
        proxy_cache off;
    }
}

OpenRC Service (Alpine/Gentoo)

Service files are provided under deploy/openrc/.

# install chatforge service + config
sudo cp deploy/openrc/chatforge /etc/init.d/chatforge
sudo chmod +x /etc/init.d/chatforge
sudo cp deploy/openrc/chatforge.confd.example /etc/conf.d/chatforge

# edit /etc/conf.d/chatforge and set CHATFORGE_SECRET
sudo rc-service chatforge start
sudo rc-update add chatforge default

Agent service (optional, if you use local tool execution):

sudo cp deploy/openrc/chatforge-agent /etc/init.d/chatforge-agent
sudo chmod +x /etc/init.d/chatforge-agent
sudo cp deploy/openrc/chatforge-agent.confd.example /etc/conf.d/chatforge-agent

# edit /etc/conf.d/chatforge-agent (set CHATFORGE_AGENT_TOKEN and allowed CHATFORGE_AGENT_CORS origins;
# include your hosted ChatForge origin, for example https://chat.example.com, if the UI is not served from localhost)
# optional: set CHATFORGE_AGENT_EXTRA_ARGS="--allow-private-fetch true" only if you explicitly need local/private URL fetching
sudo rc-service chatforge-agent start
sudo rc-update add chatforge-agent default

SearXNG via Docker Compose

Compose files are under deploy/searxng/.

cd deploy/searxng
# set a strong server.secret_key in settings.yml first
docker compose up -d
docker compose logs -f --tail=100

By default it binds to 127.0.0.1:8888 and matches CHATFORGE_SEARXNG=http://127.0.0.1:8888.

Environment Variables

Variable Default Description
CHATFORGE_PORT 3069 Server port
CHATFORGE_DB ./data/chatforge.db SQLite path
CHATFORGE_SECRET unset JWT signing secret (required in production, otherwise an ephemeral secret is generated)
CHATFORGE_JWT_EXPIRY 7d JWT lifetime
CHATFORGE_TOTP_ISSUER ChatForge Display name used in authenticator apps during TOTP setup
CHATFORGE_PASSKEY_RP_NAME ChatForge Relying Party display name shown in passkey prompts
CHATFORGE_PASSKEY_RP_ID empty Optional RP ID override for passkey ceremonies (defaults to request host without port)
CHATFORGE_PASSKEY_ALLOWED_ORIGINS empty Optional comma-separated allowed origins for passkey ceremonies (defaults to inferred same-origin values; can include native origins like android:apk-key-hash:...)
CHATFORGE_PASSKEY_ANDROID_PACKAGE_NAME empty Optional Android package name used to publish /.well-known/assetlinks.json for native passkey providers
CHATFORGE_PASSKEY_ANDROID_SHA256_CERT_FINGERPRINTS empty Optional comma-separated Android cert SHA-256 fingerprints (AA:BB:...). When set with package name, ChatForge publishes Digital Asset Links and auto-adds matching android:apk-key-hash:... passkey origins
CHATFORGE_PASSKEY_CHALLENGE_TTL_SEC 300 Passkey challenge lifetime in seconds
CHATFORGE_TRUST_PROXY false Express trust proxy setting (1, true, or trusted proxy keyword)
CHATFORGE_ALLOWED_ORIGINS empty Optional comma-separated origins allowed for mutating /api requests. Empty means same-origin only.
CHATFORGE_ALLOW_WS_QUERY_TOKEN_AUTH false Allow Bearer auth via WebSocket query params (access_token/token) on /api/sync/ws. Keep disabled to avoid token leakage in URL logs/history.
NODE_ENV unset Set to production in the runtime service env (not in .env) for secure cookies
CHATFORGE_ENABLE_LLM_PROXY true Enable /api/stream server-side model proxy endpoint. Set false to disable model requests from UI
CHATFORGE_ALLOW_PRIVATE_ENDPOINTS true outside production, false in production Allow proxying LLM traffic to localhost/private IPs
CHATFORGE_ALLOWED_CUSTOM_LLM_HOSTS empty Optional comma-separated public hostnames allowed for custom LLM endpoints while keeping arbitrary endpoints blocked
CHATFORGE_ALLOW_CUSTOM_LLM_ENDPOINTS false Allow arbitrary public LLM endpoint hosts in /api/stream. Prefer CHATFORGE_ALLOWED_CUSTOM_LLM_HOSTS when possible
CHATFORGE_ALLOW_PRIVATE_FETCH_URLS false Allow fetch_url tool requests to localhost/private IPs
CHATFORGE_CMD_TIMEOUT 30000 Default run_command timeout in ms (can be overridden per-user at runtime in Tools settings)
CHATFORGE_ENABLE_SELF_UPDATE false Allow admin UI to run self-update pipeline
CHATFORGE_UPDATE_REPO_DIR current working dir Repository directory for update commands
CHATFORGE_UPDATE_BUILD_CMD npm run build Build command run during admin update
CHATFORGE_UPDATE_INSTALL_DEPS true Install dependencies before build during admin update
CHATFORGE_UPDATE_INSTALL_CMD npm ci --include=dev Primary dependency-install command run during admin update
CHATFORGE_UPDATE_INSTALL_FALLBACK_CMD npm install --include=dev Fallback install command if primary install fails
CHATFORGE_UPDATE_INSTALL_POLICY if-needed if-needed skips install unless dependency trigger files changed in pulled commits; always installs every update
CHATFORGE_UPDATE_INSTALL_TRIGGER_FILES package.json,package-lock.json,npm-shrinkwrap.json,yarn.lock,pnpm-lock.yaml,bun.lock,bun.lockb Comma-separated files that trigger dependency install in if-needed mode
CHATFORGE_UPDATE_RESTART_CMD empty Service restart command (required if self-update enabled)
CHATFORGE_SYSTEM_STATUS_CMD empty Command for admin service-status display

UI Tour (Screenshots)

Authentication

Login

Login screen

Username/password sign-in before loading synced user preferences and chats.

Core Chat

Main Chat (Light Theme)

Main chat UI light theme

Primary conversation surface with model/provider controls, streaming responses, and message actions.

Conversation Example

Conversation example

Longer back-and-forth conversation showing grouped turns in one thread.

Markdown Rendering (1)

Markdown chat example 1

Markdown Rendering (2)

Markdown chat example 2

Markdown Rendering (3)

Markdown chat example 3

Formatted markdown output across lists, blocks, and rich response content.

Inline LaTeX

Inline latex chat

Math rendering in chat responses.

High-Contrast Chat

High contrast chat view

High-contrast chat mode for improved visual accessibility.

Search With Content Match

Search content match

Search inside chats to quickly jump to conversations/messages containing the query.

Search With No Content Match

Search no content match

Empty/no-match state behavior when no messages match the query.

Productivity Features

Commands

Chat commands

Built-in command palette/view for quick in-chat actions and workflow shortcuts.

Sidebar and Organization

Sidebar Folders (Drag and Drop)

Sidebar folders

Conversation sidebar for organizing chats and folders. Supports collapsing folders, drag-and-drop reordering for folders and threads, and server-side persistence of folder state and order.

Settings and Configuration

Settings (Collapsed)

Settings collapsed

Main settings entry point for providers, defaults, preferences, and account-level options.

General Settings

General settings

Provider Settings

Provider settings

Model Catalog Settings

Model catalog settings

Per-user provider/model catalogs can be customized directly in Settings.

Default Model Selector

Default model selector

Reasoning Settings

Reasoning settings

Sampling Settings

Sampling settings

System Prompt Settings

System prompt settings

Tools Settings

Tools settings

Memories Settings

Memories settings

Encrypted Provider Sync

Encrypted provider sync

Artifacts Settings

Artifacts settings

Self Password Change

Self password change

Admin and Operations

Admin Panel Overview

General admin panel

Top-level admin panel with user, database, and service controls.

Admin Controls

Admin controls

Operational actions for service status, update, and restart workflows.

Admin User Management

Admin user management

User list with role management, password reset, and account lifecycle actions.

Admin User Detail

Admin user detail

Full per-user snapshot including preferences, memories, conversations, and messages.

Admin SQL Query Runner

Admin SQL query runner

In-app SQL runner for read/admin queries with typed parameter input.

Admin Query Runner Example

Admin query runner example

Example query execution and result rendering in the admin database panel.

Default Themes

Blue Theme

Blue theme

Green Theme

Green theme

Purple Theme

Purple theme

Yellow Theme

Yellow theme

Theme customization note:

  • You are not limited to presets. Any valid hex color is supported (for example #1f6feb, #22c55e, #f59e0b).
Description
A vibe coded web UI for various LLM providers
Readme 7 MiB
Languages
JavaScript 99.3%
CSS 0.6%
Shell 0.1%