# Architecture webavatar-core is a base SvelteKit project meant to be branched off for future webavatar client projects. Its job is to provide the core infrastructure those projects would otherwise reimplement from scratch — session management, auth tokens, and the realtime model connection — as a clean, extensible foundation rather than a finished product. ## Module layering Code that's part of this core infrastructure lives under `src/lib/` in three places, split by where it's allowed to run: - **`src/lib/core/`** — shared, isomorphic contracts (types, protocols, constants). Importable by both browser and server code. Must stay free of SvelteKit-specific magic (no `$env/*`, no server-only imports) so it can also be compiled standalone outside Vite when needed (see [REALTIME_SESSION.md](REALTIME_SESSION.md) for why that matters). - **`src/lib/server/core/`** — server-only implementations: session orchestration, provider adapters, token minting/verification, raw HTTP upgrade handling. SvelteKit enforces this boundary at build time — browser code cannot import from here, even transitively. - **`src/lib/client/core/`** — thin browser-side wrappers around the shared contracts. No business logic; just transport. This repo is a base template meant to be branched per client project, so there's a second, parallel set of directories for code that's specific to whichever project branches from it — `src/lib/project/`, `src/lib/server/project/`, `src/lib/client/project/`, mirroring the three-way split above. **Core never imports from project, only the reverse** — that one-way dependency is what lets a branched project pull template updates without merge conflicts in core files. Where a project needs something from both (e.g. merging core session defaults with a project's tool registry), that merge happens at a composition root — a repo-root file like `server.js` or `vite.config.ts`, not under `src/lib` and not bound by the core/project layering — never inside a core file. See [TOOLS.md](TOOLS.md) for a concrete example of this split in practice. Routes (`src/routes/`) stay thin and delegate into these — a route file is glue, not where logic lives. ## Foundational infrastructure decisions - **This app runs as a persistent Node process, not serverless.** The adapter is `@sveltejs/adapter-node` — configured inline in [`vite.config.ts`](../vite.config.ts)'s `sveltekit({ adapter: ... })` option, since this project has no separate `svelte.config.js` — started via a custom [`server.js`](../server.js) rather than adapter-node's default generated entrypoint. This is a hard requirement, not a preference: the realtime session relay holds a WebSocket connection to a browser and a second WebSocket connection to the model provider simultaneously, for the lifetime of a conversation. No serverless/edge adapter can hold connections like that. Any client project branching from this base inherits that constraint — plan hosting accordingly. - **Auth tokens are generic infrastructure, not tied to any one feature.** `src/lib/server/core/tokens.ts` mints/verifies short-lived HMAC-signed tokens as a reusable primitive, currently used to gate the realtime WebSocket handshake but intentionally not coupled to it. ## Subsystems | Subsystem | Doc | | --------------------------------------------------------------------------------------------- | ------------------------------------------ | | Realtime session management (OpenAI Realtime API today, extensible to other models/pipelines) | [REALTIME_SESSION.md](REALTIME_SESSION.md) | | Tool calling (server- and client-executed) and the core/project ownership boundary | [TOOLS.md](TOOLS.md) | | Analytics recording (sessions, messages, tool calls, errors) to a local SQLite database | [ANALYTICS.md](ANALYTICS.md) | Add a row here (and a corresponding doc) for each new subsystem as it's built — see the "Documentation" section in [AGENTS.md](../AGENTS.md) for when and how.