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
@pathmentions 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_summaryandcontext_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)
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:
- stash local changes
git pull --ff-only- install/update dependencies
- rebuild
- unstash
- 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:3069by 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
- Input: optional
POST /api/auth/native/passkeys/authenticate/verify- Input:
challenge_id, WebAuthnresponse, and nativedevicemetadata (platform,device_name, etc.) - Output: native auth token bundle (
access_token,refresh_token,session) matching/api/auth/native/login
- Input:
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 }
- Output:
PUT /api/sync/live-policy- Input: partial or full
policyobject (can also send the object directly as request body) - Output:
{ ok: true, policy, updated_at }
- Input: partial or full
Policy shape:
enabled: global on/off for live WebSocket updatesfolders.state: synchronize folder open/closed statefolders.order: synchronize folder ordering and pin statefolders.names: synchronize folder rename changesfolders.lifecycle: synchronize folder create/deletechats.order: synchronize chat ordering and pin statechats.names: synchronize chat rename changeschats.lifecycle: synchronize chat create/deletechats.context: synchronize chat context metadata changes (context_summary,context_cutoff_count)chats.messages: synchronize chat message create/update/delete eventsdata.memories: synchronize memory create/update/deletedata.artifacts: synchronize artifact create/update/deleteapp.ui: synchronize UI preference changesapp.settings: synchronize non-UI settings changesapp.modelCatalog: synchronize model catalog changesapp.encryptedSecrets: synchronize encrypted provider/agent sync metadata changesapp.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-levelstore: falseto 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 ServerandLoad Values From Serverto 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
Username/password sign-in before loading synced user preferences and chats.
Core Chat
Main Chat (Light Theme)
Primary conversation surface with model/provider controls, streaming responses, and message actions.
Conversation Example
Longer back-and-forth conversation showing grouped turns in one thread.
Markdown Rendering (1)
Markdown Rendering (2)
Markdown Rendering (3)
Formatted markdown output across lists, blocks, and rich response content.
Inline LaTeX
Math rendering in chat responses.
High-Contrast Chat
High-contrast chat mode for improved visual accessibility.
Search
Search With Content Match
Search inside chats to quickly jump to conversations/messages containing the query.
Search With No Content Match
Empty/no-match state behavior when no messages match the query.
Productivity Features
Commands
Built-in command palette/view for quick in-chat actions and workflow shortcuts.
Sidebar and Organization
Sidebar Folders (Drag and Drop)
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)
Main settings entry point for providers, defaults, preferences, and account-level options.
General Settings
Provider Settings
Model Catalog Settings
Per-user provider/model catalogs can be customized directly in Settings.
Default Model Selector
Reasoning Settings
Sampling Settings
System Prompt Settings
Tools Settings
Memories Settings
Encrypted Provider Sync
Artifacts Settings
Self Password Change
Admin and Operations
Admin Panel Overview
Top-level admin panel with user, database, and service controls.
Admin Controls
Operational actions for service status, update, and restart workflows.
Admin User Management
User list with role management, password reset, and account lifecycle actions.
Admin User Detail
Full per-user snapshot including preferences, memories, conversations, and messages.
Admin SQL Query Runner
In-app SQL runner for read/admin queries with typed parameter input.
Admin Query Runner Example
Example query execution and result rendering in the admin database panel.
Default Themes
Blue Theme
Green Theme
Purple Theme
Yellow Theme
Theme customization note:
- You are not limited to presets. Any valid hex color is supported (for example
#1f6feb,#22c55e,#f59e0b).


































