# demarkus-library Sub-project hub for **demarkus-library** — the web front-end ("Universe Library") for a demarkus universe: a server-rendered Go + htmx reading room over a broker-fronted knowledge system. Standalone repo `latebit-io/demarkus-library`, own release cadence (mirrors `obsidian-demarkus`). All durable knowledge about this project lives under `/demarkus-library/`. This hub is the discovery backstop — keep it current. **Resuming?** Start with [Roadmap & Resume](/demarkus-library/roadmap.md) — how to run today, the file map, and the Phase 1b task breakdown (Fable 5 vs Opus). ## Source of truth - [Plan — Universe Library](/plans/universe-library.md) — the authoritative plan (architecture, identity model, phases 0–5, build-model strategy, resolved decisions, open questions). Fetch this first in any new session. ## Sections - [Roadmap & Resume](/demarkus-library/roadmap.md) — status, how to run, Phase 1b breakdown with Fable-5 tagging - [Architecture](/demarkus-library/architecture.md) — app structure, transport, MCP-client/session model, render pipeline (TBD) - [Decisions (ADRs)](/demarkus-library/adr/) — one file per decision at `adr/-.md` - [ADR 0001 — Echo + bulwarkauth-style layout](/demarkus-library/adr/0001-echo-bulwarkauth-layout.md) - [ADR 0002 — Hexagonal (ports & adapters) architecture](/demarkus-library/adr/0002-hexagonal-architecture.md) - [ADR 0003 — SSR-first, htmx-hard, no JSON](/demarkus-library/adr/0003-htmx-ssr-philosophy.md) - Patterns — Go + htmx + goldmark idioms, conventions (TBD — doc not yet written) - [Debugging](/demarkus-library/debugging.md) — bugs, gotchas, investigations (TBD) - [Debt](/demarkus-library/debt.md) — technical debt, deferred work (TBD) - [Journal](/demarkus-library/journal/) — session notes, one file per day at `journal/<YYYY-MM-DD>.md` - [2026-06-10](/demarkus-library/journal/2026-06-10.md) — Phase 0 + Phase 1a shipped & merged ## Status - **Phase 0 — Foundation spike: DONE, merged to `main`.** A real demarkus doc renders server-side in a browser over the QUIC fetch client. - **Phase 1a — Reading room (features-first): DONE, merged to `main` (PR #2).** Built over the current direct-QUIC world. Routes: `/` (default doc), `/d/<path>` (document, or the **stacks** listing when path ends in `/`), `/search?q=` (**card catalog**, LOOKUP, debounced htmx, click-through, real `<form>` so it works without JS), `/versions/<path>` (**editions**), `/health`. **Link rewriting** maps internal doc/dir/version links to in-app `/d/<path>`. **htmx fragment-vs-page**: targeted swaps return the `content` partial, boosted nav + no-JS return the full `page`. x/net on v0.56.0 (CVE-patched), govulncheck clean. - **Phase 1b — Library card (the turnstile): NEXT.** Broker MCP-gateway outbound adapter implementing `port.WorldGateway` (+ org-scoped OAuth login, cookie session, tokens server-side) — swapped in at the composition root, core/web untouched. Task breakdown + Fable-5 tagging in the [roadmap](/demarkus-library/roadmap.md). This is where the open anonymous-reads question gets resolved. - **Deferred:** backlinks / "cited by" (no server verb — needs a graph crawl; plan defers the viz); world directory / floor (multi-world from the `root` hub). ## Architecture — hexagonal (ports & adapters) Dependencies point inward (adapters → ports → core); the core knows nothing of Echo, QUIC, or goldmark. See **ADR 0002**. Echo v5 + bulwarkauth idioms retained (ADR 0001). ``` cmd/demarkus-library/ composition root — wires adapters into the core internal/core/ domain/ document.go entities + sentinel errors (no external deps) port/ port.go inbound + outbound port interfaces (ReadingService; WorldGateway, Renderer) service/ reading.go application core — Read/Browse/History/Search internal/adapter/ inbound/web/ Echo handlers/routes/view + link rewriting (driving adapter) outbound/world/ demarkus QUIC fetch (Fetch/List/Versions/Lookup) → port.WorldGateway outbound/markdown/ goldmark + bluemonday → port.Renderer ``` `replace` directives point demarkus `client`/`protocol` at local `../demarkus`. `.coderabbit.yaml` encodes the dependency rule as per-path review instructions. ## Front-end philosophy **SSR-first, htmx-hard, no JSON** — see [ADR 0003](/demarkus-library/adr/0003-htmx-ssr-philosophy.md). The server renders all HTML; htmx is the only interaction layer; no JSON API, no client-side state. Realized in 1a: link rewriting + `linkifyCatalogPaths` keep navigation server-driven; the `page`/`content` template split gives htmx fragments without a duplicate render path; the search box is a real `<form>` (degrades without JS). htmx is vendored (`web/static/htmx.min.js`, pinned to the latest release — currently **2.0.10**) and served from the binary — no CDN. JS islands are a last resort, each tracked as a concession in ADR 0003. ## Open questions - Does the broker expose any anonymous-readable surface? Decides whether Phase 1b hits the broker directly vs a dev world for unauthenticated reads. Resolve in 1b by checking `mcp_auth.go`/`mcp_gateway.go` in the broker source. ## Stack (decided) Server-rendered Go (Echo v5 + html/template + htmx), goldmark + bluemonday for markdown→sanitized HTML, MCP client over the broker MCP gateway (Phase 1b+), org-scoped OIDC (auth-code/PKCE/DCR), tokens server-side only. Single Go binary, no Node build. Go 1.26.4. Build on Opus 4.8 by default; Fable 5 as the escalation valve. ## Phase 1b plan (2026-06-11) - [Phase 1b — Web SSO over the Broker](/demarkus-library/plans/phase-1b-web-sso.md) — concrete two-repo plan (broker confidential web client → library redirect SSO + MCP gateway) - [ADR 0004 — Broker confidential web client + redirect SSO](/demarkus-library/adr/0004-broker-web-sso.md) — reject device flow; keep broker/library separate **Open question (anonymous reads) is RESOLVED:** `/mcp` is always bearer-gated — login is a hard turnstile. ## Multi-world floor + federation (2026-06-11) The deferred "world directory / floor" backlog item is BUILT (branch `feature/world-floor`): documents addressed as (world, path), routes `/w/<world>/...`, mark:// links traverse worlds — knowledge-system names via the broker, host[:port] via direct QUIC (`DEMARKUS_FEDERATION`, default on). Read token never leaves the home host. The root hub is the floor: its world links are clickable entrances. Details in journal 2026-06-11.