AniketWathore/bolnee-chat

Chatbot Integration in your Business website. Self hosted, free forever!

36

stars

43

commits

TypeScript

primary language

Sep 3, 2026

updated

README

Bolnee Bolnee-Chat

Chatbot integration in your business website. Self hosted, free forever!

GitHub License: MIT Release Node 18+ Python 3.10+

Bolnee Dashboard — Your Chatbots

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?

Self-Hosted & Free Forever

No vendor lock-in, no per-message billing. Run on your server, Vercel or Cloudflare Pages — SQLite, no external DB.

RAG-Grounded Answers

Crawls your site + ingests PDFs/TXT/MD/DOCX, chunks to SQLite FTS and builds grounded prompts. Sources are cited, fallback is configurable.

2-Line Embed, Any Stack

Copy window.BotConfig + chatbot-widget.js and paste before </body>. Works on any site, accent/theme/greeting live via dashboard.


All Features

Bot Overview — Stats & Snippet

Per-bot: Live status, message/user counts, creation date, source count, embed snippet with botName/avatar/chatUrl/accent/greeting/theme.

Bot Overview

Appearance — Live Preview

Edit bot name, avatar (upload/preview), accent/background colour, theme light/dark/auto, greeting. Live preview; saved via PATCH /api/chatbots/:id.

Appearance

Chats — Grouped by Visitor

Grouped by visitorIdIP, then by date, chronological. Refresh + download CSV (Excel) / JSON / PDF (print). Active sessions = distinct visitors last 5m.

Chats

Settings — Provider, Prompts, Danger Zone

provider/model/baseUrl/apiKey, default message (pre-first-turn), fallback message (no sources matched), delete bot + chats + sources + chunks.

Settings

Widget — Floating, Theme-Aware, Persistent

