Layers
KB/server.ts (Bun.serve, port 3001)
├── /api/chat → Anthropic SDK agentic loop (search_web tool)
├── /api/search-web → Serper.dev passthrough
└── static (default) → KB/public/index.html
└── /src/frontend.tsx (bundled by Bun on demand)
└── React App (KB/src/frontend.tsx:16)
├── <Sidebar> (article list + search)
├── <LegalDisclaimer> (banner)
├── <ArticleViewer> (selected article)
├── <ChatPanel> (Ask Claude)
└── <SettingsModal> (API keys)Server
Single file, no framework. KB/server.ts:306 calls Bun.serve({ port, fetch }). The fetch handler dispatches by (method, pathname).
CORS is wide-open: Access-Control-Allow-Origin: * (KB/server.ts:7–11). Suitable for local dev only.
The chat route returns a text/event-stream Response whose body is a ReadableStream. The stream coroutine runs the Anthropic agentic loop until stop_reason !== "tool_use", then chunks the final text into 500-char SSE data: slices to give a streaming feel (KB/server.ts:201–214). This is not native Anthropic streaming — stream: false is set on the SDK call (KB/server.ts:147).
Frontend
Entry: KB/src/frontend.tsx. Mounts <App> into #root under React.StrictMode. Single screen, three columns:
- Sidebar (fixed 256px) — article tree grouped by category, fuzzy search input, settings button, theme toggle.
- Main column —
<LegalDisclaimer>banner above<ArticleViewer>. - Floating chat panel — slides in from the right when “Ask Claude” is clicked.
State lives in App (useState for selectedArticle, isChatOpen, isSettingsOpen). Settings come from useSettings (KB/src/hooks/useSettings.ts).
Article registry
KB/src/kb/index.ts exports allArticles: KBArticle[] — 10 inline articles. The frontend imports this once via import { allArticles } from "./kb/index" (KB/src/frontend.tsx:7) and passes it to <Sidebar>.
Warning
Per-topic files under
KB/src/kb/gdpr/*.ts,KB/src/kb/swedish-law/*.ts,KB/src/kb/b2b-enrichment/*.ts, andKB/src/kb/enforcement/*.tsexist but are not imported anywhere. They also have a broken import path (from "../../types"instead offrom "../../lib/types"— only thegdpr/andswedish-law/files use the correct path; the rest are broken). They appear to be a planned-but-unwired expansion. See KB Content Index for the gap.
Hooks
useSettings— keys + model, persisted tolocalStorage(kb_anthropic_key,kb_serper_key,kb_model).useTheme— dark/light, persisted tolocalStorage(kb_theme), respectsprefers-color-schemeon first load.useSearch— Fuse.js index overtitle/summary/tags, threshold 0.35.useChat— POSTs to/api/chat, streams SSE response, accumulates assistant message.
See also
KB Overview, KB Chat Flow, KB Search, KB Settings, KB UI Components.