From 4f3de1b1c705507fac45b2cac51bf086255ea667 Mon Sep 17 00:00:00 2001 From: Chrysopedia Bot Date: Fri, 3 Apr 2026 22:41:16 -0500 Subject: [PATCH] docs: add Player, Impersonation pages; update LightRAG eval results (M020/S07) --- Authentication.md | 38 +++++++++++++- Impersonation.md | 124 ++++++++++++++++++++++++++++++++++++++++++++++ Player.md | 86 ++++++++++++++++++++++++++++++++ _Sidebar.md | 2 + 4 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 Impersonation.md create mode 100644 Player.md diff --git a/Authentication.md b/Authentication.md index 40601d1..c49abdb 100644 --- a/Authentication.md +++ b/Authentication.md @@ -154,6 +154,42 @@ LightRAG is a graph-based RAG (Retrieval-Augmented Generation) service added to LightRAG provides graph-structured knowledge retrieval as a complement to Qdrant's vector similarity search. It runs as a standalone service within the Docker Compose stack, sharing Qdrant and Ollama with the main application. + +## Admin Impersonation + +Admins can view the site as any creator. Full details on the dedicated [[Impersonation]] page. + +- **Mechanism:** Impersonation JWT with `original_user_id` claim and 1h expiry +- **Safety:** Write endpoints blocked via `reject_impersonation` dependency +- **Audit:** All start/stop actions logged to `impersonation_log` table with admin ID, target ID, IP, timestamp +### Evaluation Results (M020/S05) + +A/B comparison of 25 queries (13 real user queries, 12 curated) against Qdrant search and LightRAG hybrid mode: + +- **LightRAG wins:** 23/25 queries on relevance scoring +- **Qdrant wins:** 2/25 (short query rejected by LightRAG, ambiguous incomplete query) +- **Avg relevance:** Qdrant 2.09/5, LightRAG 4.52/5 +- **Avg latency:** Qdrant 99ms, LightRAG 86s + +**Key finding:** LightRAG is a RAG system producing synthesized multi-paragraph answers, not a search engine. The two serve different interaction patterns. + +### Recommended Hybrid Routing + +- **Autocomplete / typeahead** → Qdrant (sub-100ms required) +- **Search results list** → Qdrant (users expect instant ranked results) +- **"Ask a question" / chat** → LightRAG (synthesized answers are the core value) +- **Deep-dive / explore** → Both (LightRAG answer + Qdrant related pages sidebar) + +### Creator-Scoped Retrieval + +LightRAG has no metadata-based filtering. Creator scoping uses `ll_keywords` to bias retrieval toward a specific creator name (soft bias, not hard filter). Each document's text includes structured provenance: Creator name, Creator ID, Source Videos, and per-key-moment source attribution. + +Query utility: `backend/scripts/lightrag_query.py --query "snare design" --creator "COPYCATT"` + +### Data Coverage + +LightRAG reindex in progress. Entity types configured: Creator, Technique, Plugin, Synthesizer, Effect, Genre, DAW, SamplePack, SignalChain, Concept, Frequency, SoundDesignElement. + --- -*See also: [[Architecture]], [[API-Surface]], [[Data-Model]]* +*See also: [[Architecture]], [[API-Surface]], [[Data-Model]], [[Impersonation]], [[Player]]* diff --git a/Impersonation.md b/Impersonation.md new file mode 100644 index 0000000..0673187 --- /dev/null +++ b/Impersonation.md @@ -0,0 +1,124 @@ +# Admin Impersonation + +Admins can view the site as any creator to debug permission issues, verify consent settings, and test the creator experience. Added in M020/S04. + +## Overview + +``` +Admin clicks "View As" on /admin/users + │ + ▼ +POST /api/v1/admin/impersonate/{user_id} + │ + ├─ Verify caller is admin + ├─ Create impersonation JWT (1h expiry, original_user_id claim) + ├─ Log to impersonation_log table + │ + ▼ +Frontend swaps JWT, stashes admin token in sessionStorage + │ + ▼ +Amber banner: "Viewing as {Creator Name} — Exit" +All API calls use target user's identity +Write endpoints blocked by reject_impersonation guard + │ + ▼ +Click "Exit" → POST /api/v1/admin/impersonate/stop + │ + ├─ Log stop action to impersonation_log + ├─ Restore admin token from sessionStorage + │ + ▼ +Back to normal admin session +``` + +## Security Model + +- **Read-only:** Write endpoints are guarded by `reject_impersonation` FastAPI dependency. Returns 403 if the current token has `original_user_id` claim +- **Short-lived:** Impersonation tokens expire in 1 hour (vs 24h for normal tokens) +- **Transparent:** The impersonation JWT uses `sub=target_user_id`, so existing `get_current_user` loads the target user transparently. No special-casing in endpoint handlers +- **Admin-only:** Only users with `admin` role can call the impersonate endpoint + +## Audit Trail + +Every impersonation action is logged to the `impersonation_log` table: + +- `admin_user_id` — Who initiated +- `target_user_id` — Who is being impersonated +- `action` — "start" or "stop" +- `ip_address` — Client IP +- `created_at` — Timestamp + +Indexes on `admin_user_id` and `target_user_id` for efficient querying. + +## Token Structure + +Normal JWT: +```json +{ + "sub": "user-uuid", + "role": "admin", + "iat": 1234567890, + "exp": 1234654290 +} +``` + +Impersonation JWT: +```json +{ + "sub": "target-user-uuid", + "role": "creator", + "original_user_id": "admin-user-uuid", + "iat": 1234567890, + "exp": 1234571490 +} +``` + +The `original_user_id` claim is what `reject_impersonation` checks. + +## API Endpoints + +- `GET /api/v1/admin/users` — List all users (admin only) +- `POST /api/v1/admin/impersonate/{user_id}` — Start impersonation, returns new JWT +- `POST /api/v1/admin/impersonate/stop` — Stop impersonation, logs action + +## Frontend Integration + +### AuthContext Extensions + +- `startImpersonation(userId)` — Calls impersonate API, saves current admin token to `sessionStorage`, swaps to impersonation token +- `exitImpersonation()` — Calls stop API, restores admin token from `sessionStorage` +- `user.impersonating` (boolean) — True when viewing as another user + +### ImpersonationBanner + +Fixed amber bar at page top when impersonating. Shows "Viewing as {name}" with Exit button. Rendered in `AppShell` when `user.impersonating` is true. + +### AdminUsers Page + +Route: `/admin/users`. Table of all users with "View As" buttons for creator-role users. Code-split with `React.lazy`. + +## Key Files + +- `backend/auth.py` — `create_impersonation_token()`, `reject_impersonation` dependency +- `backend/routers/admin.py` — Impersonate/stop endpoints, user list +- `backend/models.py` — `ImpersonationLog` model +- `alembic/versions/018_add_impersonation_log.py` — Migration +- `frontend/src/context/AuthContext.tsx` — Impersonation state management +- `frontend/src/components/ImpersonationBanner.tsx` — Amber warning banner +- `frontend/src/pages/AdminUsers.tsx` — User management page + +## Database + +**Table: `impersonation_log`** (Migration 018) + +- `id` UUID PK +- `admin_user_id` UUID FK → users +- `target_user_id` UUID FK → users +- `action` VARCHAR (start/stop) +- `ip_address` VARCHAR +- `created_at` TIMESTAMP + +--- + +*See also: [[Authentication]], [[API-Surface]]* diff --git a/Player.md b/Player.md new file mode 100644 index 0000000..308c6d5 --- /dev/null +++ b/Player.md @@ -0,0 +1,86 @@ +# Web Media Player + +Custom video player with HLS playback, synchronized transcript sidebar, and keyboard shortcuts. Added in M020/S01. + +## Architecture + +``` +┌──────────────────────────────────────────────────────────┐ +│ WatchPage (/watch/:videoId) │ +│ │ +│ ┌─────────────────────┐ ┌────────────────────────────┐ │ +│ │ VideoPlayer │ │ TranscriptSidebar │ │ +│ │ ┌────────────────┐ │ │ Segments sorted by time │ │ +│ │ │