Lalabase
티켓·문서·근무시간·고객대화를 한 곳에 모아 KI로 검색, 소프트웨어 기업 고객 프로젝트용, 오픈소스로 자체 서버·독일 클라우드에 배포
인간이 개입하는 오픈소스 AI 실시간 채팅, AI가 당신의 목소리로 답변하고 필요 시 텔레그램으로 전환, 무료 자체 호스팅
Open-source AI live chat with a human in the loop. The AI answers in your voice and hands off to you on Telegram the second a human's needed. Free to self-host — the open alternative to Intercom & Cri
비즈니스TypeScript · ShellGitHub @krispyhq온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기🥐 Open-source AI live chat with a human in the loop. The AI answers; you tag in from Telegram. The free, self-hostable alternative to Intercom & Crisp.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
The AI answers. You tag in.
Open-source live chat with a human handoff to Telegram — the free, self-hostable alternative to Intercom & Crisp.
Krispy is open-source live chat with a human in the loop. An AI answers your visitors in your voice, and hands off to you on Telegram the moment a real person is needed — you reply from your phone, and it lands live in the chat while the bot goes quiet.README 전문 보기 ↗Open-source live chat with a human handoff to Telegram — the free, self-hostable alternative to Intercom & Crisp.
LET THE BOT COOK 🥐
Krispy is open-source live chat with a human in the loop. An AI answers your visitors in your voice, and hands off to you on Telegram the moment a real person is needed — you reply from your phone, and it lands live in the chat while the bot goes quiet.
The paid stack for this runs $100–400/mo (Intercom, Crisp, a seat here, an AI add-on there). Krispy self-hosts on Cloudflare's free tier for $0 — no per-seat tax, no login your customers never asked for, no conversations living on someone else's servers.
This repo is the lean, self-hostable core — exactly two deployable things and the CLI to run them:
services/edge — the Cloudflare Worker + SessionDO (chat + Telegram handoff). The whole backend, one deploy.packages/widget — the dependency-free embeddable widget.js.packages/cli — the krispy CLI to manage your bot's knowledge base.Buttr: bonjour — i'm the croissant that answers your visitors so you can nap. no shade to Intercom. i'm just free.
Not the fit for enterprise support orgs that need a large multi-agent inbox, a native mobile app, or pre-built CRM integrations — reach for Intercom or Zendesk there.
visitor ──▶ AI answers (Cloudflare Workers AI) ──▶ visitor
│
└──▶ one Telegram topic per visitor, on your phone
│
you reply in the topic ──▶ shows up LIVE in the widget
│
└──▶ pending becomes joined — the bot was already silent
AUTO_ARCHIVE_HOURS changes the window). A new visitor message reopens them.
Human requests and calls remain visible until a team member archives them.history; the first
handoff seeds only the prior turns, then records that live line once. Earlier repeated
questions remain real conversation turns.An optional private knowledge gateway can add cited business evidence and, when configured, one bounded professional-method reference before the model call. Method references are separate from business facts and cannot override security or human handoff. The feature is server-only and leaves the default self-hosted path unchanged.
Under the hood it's one Cloudflare Worker plus a hibernatable Durable Object (SessionDO)
that holds the strongly-consistent ai / pending / operator state and keeps idle sockets
free. That's the whole backend.
Offline tests do not prove a model's decision quality. To evaluate the current prompt with
Workers AI, run bun scripts/eval-handoff.ts <course-knowledge-file> <private-results-file>
with CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN supplied through your secret manager.
This opt-in command makes six billable inference requests, uses AI_MODEL or the edge's
default model, and never creates conversations or notifies operators. Supply reviewed course
facts covering external tool costs, first-cohort support and outcome guarantees, with the
review deadline deliberately unknown. It checks routing and prompt-leak suppression; review
the saved answers for factual correctness. Keep knowledge and result files private.
You'll need Bun, a Cloudflare account (free), and a Telegram account.
git clone https://github.com/krispyhq/krispyai
cd krispyai
bun install
bun test # unit tests, no external services needed
bun run dev:edge # runs the Worker on http://localhost:8787
wrangler dev binds Workers AI and the Durable Object automatically — you can drive the full loop locally before touching Telegram.
Fastest path — the krispy init wizard. After you've created the KV namespace and
set TENANT_SYNC_SECRET (steps 1 + 5 below), run the guided setup and it walks you through
the rest in the terminal — the same steps as the Cloud dashboard onboarding:
npx krispy init # (or: bun packages/cli/src/index.ts init)
It guides you through ① Connect Telegram (BotFather → paste the bot token, validated
live via Telegram's getMe → supergroup-with-Topics + add-bot-as-admin → chat id),
② Train your bot (point it at a kbase file, accept a starter template, or skip),
③ Embed the widget (prints the copy-paste <script> snippet, tenant baked in — paste
before </body>), and ④ Next steps (krispy dev / wrangler deploy, and how to test
the loop). Each step persists as you go via POST /api/tenant/config. Re-run it any time;
it never clobbers what you've already set.
When a business adds a lead form and email connector, Krispy sends the captured
details and recent conversation to the configured inbox. The widget confirms
submission only after the email provider accepts it; on failure the visitor keeps
their entries and can retry.
Set EMAIL_ASSET_ORIGIN to the public HTTPS host serving
/brand/buttr-chill.png to show Buttr beside the lead email's Krispy wordmark.
The wordmark remains when image loading is blocked or the setting is unset.
Instagram connectors render as clear, tappable chat buttons with the familiar
Instagram glyph; the business chooses their label, link, and reveal timing.
Operators can also send a configured form or Instagram button into an existing
visitor conversation. The edge resolves the selected ID from that session's site,
stores the card in the conversation history, and restores it when the widget reconnects.
Operator reply suggestions are editable and never sent automatically. Approved
course and checkout links remain available when the model places sentence punctuation
after the link; changed or unapproved links are filtered.
The draft request stays focused on the latest visitor question even after an AI reply;
the Delulus pilot asks its model for structured JSON only on this operator action.
While a visitor waits for a team member, a compact "Talk to the team" card offers
only the site's configured forms and contact links, plus a call request when that
tenant's call settings and live call status permit it. Selecting a form opens it
inside the chat; the lead includes recent conversation context. The choice card
closes on operator takeover or AI resume, while any expanded form keeps its input.
Hosted preview deploys use ./deploy.sh edge preview and
./deploy.sh widget preview. The edge deploy syncs present Infisical-fed
Gemini, knowledge gateway, and LiveKit settings to the preview Worker
before its health check. See secrets for the key list.
The preview widget deploy also stages a checksum-verified LiveKit browser UMD
and its license at a versioned vendor/ path for lazy loading on call acceptance.
# 1. Create the KV namespace, paste the id into services/edge/wrangler.toml (REPLACE_WITH_KV_ID)
bunx wrangler kv namespace create KRISPY_KV
# 2. Get a bot token from @BotFather (/newbot), then store it
bunx wrangler secret put TELEGRAM_BOT_TOKEN
# 3. Make a Telegram group → upgrade to supergroup → enable Topics → add your bot
# as admin (needs "Manage Topics"). Grab the chat id (e.g. -1001234567890).
bunx wrangler secret put TELEGRAM_CHAT_ID
# 4. Pick any random string as the webhook secret
bunx wrangler secret put TELEGRAM_WEBHOOK_SECRET
# 5. (Optional) a secret to gate the tenant-config route the CLI uses
bunx wrangler secret put TENANT_SYNC_SECRET
# 6. Ship it (from services/edge)
cd services/edge && bunx wrangler deploy
# 7. Point Telegram at your deployed Worker
curl "https://api.telegram.org/bot<TOKEN>/setWebhook" \
-d "url=https://krispy-edge.YOU.workers.dev/api/telegram/webhook" \
-d "secret_token=<TELEGRAM_WEBHOOK_SECRET>"
Honest about the Telegram step: BotFather, the supergroup-with-Topics, and admin rights are a real five minutes of clicking — there's no way around a token if you want replies on your phone. Full walkthrough and architecture notes: services/edge/README.md.
Hosted Cloud can use the Buttr owner app without Telegram. In that mode the tenant's prompt, theme, forms, and human handoff still work. When the optional private R2 binding is present, visitors and Buttr operators can exchange images and short videos in that conversation; both sides read the bytes only with their own session credentials. Hosted attachments expire after 180 days. Visitors can tap an image to view it full screen and save it from the viewer. Without R2 or Telegram, the widget suppresses attachment controls.
The owner app's operator bearer is checked against the cloud API's server-resolved tenant,
so verified teammates can share the owner's handoff inbox. Legacy /me responses without
tenantId use a nonempty user-id fallback; malformed identity fields fail closed.
Sites may enable an optional name prompt after a visitor has started chatting. It never blocks the first question, and guests may skip it. Saved names travel only with that conversation; the operator inbox may also show a country flag when Cloudflare supplies a country code. The prompt and its English/Hebrew wording are site settings.
No Docker, no Tilt, no orchestrator — two bun scripts in two terminals:
bun run dev:edge # edge Worker (wrangler dev) on http://localhost:8787
bun run dev:widget # widget demo (bunx serve) on http://localhost:3000
Open the widget demo, say hi, and watch the loop. Handy checks:
bun run typecheck # tsc over the edge Worker + CLI
bun run lint # oxlint (Rust, fast)
bun run test # edge unit tests + CLI smoke tests
bun run check # typecheck + lint + test in one shot
New agent tasks use:
The WT0 entrypoint requires the exact release in .wt0-version
(currently 0.1.20); upgrade the installed wt0 before starting a task.
WT0_OWNER=<session-id> node scripts/agent-worktree.mjs start <task-id> <branch>
It creates an ephemeral CoW WT0 checkout outside this repository, prepares dependencies, and returns a path only after a complete readiness check. After review and PR completion, run this from the main checkout for a scoped cleanup dry run:
node scripts/agent-worktree.mjs assess <absolute-worktree-path>
The reviewed .wt0-generated file lists only disposable docs build output;
existing worktrees are not adopted or removed. See AGENTS.md.
krispy CLIYour bot's knowledge base is its system prompt. Write it in a file, then push it into the Worker's KV — no hand-written wrangler kv calls:
KRISPY_API=https://krispy-edge.YOU.workers.dev \
TENANT_SYNC_SECRET=... \
bun packages/cli/src/index.ts set-kbase ./kbase.md
| command | what it does |
|---|---|
krispy init | guided first-run wizard — Telegram → train → embed → next steps (POST /api/tenant/config) |
krispy set-kbase <file> | write <file>'s contents as the bot's system prompt (POST /api/tenant/config) |
krispy dev | run the edge Worker locally (wrangler dev) |
Config via env: KRISPY_API (Worker URL), KRISPY_TENANT (default self), TENANT_SYNC_SECRET (must match the Worker's). Details: packages/cli/README.md.
Host the dependency-free widget.js anywhere static, then drop one tag on any page. It lives in a Shadow DOM, so your site's CSS can't leak in.
<script
src="https://widget.krispyai.com/widget.js"
data-api="https://edge.krispyai.com"
data-tenant="self"
async
></script>
| attribute | required | default | meaning |
|---|---|---|---|
data-api | yes | — | your @krispy/edge Worker base URL |
data-tenant | no | self | tenant id (multi-tenant uses this) |
data-title | no | Chat with us | header text (theme headerTitle supersedes it) |
data-accent | no | #e39a2b | brand color (used before the theme fetch lands) |
data-launcher | no | (built-in) | none suppresses the built-in launcher button |
Richer appearance — circle/pill launcher and label, avatar, greeting, position, corner radius, font, notification sound — is set from KV via the theme config, not attributes. See docs → embed + theme the widget.
Each page load revalidates that public config, so a warm browser picks up tenant changes without a cache-buster URL.
The widget keeps primary actions readable automatically: it prefers Krispy's brand ink at 4.5:1 contrast, then chooses black or white for darker custom accents.
Bring your own launcher. The theme restyles Krispy's launcher; it can't replace it. Set data-launcher="none" and drive the panel from your own mark with window.krispy (open · close · toggle · isOpen · unread · el), listening for krispy:open / krispy:close / krispy:unread on document. The host element carries class="krispy-widget". All opt-in — leave it off and nothing changes. See docs → bring your own launcher.
Set theme.avatar to "none" for a text-only customer header; the built-in launcher uses a neutral chat mark while the default remains Buttr.
On coarse-pointer devices, the mute and close controls expand to 44px touch targets while their icons and desktop sizing stay unchanged.
Audio calls are opt-in through tenant callSettings. A team member can invite a visitor, or the visitor can request a call after human handoff when visitor requests are enabled. A visitor request alerts the operator app and waits for a team member to accept; it does not activate the microphone. Accepting an invitation starts the audio connection immediately; when the visitor requested the call, the widget connects as soon as the team accepts. The browser may then ask for microphone permission. The compact call card shows whether the team member has joined, provides Mute/Unmute and End call controls, and expands to available microphone and speaker choices. The full-screen audio view adds a clear identity and status display; Return to chat collapses it without ending the call, and Open call view restores it. Unsupported speaker selection stays hidden. Calls end when the page goes into the background or closes. Closing the chat panel alone leaves the call active.
An idle call suggestion can be dismissed for the current chat session without declining an incoming invitation or ending a call.
The widget can render durable runtime 0.5 call receipts with outcome, occurred time, and verified connected duration once the coordinator outbox emits them; no audio recording or content summary is stored.
Real incoming invitations ring in the visitor browser when audio is permitted; the visible Accept/Decline card still works when autoplay blocks sound.
The widget registers call presence when the page loads, so a team member can invite a visitor before chat opens. A hidden tab keeps its call socket, though browser suspension can interrupt background delivery; the widget refreshes the connection on return.
The widget attempts an incoming ringtone in hidden tabs too, subject to browser autoplay and background restrictions. If remote call audio is blocked after connection, the guest can tap on the call card.
A tenant-scoped coordinator is gated to the configured native-call pilot tenant.
It owns new native and guest calls while existing runtime 0.4 calls retain their
SessionDO owner through rollback. The Cloud API authenticates operator/device
identity, and signed LiveKit events verify media state before receipts and
terminal signals advance. Its state, identity, timeout, and retry contract is
described in the integration design.
The authenticated device-cleanup-status route exposes only per-device cleanup
counts for diagnostics; it does not mutate coordinator state.
Details: packages/widget/README.md.
If the automatic connection cannot start the microphone or reach the room, the
call card offers Try connecting again with a retry message. A hosting page
must permit microphone access for its own origin in Permissions-Policy; the
browser asks the visitor for consent when the connection starts.
The visitor can open Connection details after a failed join to see whether
the grant, audio library, room connection, or microphone step failed. No call
credential or endpoint is displayed.
Grant request failures also show the numeric HTTP status when available, even
when a server error does not return JSON.
Full docs live in apps/docs — a Fumadocs site, themed to the Krispy palette. Run it locally with bun run dev in apps/docs (Next on :3000); it deploys to Cloudflare Pages (domain: TODO).
TenantConfig · the markers.Fair to the competition — Krispy wins on cost, openness, and lock-in, not on snark.
| Krispy | Intercom | Crisp | Chatwoot | |
|---|---|---|---|---|
| Open source | ✅ MIT | ❌ | ❌ | ✅ |
| Free to self-host | ✅ CF free tier, no key | ❌ | ❌ | ⚠️ run your own server |
| AI answers, built in | ✅ free (CF Workers AI) | ⚠️ paid add-on | ⚠️ paid add-on | ⚠️ BYO |
| Human handoff to your phone | ✅ Telegram, native | ✅ their app, paid | ✅ their app, paid | ⚠️ |
| Per-seat tax | ❌ none | ✅ $/seat | ✅ $/seat | ❌ (self-host) |
Intercom is great at being Intercom. It's just not built for a solo founder who wants to own their stack.
Don't want to touch a terminal? Krispy Cloud is the hosted version: 14-day free trial, $19/mo flat, no per-seat. Same product, we run the Worker.
This is the lean, self-hostable core — only what a user self-hosts. The dashboard, billing, accounts, and marketing surfaces live in the Cloud repo and don't ship here.
services/
edge/ ⭐ the core — Cloudflare Worker + SessionDO (chat + handoff)
packages/
widget/ ⭐ the core — dependency-free embeddable widget.js
cli/ the `krispy` CLI — manage your kbase (system prompt)
apps/
docs/ the Fumadocs documentation site (quickstart · concepts · guides · reference · security)
agents/
skills/ generic scaffolding skills (add-a-service, add-a-lib, wire-a-payment-provider)
docs/ linting · secrets · agent-skills
api-collection/ Bruno requests for the edge Worker's routes
Want just the chat? cd services/edge, deploy, embed packages/widget. That's the whole thing.
SessionDO) — the whole backend, one deploy.PRs welcome — this repo is a template people clone, so clarity and convention matter. Keep changes small, keep the map honest. Start with CONTRIBUTING.md.
MIT. Read every line, fork it, own it.
à bientôt 🥐
The hosted edge needs PUSH_TOKENS_URL set to its matching cloud API
/internal/push/tokens endpoint and PUSH_TOKENS_SECRET set to the shared
server credential. The preview and production deployment configs supply their
matching endpoints. The production edge deploy checks the live binding and API
token-read route before it reports success.
Without the URL, chat and inbox persistence work but mobile push is skipped.
The first live visitor message claims one inquiry alert per session in the
Durable Object; a first-turn handoff uses its existing single handoff alert.
The claim records one attempt, so a failed delivery is not retried on later turns.
A signed device build, notification permission, registered device token, and
valid platform push credentials are also required; simulator chat tests do not
prove notification delivery. Self-hosted installations may leave these unset.
Optional server-side public chat language and pricing guards are documented in
services/edge/README.md.
For a reviewed Worker-only update, use node scripts/release.mjs <x.y.z> --target=edge
and ./deploy.sh edge production --preserve-secrets. The Edge tag requires exact
master CI success and does not trigger CLI publication. See RELEASING.md
for clean-checkout, existing-credential, configuration comparison and rollback requirements.