This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
oa-chat is a ChatGPT-like AI chat app that implements unlinkable inference — AI inference where every request is verifiably decoupled from each other, and from your identity. It communicates directly with inference backends (OpenRouter by default) using ephemeral access keys obtained via blind signatures. Everything runs in the browser with no server backend. Development is HTML-first; production builds are bundled with esbuild and minified with terser.
Before touching implementation, read docs/PRIVACY_MODEL.md and the relevant files in
docs/ to understand the current product state. If the task touches UI or complicated
state transitions, read docs/APP_STATE.md first and then any feature-specific doc that
applies.
Only after that should you inspect the code paths you expect to change and implement the
work. When you finish meaningful investigation or implementation, update the relevant
doc in docs/ and record the non-obvious learnings in docs/APP_STATE.md so the next
agent can pick up the state without rediscovering subtle behavior from scratch.
# Local development server (from repo root)
npm run dev
# Visit http://localhost:8080The app uses <base href="/"> to resolve all relative paths. Keep devtools open; console warnings highlight integration issues early.
# Production build + preview
npm run build
npm run preview
# Tailwind
npm run tailwind:build
npm run tailwind:watch
# Fonts (self-hosted Google Fonts)
npm run fonts:syncEntry Point Flow:
index.html→ pre-hydrates theme/panel state → loads local vendor assets + precompiled Tailwind CSS → lazy-loads libcurl.js for proxy → runsprelude.js(empty-state render) → bootsapp.js
Core Files:
app.js: MainChatAppcontroller — orchestrates state, components, streaming, keyboard shortcuts, and session/message CRUD viachatDBapi.js: OpenRouter client for fetching models and streaming completionsdb.js: IndexedDB wrapper (ChatDatabase) exported aschatDB— stores sessions, messages, settings (also assigned towindow.chatDBfor legacy access)
Components (components/):
Sidebar.js,ChatArea.js,ChatInput.js,ModelPicker.js,RightPanel.js,MessageTemplates.js,MessageNavigation.js- Pattern: Event delegation + state sync; components delegate to app, unidirectional data flow
Services (services/):
inference/: Backend abstraction layerinferenceService.js: Registry routing to backendsbackends/openRouterBackend.js: Default implementationbackends/enclaveStationBackend.js,providerDirectBackend.js: Stubs for future backends
networkProxy.js: Encrypted WebSocket proxy via libcurl.js/mbedTLS with TLS inspectionticketClient.js: Ticket lifecycle and access issuance (Privacy Pass integration)networkLogger.js: In-memory request logging with header sanitization
Local Assets:
chat/vendor/: Marked, KaTeX (+ fonts), Highlight.js, libcurl.js, hash-wasm, html2pdfchat/fonts/: self-hosted Google Fonts (fonts.css+ WOFF2 files, managed viascripts/sync-fonts.mjs)
Additional Modules (in development):
embeddings/: Text embedding service with pluggable backendsvector/: Vector store with IndexedDB backend and Orama integrationlocal_inference/: Local inference backends (Ollama, vLLM, WebLLM, HTTP OpenAI)
- ES modules with 4-space indentation, trailing semicolons
- PascalCase for classes, camelCase for functions/methods
- Use relative paths for assets (resolved via
<base href="/">) — never hardcode absolute paths - Tailwind utilities from
tailwind.generated.css; custom tweaks instyles.csswith rationale - All persisted data flows through
chatDB; keep transactions minimal
Data Storage:
- IndexedDB: sessions, messages, settings (via
db.js) - localStorage: theme (
oa-theme-preference), wide mode (oa-wide-mode), panel visibility (oa-right-panel-visible), proxy settings (oa-network-proxy-settings) - Network logs are memory-only (tab-scoped)
Access Keys:
- Ephemeral, acquired via Privacy Pass ticket redemption in
ticketClient.js - Never hardcode keys; do not log secrets (use
sanitizeHeadersinnetworkLogger.js)
Adding New Backends:
- Implement in
services/inference/backends/ - Route through
inferenceService.js
⌘/— New chat⌘K— Model picker⌘⇧F— Focus session searchEsc— Close modals/menus
No automated test harness. Manual verification checklist:
- Sessions: create/switch/delete, title auto-generation, ordering
- Messages: Markdown + LaTeX rendering, streaming, token counts, reasoning traces, citations
- Model picker: fuzzy search, pinned/disabled models
- Right panel: ticket registration, key request, expiry countdown, proxy toggle, TLS security
- Themes: system/light/dark persistence with pre-hydration
- WASM flows: exercise success and error paths
See AGENTS.md for comprehensive architecture details, testing guidelines, and security notes.
See docs/APP_STATE.md for the living handoff log of current UI/state behavior and recent learnings.