English | 简体中文
Basjoo is an AI customer-support platform with three main parts:
- a FastAPI backend for agent configuration, chat, indexing, auth, and scheduling
- a Next.js admin/dashboard frontend in
frontend-nextjs/ - an embeddable chat widget in
widget/that talks to the backend over HTTP and SSE
The stack also uses SQLite for application data, Redis for rate limiting, Qdrant for vector search and document indexing, PostgreSQL for relational data, a Scrapling microservice for web content fetching, and nginx for Docker-based reverse proxying.
Basjoo runs as a set of Docker containers. All LLM inference and embedding calls are made to external APIs (OpenAI, DeepSeek, Anthropic, Gemini, Jina, SiliconFlow), so no GPU is required.
| Minimum | Recommended | |
|---|---|---|
| CPU | 2 vCPU | 2–4 vCPU |
| RAM | 4 GB | 8 GB |
| Disk | 20 GB | 50 GB |
| OS | Ubuntu 22.04+ / Debian 11+ | Ubuntu 22.04+ / Debian 12+ |
| Docker | 20.10+ | latest |
For a blank Ubuntu or Debian server, run:
curl -fsSL https://raw.githubusercontent.com/haoyiyin/basjoo/main/install-deploy.sh | sudo shIf you already have this repository checked out locally, you can also run:
sudo sh install-deploy.shAfter deployment completes, the script prints a prominent summary with the admin dashboard URL. On local desktop environments (with DISPLAY or WAYLAND_DISPLAY set), it may automatically open the URL in your browser. On headless servers, copy the printed link into a browser.
The first registered admin becomes the workspace super administrator, who can create and manage multiple isolated AI agents within the same workspace.
backend/— FastAPI app, data models, chat APIs, auth, ingestion, indexing, testsfrontend-nextjs/— active admin/dashboard UIwidget/— embeddable chat widget bundlescrapling-service/— standalone microservice for web content fetching (curl_cffi + readability)nginx/— Docker nginx config- Qdrant vector database service configuration
docker-compose.yml— dev/prod orchestration
- Configurable AI agents with multiple provider settings
- Independent Embedding API selection for knowledge retrieval: Jina or SiliconFlow
- URL ingestion and file knowledge management
- Self-KB retrieval (Qdrant-backed) and document indexing via KB pipeline
- Streaming chat responses over Server-Sent Events
- Embeddable website widget with session persistence
- Widget copy auto-translation by visitor locale
- Per-agent widget domain whitelist for public chat embeds
- Offline agent fallback replies and admin-side error alerts
- Admin authentication and dashboard management flows
- Dockerized development and production-style deployment paths
The admin dashboard is the operational center for configuring agents, reviewing knowledge coverage, and accessing the major management modules.
The Playground lets admins test replies, inspect retrieval behavior, and adjust model/provider settings from the same workflow.
The Websites page handles URL ingestion, crawling, auto-fetch settings, and retraining/index-refresh workflows for web content.
The File Upload page lets you drag-and-drop PDF, TXT, CSV, Markdown, DOCX and other files as knowledge sources for AI retrieval.
Manage admin accounts with role-based access control — Super Admin, Admin, and Support roles.
The Sessions page shows live conversations, supports human takeover, and gives operators a single place to monitor visitor activity.
Agent Settings covers language/theme preferences, widget appearance, embed behavior, and other operational controls.
The widget provides the visitor-facing chat window with persisted sessions, multilingual copy, streaming responses, and knowledge-assisted replies.
- FastAPI
- SQLAlchemy async + SQLite
- Redis (rate limiting, caching)
- Qdrant REST API (vector search, document ingestion, hybrid retrieval)
- PostgreSQL (application data persistence)
- Scrapling microservice (curl_cffi + readability-lxml web content extraction)
- APScheduler
- Provider SDKs for OpenAI-compatible APIs, Anthropic, and Google Gemini
- Next.js 14
- React 18
- TypeScript
- i18next
- TypeScript
- esbuild
- Browser-native fetch + SSE handling
Development stack:
docker compose --profile dev up -dProduction-style stack:
docker compose --profile prod up -dUseful Docker commands:
docker compose logs -f backend-dev frontend-dev nginx
docker compose --profile dev up -d --build backend-dev frontend-dev
bash scripts/prod_stability_check.shDefault dev ports:
- Frontend:
http://localhost:3000 - Backend API:
http://localhost:8000 - Qdrant:
http://localhost:6333 - PostgreSQL:
127.0.0.1:5432 - Redis:
127.0.0.1:6379
The dev frontend and backend ports are bound as 3000:3000 and 8000:8000, so they are reachable from other devices that can access the host.
cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 main.pyBackend health check:
curl http://localhost:8000/healthcd frontend-nextjs
npm install
npm run devcd widget
npm install
npm run devnpm install
npm run dev
npm run build
npm run start # production build locally
npm run lint
npm run typecheck
npm run test # vitestnpm install
npm run dev # dev bundle + example server
npm run build # full build (typecheck + dev + prod bundles)
npm run build:dev # unminified ESM bundle (dist/basjoo-widget.js)
npm run build:prod # minified IIFE bundle (dist/basjoo-widget.min.js)
npm run typecheck
npm run test # vitestpip install -r requirements.txt
python3 main.py
pytest
pytest tests/test_api.py
pytest tests/test_api.py::test_namenpm run test:e2e # smoke tests (dev environment)
npm run test:e2e:all # all Playwright test projects
npm run test:e2e:prod # production-like E2E tests
npm run test:e2e:widget # widget cross-origin embed tests
npm run sync-widget # sync widget bundle to backenddocker compose --profile dev up --watchThe backend reads settings from environment variables and .env via pydantic-settings.
Important runtime settings used in the current codebase include:
DATABASE_URLREDIS_URLQDRANT_URLSECRET_KEYSECRET_KEY_FILEDEFAULT_AGENT_IDJINA_API_KEYDEEPSEEK_API_KEYALLOWED_ORIGINSALLOWED_METHODSALLOWED_HEADERSRATE_LIMIT_PER_MINUTERATE_LIMIT_BURST_SIZELOG_LEVELSERVER_DOMAINENCRYPTION_KEY(optional; auto-generated and persisted if missing)ENCRYPTION_KEY_FILE(default/app/data/.encryption_key)REQUIRE_SECRET_KEY(settruein production to reject insecure secret keys)
Notes:
- If
SECRET_KEYis missing or insecure, the backend generates one and persists it toSECRET_KEY_FILE. DEFAULT_AGENT_IDcan be used to restore or pin a known widget agent ID during migrations; see the deployment section below for the preservation workflow.- If
ENCRYPTION_KEYis not set, the backend auto-generates a Fernet key and persists it toENCRYPTION_KEY_FILE; stored provider API keys are encrypted with this key. cors_allow_null_origin(boolean, defaultfalse) controls whetherOrigin: null(e.g.,file://widget preview) receives wildcard CORS headers. Off by default for security.SERVER_DOMAINis consumed by the nginx service in the production compose profile to enforce a canonical host and block direct IP/other-host access.- The dev compose profile sets permissive CORS and local API URLs by default.
- The production compose profile expects mounted persistent backend data under
/app/data.
backend/main.py builds the FastAPI app and wires together:
- auth routes under
/api/admin - v1 APIs under
/api/v1(chat, agent config, sessions, quotas, task status) - admin-only routers:
url_endpoints.py(URL ingestion, crawling),file_endpoints.py(file upload), andindex_endpoints.py(index rebuild jobs) are protected at the router level viaDepends(get_current_admin) - public v1 routes:
/api/v1/chat,/api/v1/chat/stream,/api/v1/contexts,/api/v1/config:public - CORS middleware with a shared
apply_cors_headers()helper for early responses (rate limit 429, body size 413) - i18n middleware
- rate limiting middleware
- Redis and scheduler startup in non-test mode
- static routes for widget assets like
/sdk.js - a 10MB request body guard that returns JSON 413 errors before the request reaches downstream handlers
The main backend domains are:
- Agent config: provider/model/system-prompt/widget settings
- Knowledge sources: URLs and uploaded files, with SSRF protection via
backend/services/url_safety.py - Indexing: chunking content and storing in per-tenant Qdrant collections for data isolation
- Chat: session creation, streaming replies, source citations, quota checks
- Admin auth: dashboard login and registration
- Scheduling: URL fetch scheduler, history cleanup, session auto-close (30-min inactivity timeout)
The main persistent entities in backend/models.py are:
WorkspaceAgentURLSourceKnowledgeFileChatSessionChatMessageWorkspaceQuotaIndexJobAdminUser
The retrieval/indexing pipeline spans:
backend/api/v1/url_endpoints.pybackend/api/v1/index_endpoints.pybackend/services/kb_service.pybackend/services/qdrant_service.pybackend/services/scraper.pybackend/services/crawler.py
The LLM abstraction is in backend/services/llm_service.py. Provider selection is driven by Agent.provider_type. The current code supports OpenAI-compatible providers plus dedicated paths for OpenAI Native and Google.
Embedding settings are independent from the chat model provider. Admins can choose Jina or SiliconFlow for knowledge-base indexing/retrieval in Playground; the Websites and File Upload pages only require the API key for the currently selected embedding provider. SiliconFlow can use a dedicated SiliconFlow Embedding API key, with legacy fallback to the main SiliconFlow AI key when the AI provider is also SiliconFlow.
The active UI is the Next.js app in frontend-nextjs/.
- App Router routes live under
frontend-nextjs/app/ - most screen logic lives in
frontend-nextjs/src/views/ - shared components live in
frontend-nextjs/src/components/ - admin auth state is stored in
frontend-nextjs/src/context/AuthContext.tsx - API calls and SSE parsing are centralized in
frontend-nextjs/src/services/api.ts
widget/src/BasjooWidget.tsx is a self-contained embeddable widget that:
- auto-detects
apiBasefrom the script source URL, infers from dev port 3000 → backend 8000, or falls back towindow.location.origin - fetches
/api/v1/config:publicon init to resolvedefault_agent_id, widget title/color, and welcome message - stores visitor/session IDs in
localStorage - streams chat replies from
/api/v1/chat/streamvia SSE with a 90-second read timeout and one automatic retry on network errors - polls
/api/v1/chat/messages?role=assistantat 3-second intervals during human takeover scenarios - relies on server-side widget origin whitelist checks when configured
The backend serves widget-related assets directly, including /sdk.js.
- SSRF protection:
backend/services/url_safety.pyvalidates all user-provided URLs. It blockslocalhost, direct IP literals, URLs with embedded credentials, and hostnames that resolve to private/special-use IPs (loopback, RFC1918, link-local, cloud metadata). DNS resolution results are cached (512-entry LRU) to avoid repeated lookups during crawls. - Widget origin whitelist: Public chat routes enforce a per-agent origin whitelist configured in the admin dashboard. Admin users bypass the whitelist for testing.
- CORS policy: Early responses (rate limit 429, body size 413) apply CORS headers through a shared helper in
backend/middleware/rate_limit.py.Origin: nullonly receives wildcard CORS whencors_allow_null_originis explicitly enabled. Requests without anOriginheader do not receive CORS headers. - Secret persistence:
SECRET_KEY,DEFAULT_AGENT_ID, andENCRYPTION_KEYare auto-generated and persisted on first boot if not provided via environment variables, ensuring stable widget embed behavior and encrypted API key storage across redeployments. - Task concurrency: A shared
TaskLockservice prevents conflicting operations (e.g., rebuild blocks fetch, fetch blocks rebuild) on the same agent.
Backend tests are under backend/tests/.
Key testing behavior from backend/tests/conftest.py:
- sets
BASJOO_TEST_MODE=1 - uses isolated SQLite databases under
backend/.pytest_dbs/ - monkeypatches LLM integrations for many tests
- falls back between Docker hostnames and localhost for Redis where needed
Run all tests:
cd backend
pytestRun a file:
pytest tests/test_api.pyRun a single test:
pytest tests/test_api.py::test_namedocker-compose.ymlis the main orchestration entrypoint.install-deploy.shis the one-command production installer/deployer for Ubuntu and Debian. It can auto-install Docker/Compose, clone the repo, and force-sync an existing clone to the chosen remote branch before deploying.- nginx is configured with
client_max_body_size 12mso oversized requests can reach the backend and return JSON errors instead of nginx HTML errors. - Optional HTTPS is enabled only when readable certificate and key files exist in
./ssl. - When certificates are present, nginx serves HTTPS on port 443 and redirects HTTP requests on port 80 to HTTPS automatically.
SERVER_DOMAINcan be set for the nginx service to enforce a canonical hostname. When set, nginx serves only that host, rejects direct IP or unexpected Host access with nginx 444, and keeps/healthavailable for load balancer probes.- If
SERVER_DOMAINis not set, nginx keeps accepting requests by the incoming host as before. - Backend responses that bypass standard middleware should still apply CORS headers so embedded widget requests do not fail cross-origin.
- The backend persists the default widget agent ID to
/app/data/.agent_id. As long as the backend data volume is preserved, existing widget embed codes keep working after redeployments. - If you know an older widget agent ID that must keep working, set
DEFAULT_AGENT_ID=agt_xxxxxxxxxxxxbefore first boot of the new deployment. - Avoid
docker compose down -vor deleting the backend data volume unless you are intentionally rotating widget/embed identity. - The one-command installer only force-resets repository files; it does not delete Docker named volumes, so
/app/datapersistence remains intact across redeployments.
Recommended production workflow:
- Preserve the backend data volume mounted at
/app/data. - Redeploy with
docker compose --profile prod up -d --build. - If you are migrating to a new server and know the old widget
agentId, setDEFAULT_AGENT_IDbefore starting the backend. - Back up at least
/app/data/basjoo.dband/app/data/.agent_id.
Example .env snippet for migration:
SECRET_KEY=
DEFAULT_AGENT_ID=agt_123456789abcIf the old data volume is lost and the old agentId is unknown, old widget embeds cannot be recovered automatically because the embed code references the previous agent ID directly.
Examples of backend endpoints present in the codebase:
/health/api/admin/login/api/admin/register/api/v1/chat/api/v1/chat/stream/api/v1/agent:default/api/v1/urls:create/api/v1/urls:list/api/v1/urls:refetch/api/v1/index:rebuild/api/v1/index:status
Basjoo is built on top of these amazing open-source projects:
- Qdrant — High-performance vector similarity search engine. Powers Basjoo's self-developed multi-tenant knowledge base.
- Scrapling — Stealthy web scraping with TLS fingerprint impersonation (curl_cffi). Powers Basjoo's URL content extraction microservice.
- FastAPI — The web framework powering Basjoo's backend APIs.
- Next.js — The React framework powering Basjoo's admin dashboard.
- pgvector — Open-source vector similarity search for PostgreSQL.
This README reflects the repository as it exists now. If you change deployment flows, provider support, or package scripts, update this file alongside the code.