Chatbot integration in your business website. Self hosted, free forever!
Bolnee-Chat is a self-hosted RAG chatbot platform. Create a bot, add your website + PDFs as knowledge, pick any OpenAI-compatible provider (OpenRouter, OpenAI, Groq, Ollama, vLLM), and embed a 2-line snippet. Answers are grounded in your sources with citations, visitor chats are grouped and exportable, and everything runs on your infrastructure with SQLite.
Why Bolnee-Chat?
All Features
Also in the box:
- Knowledge management — lists
locator · type · status · error · datewith delete; append more via Add knowledge. - Avatar file storage — data URLs converted to
/api/public/avatar/:id(2MB limit,data/avatars/). - Encrypted provider keys — per-bot
apiKey/baseUrl/modelvia AES-256-GCM, never exposed in snippet. - Dark-mode console —
#020617bg,#1e293bcards,slate-800inputs, no white surfaces.
Workflow
Requirements
| Requirement | Details |
|---|---|
| OS | macOS, Linux, Windows (WSL) |
| Runtime | Node.js 18+ |
| Package Manager | npm |
| Python | 3.10+ (crawler, optional but recommended) |
| Python Deps | aiohttp, beautifulsoup4, lxml, requests, brotli (+ playwright for JS-heavy sites) |
Tech Stack
Installation
# Clone git clone https://github.com/AniketWathore/bolnee-chat.git cd bolnee-chat # Env cp .env.example .env # edit .env — simplest self-hosted: # DISABLE_AUTH=true # JWT_SECRET=change-me-32-chars # LLM_BASE_URL=https://openrouter.ai/api/v1 # optional global fallback # LLM_API_KEY=sk-or-v1-... # optional global fallback # LLM_MODEL=inclusionai/ling-3.0-flash-fin:free # Install npm install # Python crawler deps (optional) pip install aiohttp beautifulsoup4 lxml requests brotli # Verify npm run lint # tsc --noEmit npm run build # vite + esbuild → dist/ npm run dev # http://localhost:3000 (auto-fallback to 3001 if busy)
SQLite is created at data/bolnee.db (git-ignored). Crawled sites → data/{chatbotId}_website.json, chunks in SQLite.
Usage — Dashboard Flow
- Create chatbot —
+ New chatbot→ name + avatar (preview ≤2MB). - Add knowledge — enter
https://your-site.comand/or upload PDF/TXT/MD/DOCX/FAQ → statusqueued → indexed. - Configure provider — choose provider →
Fetch models→ pick model → Save. Keys encrypted, not in embed. - Embed — copy snippet from
Overview:
<script> window.BotConfig = { botName: "Customer Bot", avatar: "https://your-domain/api/public/avatar/BOT_ID", chatUrl: "https://your-domain/api/public/chat/BOT_ID", accentColor: "#111111", greeting: "Hi! How can I help?", theme: "dark" }; </script> <script src="https://your-domain/chatbot-widget.js" async></script>
Paste before </body>. Widget stores VISITOR_ID + chat history in localStorage; greeting shows once.
Bot Console Reference
| Tab | What it does |
|---|---|
| Overview | Live status, messages/users/sources, embed copy (src/components/ChatbotDashboard.tsx:244) |
| Appearance | name/avatar/accent/theme/greeting + live preview (src/components/ChatbotDashboard.tsx:282) |
| Chats | Grouped by visitor → date, CSV/JSON/PDF, Refresh (src/components/ChatbotDashboard.tsx:371) |
| Knowledge | Sources + Add knowledge wizard (src/components/ChatbotDashboard.tsx:449) |
| Settings | provider/model/baseUrl/apiKey, defaultMessage/fallbackMessage, Danger zone delete (src/components/ChatbotDashboard.tsx:493) |
API Reference
| Method | Path | Description |
|---|---|---|
POST |
/api/chatbots |
Create bot |
GET |
/api/chatbots |
List bots |
PATCH |
/api/chatbots/:id |
Appearance + provider (name/avatar/accent/theme/greeting + provider/model/apiKey/baseUrl) |
GET |
/api/chatbots/:id/appearance |
Get appearance (server.ts:728) |
GET |
/api/chatbots/:id/messages?limit=200 |
Grouped messages |
GET |
/api/chatbots/:id/stats |
total/users |
GET |
/api/chatbots/:id/messages/export?format=csv|json |
Export |
GET |
/api/knowledge/sources?chatbotId=ID |
List sources |
POST |
/api/knowledge/sources/:chatbotId |
Add URL ({url}) or file (multipart) → queued |
DELETE |
/api/knowledge/sources/:sourceId?chatbotId=ID |
Delete source + chunks |
POST |
/api/public/chat/:chatbotId |
SSE chat {message, visitorId} → data: {token|error|sources} + data: [DONE] |
GET |
/api/public/knowledge/:chatbotId |
Public knowledge (cached) |
GET |
/api/public/avatar/:chatbotId |
Avatar file or redirect |
POST |
/api/providers/models |
List models for provider/baseUrl/apiKey |
GET |
/api/stats |
Global totalMessages/activeSessions |
Streaming: public/chatbot-widget.js:311 reads SSE via getReader(), falls back to text() + SW bypass for locked streams. Visitor grouping via X-Visitor-Id (VISITOR_ID in localStorage).
File Reference
| Path | Role |
|---|---|
server.ts |
Express + Vite dev, auth (DISABLE_AUTH), ingestion, RAG, SSE chat (server.ts:282) |
server/db.ts |
SQLite (better-sqlite3) — chatbots/sources/chunks/messages, getChatbotAppearance |
server/ingestion.ts + crawler/run_crawler_for_bolnee.py |
Crawl → /data/{id}_website.json → chunks |
crawler/crawler.py |
Same-origin crawl, robots.txt, h1/h2/p/li extraction, sitemap + homepage |
public/chatbot-widget.js |
Embeddable widget — BotConfig.chatUrl, accent, greeting, theme, VISITOR_ID, history |
src/components/ChatbotDashboard.tsx |
Tabs: overview/appearance/chats/knowledge/settings, embedCode with theme |
src/components/KnowledgeSection.tsx |
4-step wizard: Knowledge → Provider → Processing (polls status) → Embed |
src/components/Overview.tsx |
Stats + grid of 4 bots + View all |
src/components/BotCreationWizard.tsx |
Name + avatar upload (2MB limit) |
src/index.css |
Dark-mode tokens (--color-bg #020617, --color-card #1e293b) |
vercel.json / wrangler.toml |
Hosting rewrites, bucket = "./dist", DISABLE_AUTH |
Hosting
Dashboard is fully API-driven (/api/* relative) and auto-configures window.location.origin for embed URLs.
Vercel:
# env DISABLE_AUTH=true # JWT_SECRET not required for simple mode # vercel.json already: rewrites /api/:path* → /api, /(.*) → /index.html, outputDirectory: dist npm run build && vercel --prod
Cloudflare Pages / Workers:
# wrangler.toml: bucket = "./dist", DISABLE_AUTH=true npm run build wrangler pages deploy dist # or: npx wrangler deploy
Chat endpoint streams SSE; for external sites use public https:// chatUrl (not localhost).
Configuration
| Env | Description | Default |
|---|---|---|
DISABLE_AUTH / VITE_DISABLE_AUTH |
No-login console (single-tenant) | false |
JWT_SECRET |
Auth signing key (16+ chars, prod required) | development-secret-change-me |
LLM_BASE_URL / OPENROUTER_API_KEY / NVIDIA_API_KEY |
Global provider fallback (per-bot settings take precedence) | — |
LLM_API_KEY |
Global API key fallback | — |
LLM_MODEL |
Global model fallback (e.g. openai/gpt-4o-mini) |
gpt-4o-mini |
PORT |
Server port (auto-fallback +1 if busy) |
3000 |
.env.example documents all.
Troubleshooting
| Symptom | Fix |
|---|---|
Port 3000 in use → 3001 and embed fails on external site |
Embed uses window.location.origin; regenerate after restart or deploy to public URL (localhost embed is https:// mixed-content) |
getReader locked / ReadableStream locked |
Bump public/sw.js to v3 skips POST /api/public/chat; hard-refresh to update SW |
Model 404 / 402 |
Use Fetch models → pick :free (e.g. inclusionai/ling-3.0-flash-fin:free) or add credits |
404 knowledge/avatar |
Ensure data/bolnee.db exists and bot id matches data/{id}_website.json |
| Avatar too large | PNG/JPG/WEBP ≤2MB; data URLs auto-converted to /api/public/avatar/:id |
| Greeting repeats on open/close | Fixed in public/chatbot-widget.js:115 — saveHistory/loadHistory in localStorage + engine=true guard; clear bolnee_msgs_* to reset |
Verification Checklist
npm run lint # tsc --noEmit clean npm run build # vite + esbuild → dist/ npm run dev # http://localhost:3000 # Manual: # 1. Create bot → avatar preview → Save appearance → preview updates # 2. Add knowledge: URL + PDF → status queued → indexed # 3. Provider → Fetch models → pick :free → Save # 4. Overview → Copy embed → paste in plain HTML → widget loads, greeting once, close/open keeps history, dark/light/auto themes correct # 5. Chats → grouped by visitor → CSV/JSON/PDF export # 6. Settings → default/fallback messages → Chat without sources returns fallback
License
Distributed under the MIT License. See LICENSE for more information.









