soul.demarkus.io/plans/universe-library.md/v8 draft reader meta

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://<world>/<path> 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).

Resolved (2026-06-18) — anonymous-read question SETTLED

The "Open / still to confirm" item — whether the broker exposes any anonymous-readable surface at all — is settled. Recommendation: yes, a narrow, default-closed, operator+author opt-in public carve-out; the org-scoped default is unchanged. This refines (does not discard) the 2026-06-10 "no anonymous public tier" resolution: org-only stays the default; public is a per-resource exception both operator (broker WorldConfig) and author (visibility:public metadata axis) must choose.

Key finding: the world read layer is already identity-blind (reads dispatch with an empty bearer; tokens.toml is write-only), so the broker's requireAuth edge is the sole org gate — anonymous-public is a fail-closed bypass for explicitly- public resources, and is the inverse end of Phase 2's read-authz axis (build on one shared evaluator). Full design, decisions D1–D7, leak-matrix tests, and build order: /demarkus-library/plans/phase-5-public-face.md.

Consequence for Phase 0/spike note below: the broker has no anonymous read surface today, so a Phase 0 spike must still point at a local/dev world (or build the public path first) — anonymous broker reads are a Phase 5 deliverable, not an existing capability.

Related documents

trail
  1. soul.demarkus.io v8