@acetrumtech/rag-chatbot-pro
v0.1.1
Published
Pro RAG chatbot plugin — DOCX/TXT/MD/CSV + website crawl, client dashboard, citations, semantic cache. Requires a paid license key (acetrum.com) to function; degrades to a free-tier subset without one.
Maintainers
Readme
@acetrumtech/rag-chatbot-pro
Pro version of rag-chatbot-plugin — imports/extends the free package, gated by a license check against our own license-server.
Published to public npm as @acetrumtech/rag-chatbot-pro — publicly
installable, but proprietary (see LICENSE.md) and non-functional without a
valid, paid license key. npm run publish:pro builds + obfuscates + publishes
in one step.
What's here (Step 3 — skeleton)
src/license.js— the license-check module: 24h local cache, livePOST /verifyagainst the license server, HMAC signature + freshness verification, 7-day grace period if the server is unreachable. Never throws — an invalid/unreachable license degrades to free mode, never crashes the client's site.src/hmac.js— same signing scheme aslicense-server/app/src/hmac.js, so a server response can be independently re-verified here.src/config.js—loadProConfig(): wraps the free package'sloadConfig(), adds aproconfig section, runs the license check, and setscfg.pro.isPro.src/index.js— re-exports the entire free package's API + the pro pieces above.
No pro-only features/routes exist yet — those land in later roadmap phases (Client Dashboard is next, Phase 1). This step is purely the license-gating foundation everything else will check against.
Config
In chatbot.config.js (or via env vars):
export default {
// ...all the usual free config...
pro: {
licenseKey: 'XYZC-XXXXXXXXXXXX', // the only thing a client actually types in
// domain: NOT set here — auto-detected from the first request's Host
// header (as `https://<host>/`), see note below. Only set this explicitly
// for an unusual case (e.g. running behind a domain the Host header won't
// reflect correctly, like some proxy setups).
// licenseServerUrl is NOT set here either — see note below.
// Step 9 — semantic cache. Off by default. Answers repeated/near-duplicate
// visitor questions from a cache instead of a fresh RAG search + LLM call —
// see src/semantic-cache.js for the full design notes (threshold tradeoffs,
// the "matches on latest question text only" scoping decision, and the
// enquiry-bypass heuristic that keeps it from swallowing real leads).
semanticCache: {
enabled: true,
threshold: 0.93, // cosine similarity cutoff — raise for stricter matching, lower for more hits
maxEntries: 500, // oldest entries pruned once exceeded
ttlHours: 168 // 7 days; entries older than this are treated as a miss
}
}
};A client only ever needs to set licenseKey. Everything else about
licensing is either infrastructure we own or something the plugin figures out
itself:
licenseServerUrlis a build-time constant, not client config. It's our infrastructure, the same for every client.build.jsinlines it (alongsideLICENSE_HMAC_SECRET) as a build-time constant via esbuild'sdefine— a distributed/obfuscated install never needsLICENSE_SERVER_URLset anywhere, and a client's.envnever sees our VPS address. (Still overridable per-install viapro.licenseServerUrlin chatbot.config.js if we ever need to point one specific install at a different server.)domainis auto-detected, not typed in.loadProConfig()leaves it unresolved at startup if not explicitly set;handler.tsderives it from the first real request'sHostheader (ashttps://<host>/) and runs the actual/verifycheck then, on whichever request arrives first — same fire-once, memoized-in-flight pattern already used for the RAG index build. This is why the license check log line (license OK (valid) — domain: ...) shows up after the first request hits the server, not at cold start. Still overridable viapro.domain/LICENSE_DOMAINfor the rare case where auto-detection isn't right (e.g. behind a proxy that rewrites Host). Caveat: the license server does a literal string match on domain, not a normalized one — a license created via the admin dashboard with a different string format (bareclientsite.cominstead ofhttps://clientsite.com/) won't match auto-detection's reconstructed format. Create licenses with thehttps://domain/format to match what auto-detection actually sends.
LICENSE_SERVER_URL/LICENSE_HMAC_SECRET env var equivalents exist only for
source/lib/ dev (see Local dev below) — there are no env var equivalents
for licenseKey/domain at all; both are code-level (a config prop / request
Host header), never env. Semantic cache env equivalents: SEMANTIC_CACHE_ENABLED (true/false),
SEMANTIC_CACHE_THRESHOLD, SEMANTIC_CACHE_MAX_ENTRIES, SEMANTIC_CACHE_TTL_HOURS.
The Client Dashboard's /admin page shows a "Cache hits (saved AI calls)" stat
once enabled. Cache is automatically cleared on POST /reindex (new/changed docs
could make old cached answers stale).
Dashboard URL note: /admin is relative to your configured server.basePath
(default /api/chatbot), so the actual URL is <your-api-url>/admin — e.g.
https://yoursite.com/api/chatbot/admin, not bare /admin. All of the
dashboard's own internal nav/links account for this automatically now (fixed
2026-08-06 — they used to be hardcoded to bare /admin/..., which 404'd on
every real (non-root-basePath) deployment the moment you clicked anything
inside the dashboard, even though the dashboard's own landing page loaded fine
at its correct URL).
Usage
import { loadProConfig, createProCoreHandler } from '@acetrumtech/rag-chatbot-pro';
const cfg = await loadProConfig();
console.log(cfg.pro.isPro ? `Pro (${cfg.pro.plan})` : 'Free mode');
const handle = createProCoreHandler(cfg);TypeScript
Source is TypeScript (src/**/*.ts), compiled by tsc to lib/ (type-checked,
with .d.ts output — this is what main/exports in package.json point at, and
what devtest//test/ run against). npm run build runs tsc then a small
postbuild step (strips a synthetic export {} from the compiled widget so it
stays valid as a classic <script> tag — see src/widget/widget.ts's comment).
The distributable tarball (what actually ships to paying clients) is a
separate, further step: npm run bundle (build.js, esbuild) minifies +
obfuscates lib/ into dist/, inlining LICENSE_HMAC_SECRET and
LICENSE_SERVER_URL at build time. npm run pack:pro runs both
(build → bundle → npm pack).
npm run build # tsc -> lib/ (compile + type-check)
npm run watch # tsc --watch, for local dev — restart devtest/run.js to pick up changes
npm run bundle # lib/ -> dist/ (obfuscated tarball build, needs LICENSE_HMAC_SECRET + LICENSE_SERVER_URL)Local dev / testing against the real license-server
cp .env.example .env # fill in LICENSE_HMAC_SECRET (must match license-server's .env) + LICENSE_SERVER_URL
npm install
npm test # builds (tsc) then exercises all license branches against localhost:4000
npm run test:cache # builds then runs the semantic-cache unit tests (no live API needed)Requires license-server running locally (docker compose up -d in that repo) with
at least one test license created via its /admin dashboard.
