Open-source multi-tenant RAG chatbot platform — train AI on your content, deploy across web + Telegram + WhatsApp
Documentation · Report a bug · Request a feature · Discussions
The shortest path from "we have docs" to "we have an AI support bot on every channel." Sign up → upload PDFs/URLs → embed the widget on your site OR add a Telegram/WhatsApp bot → done.
- ✨ Highlights
- 🧠 What ships today
- 🛠 Tech stack
- 🚀 Quick start (5 minutes)
- 📨 Channels — Web, Telegram, WhatsApp
- 🗄 Database schema
- ⚙️ Configuration
- 🔒 Security
- 📦 Deploy to production
- 🤝 Contributing
- 💛 Sponsors
- 📜 License
Everything below is shipped, tested, deployable today — not a roadmap.
| Feature | |
|---|---|
| 🧠 | 5 LLM providers — OpenAI · Claude · Gemini · Deepseek · Ollama (set with one env var) |
| 📚 | 9 source types — PDF · DOCX · XLSX · JSON · Markdown · TXT · URLs · sitemap.xml crawl · Q&A pairs |
| 🌐 | 3 channels — Embed widget (3.2 KB Shadow-DOM) · Telegram (private + groups) · WhatsApp Cloud API |
| 🧬 | Cross-session per-user memory — same-visitor observations recalled across conversations, pgvector-native, no external service |
| 💬 | Conversation admin — search, filter, flag, bulk delete, JSON export across every channel |
| 🎓 | Promote-to-training — one click turns any good customer Q&A into a permanent training source |
| 🔐 | Multi-tenant isolation — server-side enforced, adversarially tested |
| 🪪 | Admin session security — per-device login list, revoke, audit log, password-change cascade |
| 📊 | Per-bot analytics — top questions + content gaps + 14-day chart |
| 💳 | Stripe billing — plans, hosted checkout, customer portal, webhook-synced |
| 🎨 | Vercel-grade UI — Tailwind + shadcn, light/dark, mobile-first, JSON-LD SEO |
- 9 source types: PDF · DOCX · XLSX · JSON · Markdown · Plain text · URLs · sitemap.xml crawl (up to 200 URLs per import) · Q&A pairs
- Drag-and-drop multi-file upload with per-file progress
- Preview drawer — see every chunk + token count + extracted text
- Inline editing for text / Markdown / JSON / Q&A → auto re-ingestion
- Search, filter, bulk delete, retry-on-failed
- Promote-to-training from any customer conversation
- Chat:
CHAT_PROVIDER=openai|anthropic|google|deepseek|ollama - Embeddings:
EMBEDDING_PROVIDER=openai|google|ollama - Mix providers (e.g., Ollama chat + OpenAI embeddings)
- Fully local with Ollama — no API key, $0 OpenAI bill
- Web widget — one
<script>tag → Shadow DOM, 3.2 KB gzipped, streaming SSE, citation chips, per-bot end-user cookie, CORS open, Redis rate limits - Telegram — paste a
@BotFathertoken, we register the webhook. Private + groups. Group mode:all/mention/reply. Long answers split at sentence boundaries. - WhatsApp Cloud API — Meta Business setup, signature-verified webhook (
X-Hub-Signature-256), 1-to-1 conversations (Meta doesn't allow bots in WhatsApp groups) - All channels share the same RAG pipeline, conversation admin, plan caps.
- Every chat across every channel is admin-manageable
- Full transcript with IP, device, referrer, duration, citation counts, latency
- Search by message content, filter by date range + flag, bulk delete, archive, JSON export
- Live stats: active 24h, active 7d, unique end-users, avg messages per session
- Flag:
review/star/abuse/spam - Add to training on any assistant message → editable, idempotent, reversible
- Same
endUserIdacross sessions (cookie · Telegram chat · WhatsApp phone) — one profile row per (bot, visitor) - After each turn, an extractor call distills 0–5 durable facts from the exchange and stores them as embedded observations (pgvector, HNSW cosine index)
- On the next turn, recall pulls top-K by similarity + N most-recent and prepends an About this returning visitor block to the system prompt — no query-time LLM call, no external service
- Admin sees the memory on the conversation detail page: profile fields, tag list, and every stored observation with per-row delete (privacy / GDPR)
- Empty-state for new visitors: no block prepended, zero overhead
- Write path is fire-and-forget so extraction latency never lands in the user's response time
- Per-device login list (
/account/sessions) with revoke + "Sign out everywhere else" - Password changes auto-revoke other sessions
- Full security audit log (sign-in, sign-out, password / email / profile / session events)
- bcrypt + JWT sessions, edge-safe middleware
- Plans + usage caps (bots, documents, storage, messages/month) — server-side enforced
- Stripe-backed Hosted Checkout + customer portal + webhook-synced plan state
- Per-bot analytics: top questions + content gaps (questions with zero citations)
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 15 (App Router) | RSC, server actions, edge middleware, single deploy unit |
| Language | TypeScript (strict) | Types catch bugs before runtime |
| DB | Postgres 16 + pgvector | One database for OLTP + vectors; HNSW indexes fast at SaaS scale |
| ORM | Drizzle | Type-safe, no codegen runtime, supports vector columns natively |
| Auth | Auth.js v5 | Credentials + Google + custom session tracking |
| Queue | BullMQ + Redis | Reliable background ingestion + rate limiting |
| Storage | MinIO (dev) / S3 (prod) | Original documents live outside the DB |
| LLM | 5 providers, swappable | OpenAI / Claude / Gemini / Deepseek / Ollama |
| UI | Tailwind + shadcn/ui | Vercel-grade aesthetic, mobile-first |
| Widget | Vite + Shadow DOM | Framework-free, 3.2 KB gzipped |
| Billing | Stripe | Hosted Checkout + customer portal + webhook |
| Tests | Vitest | Fast unit + integration tests against real Postgres/Redis/MinIO |
- Node.js 22+ and npm 10+
- Docker with Compose v2 (or OrbStack on macOS)
- An OpenAI / Claude / Gemini / Deepseek key, or Ollama running locally
# 1. Clone + install
git clone https://github.com/unusdon/ai-chatbot-saas.git
cd ai-chatbot-saas
npm install
# 2. Configure environment
cp .env.example .env.local
# Generate AUTH_SECRET:
openssl rand -base64 32
# Paste the output into AUTH_SECRET in .env.local
# 3. Start Postgres (+ pgvector), Redis, MinIO
docker compose up -d
# 4. Run database migrations (auto-enables pgvector extension)
npm run db:migrate
# 5. Start the dev server
npm run dev
# 6. In a second terminal: start the ingest worker
npm run workerOpen http://localhost:3000, click Get started, create an account, and you're in.
npm run typecheck # tsc --noEmit
npm run lint # next lint
npm run test # vitest (64 tests)ollama serve
ollama pull llama3.2 nomic-embed-textIn .env.local:
CHAT_PROVIDER=ollama
EMBEDDING_PROVIDER=ollamaRestart npm run dev + npm run worker. You're now running fully local.
Open the dashboard → pick a bot → Channels tab. Each channel is independent; the same bot can serve all three.
<script
src="https://your-app.com/widget.js"
data-bot-key="bot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
data-title="Ask Acme"
data-accent="#7c3aed"
defer
></script>3.2 KB gzipped · Shadow DOM (CSS-isolated) · streaming · stable end-user cookie · CORS open · Redis-backed rate limits per key + per IP.
- DM
@BotFather→/newbot→ copy token - Dashboard → Channels → Telegram → paste token + pick group mode → Connect
- Bot lives on Telegram in seconds. Group mode:
all/mention/reply(defaultmention)
- Set up a Meta Business app → add WhatsApp product → get Phone Number ID + access token + app secret
- Dashboard → Channels → WhatsApp → paste credentials → Save
- Paste the webhook URL + verify token into Meta dashboard → subscribe to
messages
⚠️ WhatsApp Groups + bots is not supported by Meta. Use Telegram for groups.
Built incrementally across 6 migrations. Single source of truth: db/schema.ts.
| Table | Purpose |
|---|---|
user · account · session · verificationToken |
Auth.js v5 tables |
user_session · security_event |
Admin device sessions + audit log |
bot |
One row per chatbot (with publicKey for widget) |
bot_channel |
Telegram / WhatsApp connections per bot |
document |
Uploaded sources, status tracking |
chunk |
Embedded chunks (vector(1536), HNSW cosine index) |
conversation |
Per-end-user thread, with IP / UA / referrer / flag / archive |
message |
Chat history with citations, tokens, latency, promoted-to-training pointer |
All env vars validated at boot by lib/env.ts (Zod). Missing values crash with a list of fields. See .env.example for the complete list.
| Variable | Purpose |
|---|---|
AUTH_SECRET |
JWT signing key. Generate with openssl rand -base64 32. |
DATABASE_URL |
Postgres connection string. |
REDIS_URL |
BullMQ queue backend. |
S3_* |
Object storage credentials. |
CHAT_PROVIDER |
openai / anthropic / google / deepseek / ollama |
EMBEDDING_PROVIDER |
openai / google / ollama |
OPENAI_API_KEY etc. |
Per-provider credentials |
STRIPE_* |
Billing (optional — UI explains how to configure) |
AUTH_GOOGLE_* |
Google OAuth (optional) |
- 🔐 Passwords stored as bcrypt hashes (cost factor 12)
- 🍪 Session strategy is JWT — middleware verifies at the edge without a DB round-trip
- 🛡 Multi-tenant isolation enforced in the data layer, not the application layer — every read/write joins through
bot.userId. Adversarial integration tests verify a second user cannot read, update, or delete the first user's resources. - ✅ SSRF guards on every URL/sitemap fetcher (rejects loopback, RFC1918, link-local, non-http(s))
- 🔍 Magic-byte checks on every file upload
- 🌍 Signature verification on Stripe + WhatsApp webhooks
- 🔑 Auto-rotated
publicKeyon widget channels (admin can regenerate to invalidate every embed) - 🚦 Redis-backed rate limits (per key + per IP) on the public widget endpoint
- 📋 Security audit log for every sign-in, password change, session revoke
- 🐛 Report a vulnerability via SECURITY.md — do not open a public issue
See DEPLOY.md for the complete production guide:
- Managed Postgres (Neon · Supabase · RDS) with pgvector
- Upstash Redis · AWS S3 or Cloudflare R2
- LLM provider setup
- Vercel for the web app + separate worker host (Railway / Render / Fly.io)
- Stripe configuration
- Channel setup (Telegram + WhatsApp Cloud API with Meta Business onboarding)
- DNS wiring + 11-point verification checklist
- 7 common gotchas + operational baselines
PRs welcome. See CONTRIBUTING.md for setup, coding standards, and the PR checklist.
- Browse good first issues
- Suggest an idea in Discussions
- Improve the docs — typos welcome
- 📱 React Native widget for iOS / Android
- 🦊 Slack / Discord channels (same pipeline pattern as Telegram)
- 🎙 Voice answers (TTS provider abstraction)
- 📈 Per-conversation feedback (👍/👎 on each message)
- 🔬 A/B test prompts per bot
If this saved you weeks of work, sponsor the project so we can keep shipping.
If you ship this, tell us @cyberunite — we'd love to see it.