Guide

Add a chat assistant

Optional AI or human chat on every page, with your site as its knowledge base.

6 min read

Meteorite has first-class, optional support for PopTalk: a self-hosted chat backend with a small widget, which can answer with an AI model or forward messages to you, and poptalk-rag, which lets it answer from your own content. Nothing is added to your site unless you turn it on.

A static site cannot run a chatbot by itself, so there are two parts:

PartWhere it runsWhat it does
PopTalk backend (and optionally poptalk-rag)Your own serverTalks to the AI provider or to you; holds all keys
MeteoriteBuild timeAdds the widget to your pages and exports your content as the knowledge base

1. Turn on the widget #

# config.yml
chat:
  provider: poptalk
  api_url: https://chat.example.com     # your PopTalk backend
  persona: my-site                      # optional, for multi-persona backends

That is all. Both bundled themes already include the hook; a custom theme needs one line before </body>:

{{ chat_widget() | safe }}

Check the backend, including that it allows your site’s origin (CORS):

meteorite chat check

What visitors get #

  • A launcher button. Nothing is requested from the chat backend until it is clicked, so there are no cookies or network calls before the visitor chooses to chat.
  • The widget file is copied into your site (assets/poptalk-<hash>.js, with a subresource integrity hash and its MIT licence), so no CDN is contacted. Fonts come from the visitor’s system by default; set fonts: google to use Inter from Google Fonts.
  • The widget follows your site’s light/dark mode and skin accent live.
  • Before the first message the widget asks for a name and a valid email address (or phone number with data-collect="phone"). PopTalk 1.2 or later requires this; there is no anonymous mode.

Options #

KeyDefaultMeaning
providernonepoptalk enables chat
api_urlnoneBackend base URL (required when enabled)
personabackend defaultPersona id
langvisitor’s browseren, fr, pt, …
themeautoauto follows the site; or dark / light
accentautoauto follows the site accent; or any CSS colour
loadclickclick (launcher), idle, or immediate
widgetbundledbundled, or an https:// URL you manage
fontssystemsystem or google
labelChatLauncher text
welcomepersona’s welcomeMessageFirst line in the chat window, for example Ask me about Meteorite themes. Plain text, 200 characters at most. Without it the widget uses the persona’s welcomeMessage from the backend, or a generic greeting

2. Use your site as the knowledge base (optional) #

poptalk-rag indexes documents in knowledge/<persona>/. Point Meteorite at that folder and every meteorite build keeps it in sync:

chat:
  provider: poptalk
  api_url: https://chat.example.com
  knowledge:
    dir: ../poptalk-rag/knowledge/my-site
    exclude: [drafts]                  # sections to leave out; `include:` limits instead
    rag_url: https://rag.example.com   # only for `meteorite chat sync`

Each published page becomes one Markdown file with the front matter needed to cite it:

---
title: Getting started
url: https://example.com/guide/getting-started/
section: guide
date: 2026-10-10
tags: [intro]
---

What is exported, and what is not:

  • Only what you publish: drafts, future-dated and expired pages are left out.
  • Shortcodes become their plain text, and the home page’s hero, features, stats, showcase, quote and cta blocks become prose, so nothing a visitor can read is missing.
  • Add chat: false to a page’s front matter to keep it out.
  • Meteorite only ever touches files it created (it keeps a .meteorite-export list). Your own documents in the same folder are left alone, and it refuses to overwrite a file it didn’t write.
  • The folder must be outside the site (not content/, themes/, public/ or the site root).
  • serve and check never export; meteorite build --no-export skips it too.

Getting the knowledge to poptalk-rag #

poptalk-rag reads a folder, and your server is usually not the machine that builds the site. Pick the delivery that fits:

HowWhen to use it
Publish and pull (recommended)Meteorite publishes knowledge.json with the site; poptalk-rag fetches itThe site and the RAG server are on different hosts
Shared folderchat.knowledge.dir points straight at poptalk-rag’s knowledge/<persona>/Build and RAG run on the same machine, or share a volume
Copy in CImeteorite build, then rsync the export folder to the serverYou already deploy by SSH

Publish and pull #

chat:
  provider: poptalk
  api_url: https://chat.example.com
  knowledge:
    publish: true            # writes public/knowledge.json with the site

On the poptalk-rag server:

KNOWLEDGE_SITE_SOURCES=my-site=https://example.com/knowledge.json

That is the whole setup. There is nothing to upload, no SSH key in CI and no extra credential: what poptalk-rag fetches is exactly what is live, and it is only content that is already public.

knowledge.json is a versioned, deterministic file (no timestamps; documents ordered by path), with a content hash per document and a digest for the whole file:

{ "version": 1, "generator": "meteorite 0.3.0", "site": "https://example.com/",
  "digest": "sha256:...",
  "documents": [ { "path": "guide/start.md", "title": "Getting started",
                   "url": "https://example.com/guide/start/", "section": "guide",
                   "tags": ["intro"], "hash": "sha256:...", "content": "# Getting started\n..." } ] }

poptalk-rag uses it conservatively: an unreachable site, a bad response, an unknown version or an empty list changes nothing, so a site outage never erases a knowledge base. Unchanged sites cost one conditional request. Change the file name with knowledge.publish_path.

Re-index immediately #

poptalk-rag polls on its own (every 10 minutes for site sources). To trigger a pass right after a deploy, for example from CI:

export POPTALK_RAG_KEY=...      # the persona's key; never put it in config.yml
meteorite build
meteorite chat sync

Privacy and security #

  • Keys never enter the site. config.yml is published with your site, so Meteorite refuses any chat: key that looks like a credential (api_key, token, secret, …). The persona key is read from the environment variable named by chat.knowledge.key_env.
  • Messages go to your backend and then to the AI provider you configured there. Say so in your privacy notice.
  • Keep load: click unless you have a reason not to; it is what makes “no request before the visitor opts in” true.