An embeddable customer-support chat widget for Stellar / Soroban dApps. Ships with a RAG backend that's pre-indexed on the official Stellar & Soroban docs, plus support for pointing it at a dApp's own docs. Automatically answers in the user's language, and escalates anything it can't answer confidently to your Discord or Slack.
[Website] <-- <script> tag --> [widget.js] <-- HTTPS POST --> [RAG backend]
|
vector store (local docs)
|
LLM (OpenAI/Anthropic)
|
low-confidence? --> webhook
cd server
cp .env.example .env # fill in LLM_API_KEY, LLM_PROVIDER, ESCALATION_WEBHOOK_URL
npm install
npm run ingest # indexes the built-in Stellar/Soroban docs + any DOC_SOURCES you added
npm start # listens on PORT (default 8787).env options are documented in server/.env.example. To index your own dApp's docs,
add URLs (or a sitemap URL) to DOC_SOURCES — comma separated — before running npm run ingest.
Drop this before </body> on any page:
<script src="https://YOUR_HOST/widget.js"></script>
<script>
StellarSupportWidget.init({
backendUrl: "https://YOUR_BACKEND_HOST",
projectName: "My Soroban dApp",
primaryColor: "#7B61FF",
welcomeMessage: "Hi! Ask me anything about the docs or how the app works."
});
</script>That's it — no build step, no framework required. The widget renders a small chat bubble in the bottom-right corner, opens a chat panel on click, and streams answers from your backend.
| Option | Default | Description |
|---|---|---|
backendUrl |
(required) | Base URL of your running RAG backend |
projectName |
"Support" |
Shown in the widget header |
primaryColor |
#5865F2 |
Accent color (bubble, header, buttons) |
welcomeMessage |
generic greeting | First message shown in an empty conversation |
position |
"bottom-right" |
"bottom-right" | "bottom-left" |
Open demo/index.html in a browser after starting the backend locally on
http://localhost:8787 — it embeds the real widget against the real backend and lets
you ask real Stellar/Soroban questions (e.g. "What is a Soroban contract's storage
TTL?", "How do I fund a testnet account?").
- Ingest (
npm run ingest): fetches each configured doc URL, strips HTML down to readable text, chunks it (~800 chars, paragraph-aware), embeds each chunk locally with@xenova/transformers(all-MiniLM-L6-v2, runs on CPU, no external API key needed for embeddings), and writes the vectors + text toserver/data/index.json. - Query: the incoming question is embedded the same way, compared by cosine similarity against the index, and the top-k chunks are pulled as context.
- Language: the question's language is detected (
franc) and the LLM is instructed to answer in that language, regardless of the docs' source language. - Generate: the question + retrieved context + a system prompt restricting the model to the provided context is sent to the configured LLM (OpenAI or Anthropic).
- Escalate: if retrieval similarity is below
CONFIDENCE_THRESHOLD, or the model's answer indicates it doesn't know, the question (plus best-guess context) is POSTed toESCALATION_WEBHOOK_URL(Discord or Slack incoming webhook format — both are auto-detected from the URL) so a human can follow up.
MIT — see LICENSE.
See SETUP.md for open issues good for a first contribution.