# Universe Library — Plan The web front-end for a demarkus universe: a reading room over a broker-fronted knowledge system. Renders the library metaphor demarkus already has (bookshelf server, librarian agent, LOOKUP card catalog, worlds-as-collections) into a human-facing web app, so a newcomer can browse a universe and have the architecture explain itself. Lives in its own repo on its own release cadence, like the Obsidian plugin. ## Why Every demarkus surface today is agent- or terminal-facing (CLI, TUI, MCP, Obsidian, the Claude Code plugins); the one third-party GUI (Caztor) is view-only FETCH. There is no human-facing web view — the real barrier to understanding demarkus. A universe library lowers that barrier by being self-explanatory: walk in, browse the stacks, search the catalog, watch the librarian work. ## Non-goals (hard) - Not a Notion clone. No block model, no relational databases/views, no live multiplayer co-editing — each fights the dumb-server / whole-doc-immutable-version core. - Editing is a cataloging desk: whole-doc PUBLISH to a new edition (version). Concurrency via the existing conflict-aware merge, not OT/CRDT. - Metaphor is information architecture, not skeuomorphic chrome. No bookshelf textures, no page-flip animation. ## Architecture A browser cannot speak QUIC/Mark. Go through the broker, which already terminates HTTPS, owns identity (OIDC + auth-code/PKCE/DCR), and proxies the 14-tool MCP surface to worlds. The front-end is a **server-rendered Go web app with htmx** — not a SPA. The app is fundamentally hypermedia (documents, links, navigation, a catalog you click through), which is htmx's sweet spot and a poor fit for SPA client-state. This collapses a tier: no separate REST/JSON projection consumed by a JS framework — the Go server renders HTML (and htmx fragments) directly and is itself an MCP client to the broker. ``` Browser (HTML + htmx) --HTTP / cookie--> Go app --MCP / HTTPS--> Broker --QUIC--> Worlds ~14kb htmx, JS islands html/template + goldmark identity, bookshelves only where earned OAuth session, MCP client world routing ``` - **Go app**: html/template for pages + htmx fragments; goldmark renders markdown to (sanitized) HTML server-side — same ecosystem as the rest of the project (the TUI uses Glamour for terminals, web uses goldmark for HTML). Owns the human OAuth session (tokens never reach the browser — the XSS boundary). As an MCP client to the broker it reuses the broker's existing session model — the same path Claude Code's MCP SDK takes; no new session design. A thin adapter, not a second brain — the librarian principle holds (intelligence stays at the edge/agent). - **JS islands only where earned**: a CodeMirror island for the editor (Phase 3), SSE for the streaming librarian (Phase 4), a canvas graph later. Everything else is htmx attributes. - **Single binary, no Node build** — matches demarkus's single-Go-binary ethos. SSR is the default, so Phase 5's shareable-links-that-render come free rather than as bolted-on SPA work. - **Security**: rendering org-authored markdown to HTML requires output sanitization (bluemonday) — markdown-to-HTML XSS is a real path. ## Identity model Access is **organization-scoped**: the library card is your org identity via the broker's OIDC (e.g. GCP org). No anonymous public tier for now — you need a card to enter your org's universe. The exact card-issuance UX is deliberately left open; the firm decision is the *scope* (per-organization), not the mechanism. Consequence: the org login is the turnstile, so it lands in Phase 1, not later. ## Phases ### Phase 0 — Foundation spike Repo, stack, and one doc on screen end-to-end. De-risks transport + server-side markdown rendering + MCP session handling before any UI investment. Stays auth-free by pointing at a local/dev world (or a public doc if one exists), so OAuth is deferred to Phase 1. - New repo `latebit-io/demarkus-library`; CI, license, README. - Go app skeleton: one route that FETCHes a doc, renders it with goldmark (sanitized), serves HTML. - Exit: a real demarkus doc renders in a browser, server-rendered. ### Phase 1 — Library card + reading room Org login is the turnstile; then browse and read the org's universe. Read-only. - Library card: broker OAuth (auth-code + PKCE + DCR), org-scoped; cookie session; tokens server-side only. Login/logout. - World list / floor directory from the `root` hub. - Stacks: per-world LIST path tree, navigable (htmx fragment swaps). - Document view: goldmark-rendered GFM; rewrite `mark:///` links to in-app navigation. - Card catalog: LOOKUP search (debounced `hx-get`, results as an HTML table to click-through). - Editions: VERSIONS panel; view a historical version (fragment swap). - Citations: backlinks panel (`mark_backlinks`); a lightweight "cited by" list (defer force-directed graph viz). - Exit: an org member logs in, browses, searches, reads, and traces citations across the org's universe. ### Phase 2 — Restricted collections (within-org) Finer-grained access on top of "you're in the org." - Per-path read-auth respected: locked collections shown, unlocked when the token permits. - Per-user surface: home, recently read, bookmarks. - Exit: a user sees only the collections their token permits within their org. ### Phase 3 — The cataloging desk (writes) - Markdown editor (CodeMirror island); live preview via `hx-post` to a server render. Save is a form POST to PUBLISH, creating a new edition. - Conflict-aware merge surfaced in the UI (existing diff3) on concurrent edits. - Create new document; archive (deaccession) soft-delete. - Metadata/cataloging editor: title + tags + importance (feeds LOOKUP — cataloging literally improves the catalog). - Exit: a librarian creates, edits, re-catalogs, and archives docs from the browser; whole-doc, version-per-save. ### Phase 4 — The AI librarian (the differentiator) - Embedded assistant driving the same MCP tools (LOOKUP/FETCH/graph/VERSIONS) to answer natural-language questions, summarize a wing, trace citations. - Streaming via SSE (`hx-ext="sse"`): token stream + a live, visible tool-call trace — watch the librarian work the catalog. Spike the SSE pattern before committing the design. - Claude via the API with the broker MCP surface wired as tools (MCP connector / tool use). - Lands here because it needs a furnished building to work in. - Exit: "where's our deploy runbook?" returns the doc with its catalog reasoning shown. ### Phase 5 — Public face & polish - Universe home as front-of-house: featured collections, recent acquisitions (recent publishes), most-cited. - Shareable public doc URLs with OpenGraph cards — already server-rendered, so this is polish, not new infra. The adoption payoff: a shared link explains demarkus on open. - Catalog caching, accessibility, mobile reading, theming. ## Cross-cutting decisions - Front-end: server-rendered Go app + htmx + goldmark; JS islands only where earned. (Alt considered: React/TS SPA — rejected; the app is hypermedia, the SPA adds a JSON tier and a Node build for little gain.) - Transport: the Go app is an MCP client over the broker MCP gateway. (Alt: browser-direct MCP — rejected for token exposure.) - Auth: organization-scoped OIDC (e.g. GCP org); no anonymous public tier for now. Tokens server-side only; the browser holds a cookie session. - Markdown: goldmark render + bluemonday sanitize. - Graph viz: backlinks list first; force-directed graph (canvas island) deferred past v1. - Repo: standalone `latebit-io/demarkus-library`, own cadence (mirrors `obsidian-demarkus`). ## Build model strategy Build the project on **Opus 4.8** by default — it is the most capable GA model, state-of-the-art on long-horizon agentic coding, half the cost of Fable 5 ($5/$25 vs $10/$50 per MTok), and this is a conventional Go + htmx web app where its ceiling is more than enough. Keep **Fable 5** as the escalation valve for the few genuinely hard, high-leverage moments: the broker/MCP transport + auth-session design, a concurrency or token-refresh bug Opus stalls on, or a one-shot decision where being wrong is expensive. Pattern: plan and implement on Opus 4.8, escalate a specific stuck or high-stakes sub-problem to Fable 5, drop back. Don't run the whole build at the premium tier. ## Resolved - **Front-end (2026-06-10):** server-rendered Go + htmx, not a SPA. The app is hypermedia; htmx collapses the REST/JSON tier, keeps one Go binary with no Node build, and makes SSR (shareable links) the default. JS islands only for the editor (CodeMirror), the streaming librarian (SSE), and the eventual graph. - **Library card (2026-06-10):** scope to **organizations** (per-org OIDC, e.g. GCP org). Issuance UX deferred; scope is the firm part. No anonymous public tier — auth is the Phase 1 turnstile. - **World enumeration (2026-06-10):** the **`root` hub** is the world directory for v1. Caveat: hand-maintained, so an unlisted world is invisible; add a broker enumeration endpoint later only if the hub drifts. - **MCP session model (2026-06-10):** **same as the broker** — the Go app is an MCP client over the broker's existing session path (the route Claude Code's SDK uses). No new session design. - **Build model (2026-06-10):** Opus 4.8 by default, Fable 5 as the escalation valve. See Build model strategy. ## Open / still to confirm - Whether the broker exposes any anonymous-readable surface at all (only affects whether the Phase 0 spike can hit the broker directly vs a local dev world). ## Naming Repo `demarkus-library` (working). Product-name candidates: Athenaeum, Stacks, the Reading Room, or literal "demarkus Library". TBD — not load-bearing. ## First session in the new repo (handoff) When the `latebit-io/demarkus-library` repo exists and a fresh Claude Code session boots there: 1. Fetch this plan: `mark_fetch /plans/universe-library.md` from demarkus-soul — it is the source of truth, not a local copy. 2. Start Phase 0: scaffold the Go module, a single route that FETCHes one demarkus doc and renders it through goldmark + bluemonday, and serve the HTML. Point at a local dev world (or a known public doc) to stay auth-free. 3. Resolve the one open question in passing — does the broker serve any anonymous reads — since it decides whether Phase 0 can hit the broker directly. 4. Exit Phase 0 when a real demarkus doc renders in a browser, server-rendered. Then move to Phase 1 (library card + reading room).