The deployed app is a single SvelteKit 2 / Svelte 5 process on adapter-node. State lives in per-room JSONL files; the browser talks to a room page, a form action and three small JSON/SSE routes.

Runtime layers

LayerImplementationNote
Edgenginx TLS vhost for chat.loca.zone, proxy to loopbackOwned with the app deployment; see [[operations
Servernode build/index.js from @sveltejs/adapter-node, chat.serviceHardened system-scope unit, runs as the loca user
StorageDATA_DIR/rooms/<slug>/events.jsonl plus room.json per roomNo database, no migrations; rollback is a file copy
ClientSvelte 5 runes ($state, $derived), keyed block listComponent-local state; display name kept in localStorage

Routes

RouteMethodPurpose
/GET, form action POSTRoom list and create-room form; title Chat · loca.zone
/r/<room>GETChat page with server-rendered history; title <Room title> · Chat
/api/statusGET{ assistant: { configured, model }, rooms }
/api/rooms/<room>/messagesGETRoom history from the JSONL log
/api/rooms/<room>/messagesPOST{ author, content }, content at most 8000 characters, answers 201 and fans out
/api/rooms/<room>/eventsGET (SSE)Live stream; replays from ?after=<ms> or Last-Event-ID

Room slugs derive from the title and match ^[a-z0-9][a-z0-9-]{0,31}$. There is no room-list API and no room baked into the code; rooms are public to anyone holding the link.

Backend boundary

  • Rooms are real and multi-user: every browser on the same room sees appended messages through SSE. There is no authentication and no identity guarantee; display names are self-declared.
  • The assistant is optional, never mocked. It is driven by an OpenAI-compatible upstream selected through environment variables (see Assistant environment). On this host no key-free model exists, so the deployment ships with the assistant unconfigured: /api/status reports assistant.configured=false, a message starting with @ai yields a system message stating that no model is configured, and the UI shows a standing “Assistant offline” notice.
  • No third-party services are called unless the upstream is configured.

Render pipeline

message text → splitBlocks(src, streaming) → renderBlock() per block → keyed {#each} → sanitized {@html}
PieceWhat it guaranteesSource
blocks.ts splitBlocksClosed blocks get content-hash ids and never re-key; the growing tail keeps a fixed tail / tail-code key; fences close only on a CommonMark closer, never on the last line while streamingResearch proof, replayed char-by-char in seven cases with zero closed-id churn
render.ts renderBlockmarked (GFM, breaks: true so single newlines become line breaks, a chat-specific deviation from the proof) then isomorphic-dompurify with a forbidden-tag list and no data attributesBare dompurify throws during SSR; the isomorphic build is required
SvelteKit CSPscript-src 'self' with Kit nonces, img-src 'self' data: blob:Sanitizing alone does not stop image-load exfiltration; the CSP does

Stable keys are not render caching: a block still re-renders when its own content changes. The 124.6× figure in Research counts block renders on a 590-character document, not wall-clock time.

Wired versus evaluated

CapabilityDeployedEvidence
Multi-user rooms, history, SSE fan-outyeslive routes above
Streaming-safe Markdown blocks, sanitizer, CSPyescarried over from the research proof
OpenAI-compatible assistantcode present, not configured/api/status
Virtualized message list (virtua)noevaluated only
Voice: STT, TTS, VAD (speaches, kokoro-js, vad-web)noevaluated only
Math (katex), rich highlighting (shiki), diagrams (mermaid)noevaluated only
Offline / PWA / resumable streamsnoevaluated only
Auth, accounts, moderationnonot implemented; treat identity as unverified

Runtime dependencies are limited to marked and isomorphic-dompurify; the rest of the research stack stays a recommendation in Research.