English · 简体中文 · 日本語 · Español
Open-source AI customer support: chat widget + real-time voice agent + a full MCP server, grounded in your own knowledge base.
Chatty is an open-source, self-hostable Zendesk alternative and Intercom alternative for teams that need an AI chat widget, a shared support inbox, and voice agents. Chatty is an independent project and is not affiliated with or endorsed by Zendesk or Intercom.
Run the application containers on your own host while keeping Supabase Auth, Postgres, Storage, and Realtime managed. The same deployment contract works on a VPS, Railway, Render, or another Docker host without touching the live Supabase project.
Chatty Cloud (hosted) · Documentation · Performance Scorecard · Flow Builder · Widget Embed · Secure customer identity · Quick Start · Platform Runbook · MCP Server · AI Plugins · Community Traction
- Why Chatty
- Performance Scorecard (A-F Auditing)
- Flow Builder & Integrations
- Widget Integration (3-Line Embed)
- Features
- Architecture
- Requirements
- Quick Start (Docker Compose)
- Self-Hosting, Step by Step
- Local Development (without Docker)
- Managed-Supabase platform runbook
- MCP Server & AI Coding Plugins (Claude, Cursor, Codex)
- Environment Variable Reference
- Testing & CI
- Security
- Contributing
- License
Every hosted chatbot SaaS charges per-seat or per-message and holds your conversation data. Chatty is open-core: run it yourself for free, or use Chatty Cloud - our own hosted version of this exact repo, if you'd rather skip the ops work. Either way you get the same feature set - streaming chat, a real-time voice agent, RAG over your own documents, meeting booking, lead capture, WhatsApp/Slack channels, and a full MCP server so an AI agent can run the whole dashboard for you.
| Closed-source SaaS chatbots | Chatty | |
|---|---|---|
| Your conversation data | Lives on their servers, always | Your Supabase project - whether you self-host or use Chatty Cloud |
| Pricing | Per-seat / per-message, no free tier | Self-host for free, or a straightforward hosted plan on Chatty Cloud |
| LLM | Locked to their model | Bring your own - Gemini by default, or BYOK OpenAI/Anthropic/OpenRouter |
| Voice agent | Usually a separate, pricier tier | Included, same knowledge base as chat |
| Agent/automation access | Usually none, or a paid add-on | Full MCP server included - 55 tools, OAuth 2.0 secured |
| Source code | Closed | MIT licensed - fork it, audit it, extend it, run it anywhere |
- 💬 Embeddable Chat Widget — One
<script>tag, streaming SSE replies, crisp vector Shadow DOM rendering on any site. - 🎙️ Real-Time Voice Agent — Sub-150ms telephone-grade WebRTC voice calls via LiveKit, sharing the same knowledge brain.
- 📚 RAG Knowledge Hub — Ingest PDF, DOCX, TXT, CSV, or crawl website sitemaps with
pgvectorsemantic retrieval. - 🛠️ Autonomous Tool-Calling — Schedules real meetings (Google Meet, Teams, Zoom), qualifies leads, and checks calendars.
- 🔌 Omnichannel Ingress — Web widget, WhatsApp Cloud API, Slack events, and inbound email ticketing.
- 🔑 Bring Your Own Key (BYOK) — Free-tier Gemini by default, or per-bot OpenAI/Anthropic/Groq with AES-128 encryption.
- 🤖 55-Tool MCP Server — Control the entire dashboard programmatically from Claude Desktop, Cursor, or Codex via OAuth 2.0.
- 📊 5-Pillar Scorecard — Independent A–F performance auditing across Conversion, Deflection, Accuracy, Reliability, and CSAT.
- 🎯 Proactive Campaigns — Behavioral triggers (exit-intent, time-on-page, scroll-depth) with timezone-aware cadence planning.
- 🗺️ Visual Flow Architect — Interactive
@xyflow/reactcanvas with AI flow generation and deterministic dry-run simulations. - 🐳 One-Command Self-Host —
docker compose up, point to Supabase, and launch in 60 seconds.
🔧 Deep Dive: Campaign Telemetry & Flow Builder Safety Architecture
Campaign configuration is persisted in chatty_campaigns. Widget deployments record impression, click, and conversion events through POST /api/widget/campaign-events with bot_id, campaign_id, and an optional idempotency_key (safe for retries). Recomputed analytics are accessible via GET /api/bots/{bot_id}/campaigns/{campaign_id}/analytics.
Campaign drafts support a validated schedule_config (once, hourly, daily, or weekly with IANA timezone). POST /api/bots/{bot_id}/campaigns/audience-suggest generates bounded segments, intent thresholds, and rationales before saving. Delivery channels include web, email, WhatsApp, and SMS. Safety policies support overnight quiet hours (quiet_hours: {"start": "22:00", "end": "08:00"}). The background worker python -m app.workers.campaign_scheduler claims Redis idempotency keys for 24 hours to prevent duplicate dispatches. Provider-backed steps are expanded only for leads with explicit marketing consent and are then filtered by the same segment, returning_only, and min_intent_score rules used by the widget; qualification signals may be persisted in chatty_leads.custom_fields by an AI flow. Provider jobs additionally claim a hashed recipient/campaign frequency-cap key atomically before sending; cap hits are recorded as intentional suppressions, not retries. Each queued provider job carries its deterministic delivery key into the worker, which upserts queued, sent, failed, or suppressed state in chatty_campaign_deliveries; campaign analytics reports delivery success rate and channel breakdown without exposing recipient addresses. Apply migration 20260930130000_campaign_delivery_ledger.sql before enabling provider campaigns in a new environment. Email delivery uses configured Resend or OneSignal credentials; unconfigured channels fail closed.
The AI Flow Architect endpoint (POST /api/flow/generate) requires authenticated users with settings permission. Prompts are bounded to 4,000 characters, and generated graphs are validated for unique node IDs and resolvable edge references. Dry-runs enforce execution step budgets to prevent runaway loops and store immutable graph snapshots for deterministic replay. Webhook/action mappings use a safe {{input}} and {{context.path}} resolver; exact-value mappings preserve JSON types and unresolved fields are surfaced in the trace rather than silently sent.
Published widget flows execute their control nodes too: delay is capped at five minutes, condition accepts only the declarative input ==, !=, contains, starts_with, and ends_with grammar, and loop has a 100-iteration hard ceiling plus an explicit done, complete, or exit route. A webhook node is dispatched through POST /api/widget/flow/webhook, not from browser JavaScript. The API reloads the current active flow and permits only the selected published node's config.url (or config.webhook_url), posts a bounded mapped body, adds an idempotency key, disables redirects, and pins DNS through the SSRF guard. Browser-supplied destinations, headers, and methods are never accepted. A webhook can set bounded max_attempts (1–5), backoff_ms (0–5,000), and timeout_ms (500–30,000); only connection errors, timeouts, and 5xx responses are retried. Use labeled success/true and error/failed/timeout edges to make the outcome path explicit. Operators can inspect an immutable run snapshot and trace at GET /api/bots/{bot_id}/flow/runs/{run_id} before replaying it.
The public repository has one canonical application layout: frontend/ contains the Next.js dashboard and widget, while
backend/ contains the FastAPI API, workers, integrations, and migrations. The application runs against a managed
Supabase project (the same contract used by Chatty Cloud). The optional voice worker can use LiveKit Cloud or the
isolated self-hosted LiveKit media-plane stack under backend/voice-agent; that stack does not replace Supabase,
Postgres, Auth, Storage, or the Chatty API.
flowchart TB
classDef actor fill:#f8fafc,stroke:#64748b,color:#0f172a
classDef edge fill:#eff6ff,stroke:#2563eb,color:#1e3a8a
classDef app fill:#ecfdf5,stroke:#059669,color:#064e3b
classDef data fill:#f0fdfa,stroke:#0f766e,color:#134e4a
classDef integration fill:#fff7ed,stroke:#ea580c,color:#7c2d12
classDef deploy fill:#f5f3ff,stroke:#7c3aed,color:#4c1d95
subgraph clients["Clients and channels"]
direction LR
visitor(("Website visitor")):::actor
operator(("Workspace operator")):::actor
channels["WhatsApp · Slack · MCP"]:::actor
end
subgraph edge["Public edge"]
direction LR
tls["TLS · domains · rate limits"]:::edge
web["Next.js frontend"]:::app
end
subgraph runtime["Chatty runtime"]
direction LR
api["FastAPI API · chat · RAG · webhooks"]:::app
voice["Voice worker · LiveKit Agents"]:::app
end
subgraph managed["Managed Supabase — default profile"]
direction LR
auth["Supabase Auth"]:::data
postgres["Postgres + pgvector · RLS"]:::data
storage["Supabase Storage"]:::data
realtime["Supabase Realtime"]:::data
end
subgraph integrations["Optional integrations"]
direction TB
llm["LLM providers"]:::integration
calendar["Calendar providers"]:::integration
channelsApi["WhatsApp / Slack"]:::integration
livekit["LiveKit Cloud or self-hosted"]:::integration
billing["Billing + webhooks"]:::integration
end
subgraph targets["Deployment targets"]
direction LR
docker["Docker Compose / VPS"]:::deploy
railway["Railway"]:::deploy
render["Render Blueprint"]:::deploy
heroku["Heroku-style host"]:::deploy
end
visitor --> tls
operator --> web
channels --> api
tls --> web
web -->|HTTPS| api
api --> voice
api --> auth
api --> postgres
api --> storage
api --> realtime
api --> llm
api --> calendar
api --> channelsApi
voice --> livekit
api --> billing
targets -. runs .-> runtime
linkStyle default stroke:#64748b,stroke-width:1.5px
chatty/
├── frontend/ Next.js - dashboard, embeddable widget, widget.js loader
├── backend/ FastAPI - chat/RAG/bookings/channels/OAuth/MCP API
│ ├── app/ Routers, core (auth/security/db helpers), schemas
│ ├── plugins/ Google/Microsoft integrations, RAG, widget orchestration
│ ├── supabase/ Database schema and migrations (applied in order)
│ ├── scripts/ apply_migrations.py and other one-off ops scripts
│ ├── voice-agent/ LiveKit voice worker agent + self-hosted VPS Docker stack
│ └── tests/ pytest smoke + unit tests
└── docker-compose.yml
| Profile | Data and identity services | When to use |
|---|---|---|
managed_supabase (default) |
Supabase Auth, Postgres, Storage, pgvector, and RLS | Chatty Cloud and every supported application deployment |
| Voice media plane (optional) | LiveKit + private Redis on a VPS | Keep audio media on your own VPS while the application data remains in Supabase |
For voice deployment, follow backend/voice-agent/README.md and
frontend/docs/guides/voice-self-hosting.mdx.
The managed-Supabase deployment contract is two Docker services (API + frontend).
The root docker-compose.yml runs both services without provisioning a second
database. The complete platform runbook, including secret handling, domains,
health checks, verification, rollback, and troubleshooting, is in
docs/SELF_HOST_MANAGED_SUPABASE.md.
| Platform | Official deployment guide | Current repository status |
|---|---|---|
| Managed-Supabase Docker Compose | Supported now for the API + frontend; put TLS in front. | |
| Railway Docker Compose guide | Supported as two services; Railway maps the API and frontend Dockerfiles separately. | |
render.yaml / Render Blueprint reference |
Blueprint included; it creates the API + frontend services and prompts for secrets. | |
| Heroku container runtime | Supported as two container apps; no full-stack Button is claimed. |
For every supported application deployment keep DEPLOYMENT_PROFILE=managed_supabase. LiveKit self-hosting is an
independent media-plane choice configured only in backend/voice-agent/.env; it does not introduce a second database,
object store, or authentication system.
| Version | Needed for | |
|---|---|---|
| Docker + Docker Compose | 24+ | Quick Start (recommended path) |
| Python | 3.11+ | Backend, without Docker |
| Node.js | 20+ | Frontend, without Docker |
| Supabase account | free tier | Database (Postgres + Auth + Storage) |
| Google AI Studio API key | free tier | Default LLM (Gemini) |
Everything else (LiveKit for voice, WhatsApp/Slack tokens, Google/Microsoft OAuth, Lemon Squeezy billing, Sentry, Upstash Redis) is optional - each env var you leave blank just disables that one feature; nothing else breaks.
The fastest path from clone to a running instance. See Self-Hosting, Step by Step below if you want the full walkthrough with screenshots-in-words and troubleshooting.
git clone --recurse-submodules https://github.com/PersonaliAI/chatty.git
cd chatty
# Optional plugin and SDK source checkouts are independent submodules.
# For an existing clone, use: git submodule update --init --recursive
# 1. Apply the database schema to a Supabase project you've already created
cd backend && pip install psycopg2-binary
python scripts/apply_migrations.py "postgresql://postgres:YOUR_PASSWORD@YOUR_HOST:5432/postgres"
cd ..
# 2. Configure environment variables
cp backend/.env.example backend/.env # fill in Supabase + Gemini keys
cp frontend/.env.example frontend/.env # fill in Supabase URL + anon key
cp .env.example .env # same NEXT_PUBLIC_* values (used at Docker build time)
# 3. Run it
docker compose up --build backend frontendDashboard: http://localhost:3000 - sign up, create a bot, and the widget embed snippet is generated for you under bot settings.
A fuller walkthrough than the Quick Start above - read this if it's your first time, or if something in Quick Start didn't work.
- Go to supabase.com, sign up, and click New Project. The free tier is enough to run Chatty (upgrade later if you outgrow it).
- Pick a region close to where you'll deploy the backend, set a database password, and wait for provisioning (~2 minutes).
- Once the project is ready, go to Project Settings → API and note down:
- Project URL (
https://xxxxx.supabase.co) anonpublickeyservice_rolekey (keep this one secret - it bypasses row-level security)
- Project URL (
- Go to Project Settings → Database → Connection string → URI. Use the Session pooler or direct connection string (not the transaction pooler - schema migrations need a persistent session). You'll use this once, in the next step.
Chatty ships ordered SQL migration files under backend/supabase/migrations/. Apply them all in order with the included script - it's idempotent (tracks what's already applied in a _migrations_log table), so it's always safe to re-run after pulling updates:
cd backend
pip install psycopg2-binary
python scripts/apply_migrations.py "postgresql://postgres:YOUR_PASSWORD@YOUR_HOST:5432/postgres"You should see output like:
<current migration count> migration files found
applied 20260510084430_initial_schema.sql
applied 20260511120000_slice2_billing_and_integrations.sql
...
Done - applied 67 new, skipped 0 already-applied.
If a migration fails partway through, the script stops and prints which file failed - fix the reported issue (usually a stale connection or an already-modified table from manual tinkering) and re-run; already-applied files are skipped automatically.
Chatty defaults to Google's Gemini (generous free tier, no credit card required to start):
- Go to aistudio.google.com/apikey.
- Sign in with a Google account and click Create API key.
- Copy the key - you'll paste it into
GEMINI_API_KEYin the next step.
Prefer a different model? Every bot supports BYOK (OpenAI, Anthropic, or OpenRouter) configurable per-bot from the dashboard once it's running - you don't need to decide this now.
Three files, one per surface:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
cp .env.example .envOpen backend/.env and fill in, at minimum:
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_SECRET_KEY=<secret key from Step 1>
SUPABASE_DB_HOST=<from the connection string in Step 1>
SUPABASE_DB_PASSWORD=<your database password from Step 1>
GEMINI_API_KEY=<from Step 3>
# Generate each of these once with the command shown next to it:
FUNCTION_SECRET=<python -c "import secrets; print(secrets.token_urlsafe(32))">
BYOK_ENCRYPTION_KEY=<python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())">Open frontend/.env and .env (repo root) and fill in the matching NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY (same values as above - the frontend only ever sees the public anon key, never service_role).
Every other variable in backend/.env.example is optional and documented inline - voice, WhatsApp, Slack, Google/Microsoft OAuth, billing, error monitoring, and more. Leave them blank to start; each one unlocks exactly one feature. See the full Environment Variable Reference below for what each does.
If you're deploying under your own domain (not just running locally), also set CHATTY_BACKEND_URL and CHATTY_FRONTEND_URL in backend/.env to your real domains - these are the OAuth issuer and MCP resource identifiers; leaving them at the default breaks OAuth/MCP client discovery once you're not running on localhost. See MCP Server & Agent Control.
docker compose up --build backend frontendFirst build takes a few minutes (installing Python/Node dependencies, compiling the Next.js production bundle). Subsequent runs are fast - Docker caches layers.
- Dashboard:
http://localhost:3000 - Backend API:
http://localhost:8000(interactive docs athttp://localhost:8000/docs)
- Open
http://localhost:8000/docs- you should see the FastAPI Swagger UI listing every endpoint. If this doesn't load, the backend container failed to start; checkdocker compose logs backend. - Open
http://localhost:3000, click Sign up, and create an account (this goes through Supabase Auth against your own project). - Create your first bot from the dashboard. Add a knowledge source (paste some text or a URL) and try it in the Playground tab.
- Copy the embed snippet from the bot's settings and drop it into any HTML page - the widget should load and respond using your knowledge base.
If any of these steps fail, see Troubleshooting below.
Each of these is opt-in - set the relevant env vars in backend/.env and restart (docker compose up --build backend):
| Feature | Env vars | Notes |
|---|---|---|
| Voice agent | LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET |
Free tier at cloud.livekit.io, or self-host LiveKit. Run the worker from backend/voice-agent as a separate service; the managed-Supabase compose keeps it opt-in. |
| WhatsApp channel | WHATSAPP_VERIFY_TOKEN, WHATSAPP_ACCESS_TOKEN |
Meta Cloud API - see Meta's developer docs. |
| Slack channel | SLACK_SIGNING_SECRET |
From your Slack app's Basic Information page. |
| Google Calendar/Gmail booking | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
Create OAuth credentials at console.cloud.google.com/apis/credentials. |
| Microsoft 365 booking | MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET |
Register an app in Azure AD. Real Teams online-meeting links, not a placeholder. |
| Real Zoom meeting links | ZOOM_ACCOUNT_ID, ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET |
Server-to-Server OAuth app in the Zoom Marketplace (meeting:write:meeting scope). A single backend-wide credential - no per-user connect flow. Without this, bookings fall back to a placeholder link. |
| Billing / paid plans | LEMONSQUEEZY_API_KEY + related vars |
Only relevant if you're reselling access; skip entirely for internal/free use. |
| Error monitoring | SENTRY_DSN |
Optional - leave blank and errors just go to stdout/logs. |
The supported production shape is two Docker services (API + frontend) backed
by the same managed Supabase project. The full copy/paste runbook for each
platform is SELF_HOST_MANAGED_SUPABASE.md.
It covers DEPLOYMENT_PROFILE=managed_supabase, secret-manager setup, domains,
health checks, production verification, rollback, and security boundaries.
The retired provider-neutral Postgres/Redis/object-store stack is intentionally
not part of this repository. Do not use old install-self-host.ps1 instructions;
the supported self-hosted component is the LiveKit voice media plane described
in backend/voice-agent/README.md.
git pull
cd backend && python scripts/apply_migrations.py "postgresql://..." # picks up any new migrations, skips old ones
cd .. && docker compose up --build backend frontend| Symptom | Likely cause / fix |
|---|---|
docker compose up fails immediately on backend with RuntimeError: ... environment variable is required |
SUPABASE_URL or SUPABASE_SECRET_KEY is missing from backend/.env - these two fail startup on purpose rather than silently falling back to nothing. |
| Backend starts but BYOK/booking/other features error at request time | GEMINI_API_KEY, FUNCTION_SECRET, or BYOK_ENCRYPTION_KEY is blank - these don't crash startup, but the specific feature that needs them fails when you actually use it. Fill them in per Step 4. |
| Migration script hangs or times out | You're using the transaction pooler connection string. Switch to the Session pooler or direct connection string from Supabase's Connection string page. |
| Dashboard loads but sign-up fails | NEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_ANON_KEY in frontend/.env don't match the project you ran migrations against. |
| Widget embeds but never replies | Check docker compose logs backend for a Gemini API error - usually an invalid/missing GEMINI_API_KEY, or the free tier's rate limit. |
| MCP/OAuth client can't discover the server once deployed | CHATTY_BACKEND_URL is still the default (https://api.chatty.personaliai.com) instead of your own domain - set it explicitly once you're not on localhost. |
| Voice agent never joins the call | LIVEKIT_URL/LIVEKIT_API_KEY/LIVEKIT_API_SECRET unset, or the voice-worker service isn't running (docker compose up --build with no service names starts it; docker compose up backend frontend doesn't). |
Still stuck? Open an issue with your docker compose logs output (redact secrets first).
Backend:
cd backend
python -m venv .venv && .venv\Scripts\activate # or source .venv/bin/activate on macOS/Linux
pip install -r requirements.txt
uvicorn main:app --reload --port 8000Frontend:
cd frontend
npm install
npm run devVoice worker (optional):
cd backend
python voice-agent/voice_worker.py devChatty ships a full Model Context Protocol server - connect Claude, ChatGPT, or any MCP-compatible client and run the entire dashboard from a conversation instead of clicking through screens.
- Auth: OAuth 2.0 authorization-code flow with mandatory PKCE (RFC 7636), dynamic client registration (RFC 7591), and authorization server metadata discovery (RFC 8414) - no API key to paste into a config file.
- Scopes:
read,write,knowledge,voice,actions,admin- a client only gets what it's granted. - 55 tools across 10 categories: bot lifecycle, customizer & design, visual flow builder, proactive campaigns, voice agent, knowledge base & RAG, inbox & human takeover, leads/calendar/meetings, analytics & self-healing, and settings/guardrails/BYOK/team RBAC.
- Real data only - every tool reads and writes the exact same tables the dashboard does; nothing simulated.
Connect by pointing an MCP client at:
{
"mcpServers": {
"chatty": {
"url": "https://your-backend-domain/mcp"
}
}
}The client opens a normal OAuth consent screen on first connect. If you're self-hosting under your own domain, set CHATTY_BACKEND_URL (and CHATTY_FRONTEND_URL, for the consent screen redirect) in backend/.env to your real URLs first - see Step 4.
For setup instructions and plugin manifests for Claude Code (/plugin marketplace add PersonaliAI/chatty), Cursor IDE (.cursor/rules/), Claude Desktop, and Windsurf, see the full AI Coding Tools & Plugin Marketplace Guide.
Every environment variable is documented inline in backend/.env.example and frontend/.env.example - what it's for, where to get it, and what happens if you leave it blank. The tables below are the short version.
Backend - required to start:
| Variable | Purpose |
|---|---|
SUPABASE_URL, SUPABASE_SECRET_KEY |
Database + auth connection; the secret key stays server-side |
SUPABASE_DB_HOST, SUPABASE_DB_PASSWORD |
Direct Postgres connection for a few operations PostgREST can't do |
FUNCTION_SECRET |
Signs widget origin tokens + OAuth state JWTs; gates internal cron/admin endpoints |
BYOK_ENCRYPTION_KEY |
Encrypts customer-supplied BYOK API keys at rest |
GEMINI_API_KEY |
Default LLM |
Backend - optional, one per feature: voice (LiveKit), WhatsApp, Slack, Google/Microsoft OAuth (calendar booking), Zoom (Server-to-Server OAuth), transactional email (OneSignal), web crawl (Jina), billing (Lemon Squeezy), rate limiting (Upstash Redis), error monitoring (Sentry), and CHATTY_BACKEND_URL/CHATTY_FRONTEND_URL (only needed once you're deployed under your own domain - see MCP Server & Agent Control). Full list with setup links: backend/.env.example.
Frontend - required:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY |
Must match the backend's Supabase project |
NEXT_PUBLIC_BACKEND_URL |
Where the frontend calls the API - http://localhost:8000 locally, your real backend URL in production |
Frontend - optional: billing portal (Lemon Squeezy), Firebase Analytics. Full list: frontend/.env.example.
.github/workflows/ci.yml runs on every push/PR to main:
- Backend: compile-check (
python -m compileall), the full pytest suite (backend/tests/), and a non-blockingpip-auditdependency scan. - Frontend: TypeScript typecheck, ESLint, a widget design-token drift check, and a production build.
Run the backend suite locally:
cd backend
pip install -r requirements.txt pytest
python -m pytest tests/ -q.github/dependabot.yml watches pip/npm/Docker/GitHub Actions dependencies weekly.
Found a security issue? Please don't open a public GitHub issue for it. Instead, use GitHub's private reporting: this repo's Security tab → Report a vulnerability. We'll respond as quickly as we can.
SUPABASE_SECRET_KEY, FUNCTION_SECRET, BYOK_ENCRYPTION_KEY, and every OAuth/API client secret should be treated as production credentials - never commit a filled-in .env file (all three .env.example templates are already gitignored under their real names).
Issues and PRs welcome - this is a young project and there are rough edges. See CONTRIBUTING.md for the development setup and good first areas: additional STT/TTS provider plugins, additional channel integrations, deployment guides for platforms beyond Docker Compose, and docs.
MIT - see LICENSE. Use it, fork it, ship it commercially - attribution appreciated but not required.
If Chatty is useful to you, star the repo ⭐ - it helps other people find it.