Bottom-right bubble → sliding window (360×520, 75vh mobile). Header with accent + avatar, typing dots, SSE streaming, sources cited, VISITOR_ID + history in localStorage so greeting shows once and chats survive close/open. Theme light/dark/auto (#0f172a / #fff).

Widget

Also in the box:

  • Knowledge management — lists locator · type · status · error · date with 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/model via AES-256-GCM, never exposed in snippet.
  • Dark-mode console#020617 bg, #1e293b cards, slate-800 inputs, no white surfaces.

Workflow

1. Create Chatbot — Brand in Seconds

Name + avatar (PNG/JPG/WEBP ≤2MB, preview) → stored as /api/public/avatar/:id and shown in the widget header.

Create Chatbot

2. Add Knowledge — Website + Files

URL-only, files-only, or both. Same-origin crawler (crawler/crawler.py) respects robots.txt, extracts h1/h2/p/li, dedups, saves data/{chatbotId}_website.json. Status: queued → crawling → parsing → indexing → indexed.

Add Knowledge

3. Configure Provider — Any OpenAI-Compatible API

Pick provider → Base URL auto-fills → paste API key → Fetch models lists live models (prioritizes :free for OpenRouter). Keys stored encrypted (AES-256-GCM), never in embed.

Configure Provider

4. Embed — 2 Lines

Copy from Overview → Embed code or Knowledge → Step 4. Auto-configures origin via window.location.origin; widget loads via chatbot-widget.js with SSE streaming.

Embed Code

Requirements

RequirementDetails
OSmacOS, Linux, Windows (WSL)
RuntimeNode.js 18+
Package Managernpm
Python3.10+ (crawler, optional but recommended)
Python Depsaiohttp, beautifulsoup4, lxml, requests, brotli (+ playwright for JS-heavy sites)

Tech Stack

React Vite TypeScript Tailwind Express SQLite Python


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

  1. Create chatbot+ New chatbot → name + avatar (preview ≤2MB).
  2. Add knowledge — enter https://your-site.com and/or upload PDF/TXT/MD/DOCX/FAQ → status queued → indexed.
  3. Configure provider — choose provider → Fetch models → pick model → Save. Keys encrypted, not in embed.
  4. 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

TabWhat it does
OverviewLive status, messages/users/sources, embed copy (src/components/ChatbotDashboard.tsx:244)
Appearancename/avatar/accent/theme/greeting + live preview (src/components/ChatbotDashboard.tsx:282)
ChatsGrouped by visitor → date, CSV/JSON/PDF, Refresh (src/components/ChatbotDashboard.tsx:371)
KnowledgeSources + Add knowledge wizard (src/components/ChatbotDashboard.tsx:449)
Settingsprovider/model/baseUrl/apiKey, defaultMessage/fallbackMessage, Danger zone delete (src/components/ChatbotDashboard.tsx:493)

API Reference

MethodPathDescription
POST/api/chatbotsCreate bot
GET/api/chatbotsList bots
PATCH/api/chatbots/:idAppearance + provider (name/avatar/accent/theme/greeting + provider/model/apiKey/baseUrl)
GET/api/chatbots/:id/appearanceGet appearance (server.ts:728)
GET/api/chatbots/:id/messages?limit=200Grouped messages
GET/api/chatbots/:id/statstotal/users
GET/api/chatbots/:id/messages/export?format=csv|jsonExport
GET/api/knowledge/sources?chatbotId=IDList sources
POST/api/knowledge/sources/:chatbotIdAdd URL ({url}) or file (multipart) → queued
DELETE/api/knowledge/sources/:sourceId?chatbotId=IDDelete source + chunks
POST/api/public/chat/:chatbotIdSSE chat {message, visitorId}data: {token|error|sources} + data: [DONE]
GET/api/public/knowledge/:chatbotIdPublic knowledge (cached)
GET/api/public/avatar/:chatbotIdAvatar file or redirect
POST/api/providers/modelsList models for provider/baseUrl/apiKey
GET/api/statsGlobal 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

PathRole
server.tsExpress + Vite dev, auth (DISABLE_AUTH), ingestion, RAG, SSE chat (server.ts:282)
server/db.tsSQLite (better-sqlite3) — chatbots/sources/chunks/messages, getChatbotAppearance
server/ingestion.ts + crawler/run_crawler_for_bolnee.pyCrawl → /data/{id}_website.json → chunks
crawler/crawler.pySame-origin crawl, robots.txt, h1/h2/p/li extraction, sitemap + homepage
public/chatbot-widget.jsEmbeddable widget — BotConfig.chatUrl, accent, greeting, theme, VISITOR_ID, history
src/components/ChatbotDashboard.tsxTabs: overview/appearance/chats/knowledge/settings, embedCode with theme
src/components/KnowledgeSection.tsx4-step wizard: Knowledge → Provider → Processing (polls status) → Embed
src/components/Overview.tsxStats + grid of 4 bots + View all
src/components/BotCreationWizard.tsxName + avatar upload (2MB limit)
src/index.cssDark-mode tokens (--color-bg #020617, --color-card #1e293b)
vercel.json / wrangler.tomlHosting 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

EnvDescriptionDefault
DISABLE_AUTH / VITE_DISABLE_AUTHNo-login console (single-tenant)false
JWT_SECRETAuth signing key (16+ chars, prod required)development-secret-change-me
LLM_BASE_URL / OPENROUTER_API_KEY / NVIDIA_API_KEYGlobal provider fallback (per-bot settings take precedence)
LLM_API_KEYGlobal API key fallback
LLM_MODELGlobal model fallback (e.g. openai/gpt-4o-mini)gpt-4o-mini
PORTServer port (auto-fallback +1 if busy)3000

.env.example documents all.


Troubleshooting

SymptomFix
Port 3000 in use → 3001 and embed fails on external siteEmbed uses window.location.origin; regenerate after restart or deploy to public URL (localhost embed is https:// mixed-content)
getReader locked / ReadableStream lockedBump public/sw.js to v3 skips POST /api/public/chat; hard-refresh to update SW
Model 404 / 402Use Fetch models → pick :free (e.g. inclusionai/ling-3.0-flash-fin:free) or add credits
404 knowledge/avatarEnsure data/bolnee.db exists and bot id matches data/{id}_website.json
Avatar too largePNG/JPG/WEBP ≤2MB; data URLs auto-converted to /api/public/avatar/:id
Greeting repeats on open/closeFixed in public/chatbot-widget.js:115saveHistory/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.

Contributors

AniketWathore

43 commits

AniketWathore/bolnee-chat

Chatbot Integration in your Business website. Self hosted, free forever!

36

stars

43

commits

TypeScript

primary language

Sep 3, 2026

updated

README

Bolnee Bolnee-Chat

Chatbot integration in your business website. Self hosted, free forever!

GitHub License: MIT Release Node 18+ Python 3.10+

Bolnee Dashboard — Your Chatbots

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?

Self-Hosted & Free Forever

No vendor lock-in, no per-message billing. Run on your server, Vercel or Cloudflare Pages — SQLite, no external DB.

RAG-Grounded Answers

Crawls your site + ingests PDFs/TXT/MD/DOCX, chunks to SQLite FTS and builds grounded prompts. Sources are cited, fallback is configurable.

2-Line Embed, Any Stack

Copy window.BotConfig + chatbot-widget.js and paste before </body>. Works on any site, accent/theme/greeting live via dashboard.


All Features

Bot Overview — Stats & Snippet

Per-bot: Live status, message/user counts, creation date, source count, embed snippet with botName/avatar/chatUrl/accent/greeting/theme.

Bot Overview

Appearance — Live Preview

Edit bot name, avatar (upload/preview), accent/background colour, theme light/dark/auto, greeting. Live preview; saved via PATCH /api/chatbots/:id.

Appearance

Chats — Grouped by Visitor

Grouped by visitorIdIP, then by date, chronological. Refresh + download CSV (Excel) / JSON / PDF (print). Active sessions = distinct visitors last 5m.

Chats

Settings — Provider, Prompts, Danger Zone

provider/model/baseUrl/apiKey, default message (pre-first-turn), fallback message (no sources matched), delete bot + chats + sources + chunks.

Settings

Widget — Floating, Theme-Aware, Persistent

Bottom-right bubble → sliding window (360×520, 75vh mobile). Header with accent + avatar, typing dots, SSE streaming, sources cited, VISITOR_ID + history in localStorage so greeting shows once and chats survive close/open. Theme light/dark/auto (#0f172a / #fff).

Widget

Also in the box:

  • Knowledge management — lists locator · type · status · error · date with 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/model via AES-256-GCM, never exposed in snippet.
  • Dark-mode console#020617 bg, #1e293b cards, slate-800 inputs, no white surfaces.

Workflow

1. Create Chatbot — Brand in Seconds

Name + avatar (PNG/JPG/WEBP ≤2MB, preview) → stored as /api/public/avatar/:id and shown in the widget header.

Create Chatbot

2. Add Knowledge — Website + Files

URL-only, files-only, or both. Same-origin crawler (crawler/crawler.py) respects robots.txt, extracts h1/h2/p/li, dedups, saves data/{chatbotId}_website.json. Status: queued → crawling → parsing → indexing → indexed.

Add Knowledge

3. Configure Provider — Any OpenAI-Compatible API

Pick provider → Base URL auto-fills → paste API key → Fetch models lists live models (prioritizes :free for OpenRouter). Keys stored encrypted (AES-256-GCM), never in embed.

Configure Provider

4. Embed — 2 Lines

Copy from Overview → Embed code or Knowledge → Step 4. Auto-configures origin via window.location.origin; widget loads via chatbot-widget.js with SSE streaming.

Embed Code

Requirements

RequirementDetails
OSmacOS, Linux, Windows (WSL)
RuntimeNode.js 18+
Package Managernpm
Python3.10+ (crawler, optional but recommended)
Python Depsaiohttp, beautifulsoup4, lxml, requests, brotli (+ playwright for JS-heavy sites)

Tech Stack

React Vite TypeScript Tailwind Express SQLite Python


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

  1. Create chatbot+ New chatbot → name + avatar (preview ≤2MB).
  2. Add knowledge — enter https://your-site.com and/or upload PDF/TXT/MD/DOCX/FAQ → status queued → indexed.
  3. Configure provider — choose provider → Fetch models → pick model → Save. Keys encrypted, not in embed.
  4. 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

TabWhat it does
OverviewLive status, messages/users/sources, embed copy (src/components/ChatbotDashboard.tsx:244)
Appearancename/avatar/accent/theme/greeting + live preview (src/components/ChatbotDashboard.tsx:282)
ChatsGrouped by visitor → date, CSV/JSON/PDF, Refresh (src/components/ChatbotDashboard.tsx:371)
KnowledgeSources + Add knowledge wizard (src/components/ChatbotDashboard.tsx:449)
Settingsprovider/model/baseUrl/apiKey, defaultMessage/fallbackMessage, Danger zone delete (src/components/ChatbotDashboard.tsx:493)

API Reference

MethodPathDescription
POST/api/chatbotsCreate bot
GET/api/chatbotsList bots
PATCH/api/chatbots/:idAppearance + provider (name/avatar/accent/theme/greeting + provider/model/apiKey/baseUrl)
GET/api/chatbots/:id/appearanceGet appearance (server.ts:728)
GET/api/chatbots/:id/messages?limit=200Grouped messages
GET/api/chatbots/:id/statstotal/users
GET/api/chatbots/:id/messages/export?format=csv|jsonExport
GET/api/knowledge/sources?chatbotId=IDList sources
POST/api/knowledge/sources/:chatbotIdAdd URL ({url}) or file (multipart) → queued
DELETE/api/knowledge/sources/:sourceId?chatbotId=IDDelete source + chunks
POST/api/public/chat/:chatbotIdSSE chat {message, visitorId}data: {token|error|sources} + data: [DONE]
GET/api/public/knowledge/:chatbotIdPublic knowledge (cached)
GET/api/public/avatar/:chatbotIdAvatar file or redirect
POST/api/providers/modelsList models for provider/baseUrl/apiKey
GET/api/statsGlobal 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

PathRole
server.tsExpress + Vite dev, auth (DISABLE_AUTH), ingestion, RAG, SSE chat (server.ts:282)
server/db.tsSQLite (better-sqlite3) — chatbots/sources/chunks/messages, getChatbotAppearance
server/ingestion.ts + crawler/run_crawler_for_bolnee.pyCrawl → /data/{id}_website.json → chunks
crawler/crawler.pySame-origin crawl, robots.txt, h1/h2/p/li extraction, sitemap + homepage
public/chatbot-widget.jsEmbeddable widget — BotConfig.chatUrl, accent, greeting, theme, VISITOR_ID, history
src/components/ChatbotDashboard.tsxTabs: overview/appearance/chats/knowledge/settings, embedCode with theme
src/components/KnowledgeSection.tsx4-step wizard: Knowledge → Provider → Processing (polls status) → Embed
src/components/Overview.tsxStats + grid of 4 bots + View all
src/components/BotCreationWizard.tsxName + avatar upload (2MB limit)
src/index.cssDark-mode tokens (--color-bg #020617, --color-card #1e293b)
vercel.json / wrangler.tomlHosting 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

EnvDescriptionDefault
DISABLE_AUTH / VITE_DISABLE_AUTHNo-login console (single-tenant)false
JWT_SECRETAuth signing key (16+ chars, prod required)development-secret-change-me
LLM_BASE_URL / OPENROUTER_API_KEY / NVIDIA_API_KEYGlobal provider fallback (per-bot settings take precedence)
LLM_API_KEYGlobal API key fallback
LLM_MODELGlobal model fallback (e.g. openai/gpt-4o-mini)gpt-4o-mini
PORTServer port (auto-fallback +1 if busy)3000

.env.example documents all.


Troubleshooting

SymptomFix
Port 3000 in use → 3001 and embed fails on external siteEmbed uses window.location.origin; regenerate after restart or deploy to public URL (localhost embed is https:// mixed-content)
getReader locked / ReadableStream lockedBump public/sw.js to v3 skips POST /api/public/chat; hard-refresh to update SW
Model 404 / 402Use Fetch models → pick :free (e.g. inclusionai/ling-3.0-flash-fin:free) or add credits
404 knowledge/avatarEnsure data/bolnee.db exists and bot id matches data/{id}_website.json
Avatar too largePNG/JPG/WEBP ≤2MB; data URLs auto-converted to /api/public/avatar/:id
Greeting repeats on open/closeFixed in public/chatbot-widget.js:115saveHistory/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.

Contributors

AniketWathore

43 commits

Languages

TypeScript

63.7%

Python

23.1%

JavaScript

9.4%

Shell

3.0%