Archives are the authoritative content source. SQLite FTS and vector rows are disposable representations. Per-archive lexical search retains a bounded native-ZIM fallback only for a missing index; all-library search remains indexed-only. Rebuilding or deleting an index does not mutate an archive.
One local SQLite file provides FTS5, popularity, and vector storage without a separate database service. The optional index.backend: sqlite setting is accepted for compatibility.
cmd.App owns configuration and input/output streams for one invocation. main owns signal creation and one exit translation. Configuration uses a fresh loader per app so tests and concurrent invocations cannot leak Viper state.
The library service owns configured-root path resolution. The indexer and search packages own narrow interfaces at their consumption boundaries. Archive handles are cached and synchronized by the archive manager. Domain packages do not depend on CLI or web presentation packages.
The standard-library server hosts the embedded Svelte SPA and JSON API. serve is canonical; ui is a deprecated alias. Request-local archive selection replaces mutable active-archive state.
Downloaded archive HTML is not application UI. It is served as a separate document with restrictive CSP and loaded by a sandboxed iframe. Validated rewriting keeps internal navigation and resources on the selected same-origin archive routes.
The compiled Svelte bundle is committed at internal/webui/dist, embedded into the Go binary, and regenerated from web/ using npm ci && npm run build. Go builds and tests therefore work from a clean archive without Node.