# Product > Claims cite the design doc they come from where the source is not obvious. > Anything genuinely undecided is marked **[undecided]** rather than invented. ## Platform web ## Users **One person: the operator who self-hosts this instance and owns the corpus.** Today that is Tom Vaucourt on his own box. `owner_id` is a constant `1` (`DECISIONS.md` #2 — single-user behaviour, multi-user-ready schema), so there is no second audience with different permissions, and there is no user management, no roles, no invitations. Three situations, and they are not the same job: 1. **At the terminal, mid-batch.** Something has been running for twenty minutes and the operator wants to know whether it is working, waiting, or quietly broken — at 03:00, on a laptop, over an SSH tunnel. 2. **After the fact, answering a question about the index itself.** Which model transcribed this? Why does this video have no on-screen text? What did OCR actually read off that frame, and where on the frame did it read it? 3. **On the phone, between two things.** A notification says a new video is worth a look. The owner has a minute, one thumb and no terminal: read the verdict, jump to the moment on YouTube, or hand the video to Claude (`docs/design/companion.md` §6). Nothing on this screen is about the machine. A third consumer exists and is not a person: **an MCP client** (Claude and friends) talking to `/mcp`. Agents are the right consumer of the corpus and the wrong consumer of the corpus's plumbing — that inversion is the stated reason the dashboard exists (`docs/design/dashboard.md`, opening decision). ## Product Purpose **vidtheque empowers AI with the knowledge of the builders and creators** (`positioning.md`, the position). It turns the channels you follow — the videos you *didn't have time to watch* — into solid, timestamped knowledge: every sentence spoken, every line that crossed the screen, every frame, and it answers with **receipts that land on the exact second** (`https://youtu.be/ID?t=123`) — the sentence, the slide, and the second. Your agent watched it so you didn't have to. It is a self-hosted MCP server: two services (`mcp/` on CPU, `worker/` on GPU), one SQLite index, HTTP between them. Success is a question answered with a link that proves it. A vidtheque answer that cannot be checked against the source video has failed even when it is correct. ## Positioning **The contract is `docs/design/positioning.md` (LOCKED, Tom, 2026-08-10)** — the position, the three pillars (follow the builders · your agent watched it · receipts, always), the twin line, the personas, the blessed and banned vocabulary, and the word law (ownership attaches to the *choice*, never the viewing; the agent is the one who watched). Every public string on every surface answers to it. What belongs here is the substantiation — the mechanism a neighbouring product could not truthfully copy: - **Three channels over one timeline.** Spoken transcript, on-screen text (OCR, with the box coordinates kept), and frame embeddings all indexed against the same second of the same video, so a query can be answered from whichever channel actually holds the evidence and the citation still lands. - **Provenance is a first-class column, not a log line.** Every stage of the pipeline (`fetch · stt · chunk · text_embed · keyframe · ocr · frame_embed`) records its own state, its own `model_key`, and its own clock, per video. The index can therefore explain what it is made of, and admit what it is missing. - **`all` means all.** A leg or a filter that cannot apply prints a `note:`; it never silently narrows the result (`CLAUDE.md` invariant). - **Self-hosted and single-operator by construction.** Your corpus stays on your box, on one SQLite file, with one writer. ## Operating Context - **Deployment:** `docker compose up -d` in `deploy/`; one image, one process for `mcp/`, an optional GPU `worker/`, an optional `cloudflared` tunnel profile. `deploy/.env.example` is the document of record for every knob. - **Access:** most often over an **SSH tunnel on an arbitrary local port**, so every asset URL the surfaces emit must survive being served from a prefix nobody predicted. This is a shipped lesson, not a hypothetical (`docs/design/dashboard.md` §8). - **Auth modes:** `none` | `token` | `oauth` (`DECISIONS.md` #1). A browser that is not signed in gets a page, not a JSON blob. - **Four web surfaces, one product** (aligned 2026-08-11: `/` is the landing, the demo moved to `/demo` — commit `4ddd45d`): - `/` — the landing, static by design; the argument before the product. - `/demo` — the public demo, a read-only projection of the same corpus, gated by `VIDTHEQUE_PUBLIC_READONLY`. Its role per the positioning contract: **the proof** — the corpus on tap, ask it something; it searches and answers, it does not manage (`docs/design/demo-site.md` §6, `positioning.md` surface implications). - `/dashboard` — the management surface. Five routes: corpus overview, videos table, video detail, jobs, job detail. - `/feed` — the phone feed, behind the same sign-in: what should I watch? Three screens (feed, video, profile) and nothing the console shows. - **The GPU is leased.** On Tom's box the worker shares a 3090 with a llama.cpp LXC through `GPU_ACQUIRE_CMD`/`GPU_RELEASE_CMD` hooks; public defaults leave them unset. Jobs therefore wait for reasons that are real and need to be visible. - **Sources push back.** YouTube bot-checks and rate-limits are ordinary operating conditions (commit `a5b7cd4`), so "deferred, retrying in 300s" is a normal state the surfaces must be able to state plainly. ## Capabilities and Constraints **Capabilities.** Index a video, playlist or channel; **follow a channel, so matching uploads index themselves inside a daily budget of hours of video** (`docs/design/following.md`); hybrid search (FTS + vector, RRF) across transcript, on-screen text and frames; per-video summary; segment context; frame retrieval by signed URL; tagging; a job queue with retries, deferral and an event log; a management dashboard; a public demo projection. **Durable technical constraints — these bind every future surface:** - **`mcp/` ↔ `worker/` is HTTP only.** No Python imports across the boundary, not even in tests. All state (SQLite, keyframes, jobs) lives in `mcp/`. - **One SQLite file, one writer.** - **Every surface is Next.js (App Router) — React, CSS Modules, a build step, and the design tokens generated from `DESIGN.md`.** **No external requests at runtime**, and no HTML sink on any page: React escapes by construction. - **Every asset path is relative or root-relative**, never built from `PUBLIC_URL` — see the SSH tunnel above. - **Light and dark schemes are both first-class**, driven by `prefers-color-scheme`. A persisted three-state toggle is deferred to phase 3 and must arrive without an inline `