# demarkus-library — Roadmap & Resume Pick-up-where-we-left-off doc. Authoritative plan is [/plans/universe-library.md](/plans/universe-library.md); this tracks status, the Phase 1b breakdown, and which tasks to run on **Claude Fable 5** vs Opus 4.8. ## Where we are (2026-06-10, end of session) - **Phase 0 — foundation spike: DONE, merged to `main`.** - **Phase 1a — reading room (features-first): DONE, merged to `main`** (PR #2). Built over the current **direct-QUIC** world adapter. Working today: - `/` default doc · `/d/` document or **stacks** listing (path ending `/`) · `/search?q=` **card catalog** (LOOKUP, debounced htmx, click-through, and a real `
` so it works without JS) · `/versions/` **editions** · `/health`. - **Link rewriting** maps internal doc/dir/version links → in-app `/d/`. - **htmx fragment-vs-page**: targeted swaps return the `content` partial; boosted nav + no-JS return the full `page`. One render path, degrades without JS. - x/net on **v0.56.0** (CVE-patched), `govulncheck` clean. ## How to resume (runs today, direct-QUIC, no auth) ```sh cd ~/latebit/demarkus-library go test ./... && go build -o /tmp/demarkus-library ./cmd/demarkus-library PORT=8080 DEMARKUS_INSECURE=true DEMARKUS_HOST=soul.demarkus.io \ DEMARKUS_DEFAULT_DOC=/index.md \ DEMARKUS_AUTH= /tmp/demarkus-library # open http://localhost:8080 ``` `.mcp.json` (gitignored) holds the soul read token. The repo depends on the local demarkus checkout via `replace` → `../demarkus` (client + protocol modules). ### File map (hexagonal — see ADR 0002) - `internal/core/domain` — entities + sentinel errors (no external deps) - `internal/core/port/port.go` — `ReadingService` (in); `WorldGateway`, `Renderer` (out) - `internal/core/service/reading.go` — Read/Browse/History/Search - `internal/adapter/inbound/web` — Echo handlers/routes, `view.go`, `links.go`, templates - `internal/adapter/outbound/world` — direct-QUIC gateway (Fetch/List/Versions/Lookup) - `internal/adapter/outbound/markdown` — goldmark + bluemonday - `cmd/demarkus-library` — composition root + env `AppConfig` ## Phase 1b — the library card (NEXT) Add org-scoped auth + a broker transport, then gate the reading room behind login. The hexagonal split means this is a new **outbound adapter + session**, swapped at the composition root; **the core and web reading-room logic do not change**. Broker (source: `~/latebit/demarkus/tools/demarkus-broker/internal/broker/`): standard OAuth surface — `/.well-known/oauth-authorization-server`, `/.well-known/oauth-protected-resource`, `/authorize`, `/token`, `/register` (RFC 7591 DCR), `/auth/callback` — plus the `/mcp` gateway (14 tools: mark_fetch/list/versions/lookup/...). This is the same path Claude Code's MCP SDK authenticates through (plan: "MCP session model = same as the broker"). ### Tasks — and who runs them **Run on Claude Fable 5** (the escalation valve — transport + auth-session design, one-shot correctness, refresh/concurrency; per the plan's build-model strategy): 1. **Broker MCP client (Go).** Speak MCP over HTTPS to the broker `/mcp` with a bearer token; call the read tools. Replaces the direct-QUIC fetch client as the transport. Decide the client lib (mcp-go, already a demarkus dep) vs hand-rolled. *Hardest, load-bearing — design it on Fable.* 2. **Org OAuth login (auth-code + PKCE + DCR).** `/register` for a client, build the authorize redirect with PKCE S256, handle `/auth/callback`, exchange code→tokens. Correctness-critical and one-shot — Fable. 3. **Token refresh + session lifecycle.** `grant_type=refresh_token`, expiry/renew, revoke on logout. Refresh races are bug-prone — Fable. **Run on Opus 4.8** (mechanical once the above exist): 4. **Broker `WorldGateway` adapter.** Implement `port.WorldGateway` over the MCP client + the request's session token. New `internal/adapter/outbound/broker/`. 5. **Cookie session store.** Server-side session holding tokens (never to the browser — the XSS boundary); Echo session middleware; login/logout routes. 6. **Login turnstile + UI.** htmx-hard login page; middleware redirecting unauthenticated users; logout. Keep ADR 0003 (degrade without JS — real form). 7. **Composition-root swap.** Env flag selects direct-QUIC vs broker adapter so the Phase-0/1a demo path still works. 8. **Resolve the open question** (below) — investigation, Opus. ### Open question to resolve in 1b Does the broker expose any **anonymous-readable** surface? Decides whether login is a hard gate or reads can be unauthenticated. Check `mcp_auth.go` / `mcp_gateway.go` in the broker source (is `/mcp` always auth-gated?). Not blocking 1a. ## Deferred backlog (not 1b core) - **Backlinks / "cited by"** — no server verb; needs a graph crawl (client `graph` package does `Crawl()`). Plan defers the force-directed viz. - **Multi-world directory / floor** — enumerate worlds from the `root` hub; cross-world `mark:///...` link rewriting (link rewriting is single-world today). - **Phase 2** restricted collections; **Phase 3** cataloging desk (writes, CodeMirror island); **Phase 4** AI librarian (SSE); **Phase 5** public face. ## Git / process notes - `main` has Phase 0 + 1a. Work Phase 1b on a `feature/phase-1b-*` branch. - **Never auto-commit** — Fritz always commits (global rule). Prepare changes, he commits + opens the PR; CodeRabbit reviews via `.coderabbit.yaml`. - PR titles use conventional-commit scopes: domain, port, service, web, world, markdown, cmd, view, deps, ci (+ add `broker`, `auth` for 1b). ## Phase 1b plan finalized (2026-06-11) Concrete two-repo plan written: **[Phase 1b — Web SSO over the Broker](/demarkus-library/plans/phase-1b-web-sso.md)** + rationale in [ADR 0004](/demarkus-library/adr/0004-broker-web-sso.md). Corrections to the breakdown above (from reading broker source): - **Open question RESOLVED:** `/mcp` is always bearer-gated (`mcp_auth.go:33-65`). Login is a hard turnstile; no anonymous reads. - **Auth-code+PKCE does NOT work for the deployed library.** `/oauth/authorize` is loopback-only (`oauth_authorize.go:54-60`). New approach: **add a confidential web-client class to the broker** (registered https redirect + `authorization_code` with client auth), then standard redirect SSO in the library. Device flow rejected as a shortcut. See ADR 0004. - Endpoint corrections: token = **`/device/token`**, authorize = **`/oauth/authorize`**. - **Step 1 (broker confidential web client) is the new prerequisite** — run on Fable in the demarkus repo first. Then library Steps 2.2/2.5 (oauth client, session+refresh) on Fable; 2.3/2.4/2.6/2.7 on Opus. ## Status update (2026-06-11, PR #3 merged) - Broker Step 1 (confidential web clients): **DONE** — demarkus `117e2a6`. - Library 2.2 (oauth client) + 2.5 (session store + single-flight refresh): **DONE, merged** — PR #3 (`7710e39`). - **Remaining 1b:** 2.4 ctx threading on ports → 2.3 broker MCP WorldGateway → 2.6 middleware + login UI → 2.7 composition-root swap (all Opus-tagged). ## Status update (2026-06-11 EOD) — Phase 1b code-complete All library steps merged to `main` (7d24d41): 2.2 oauth client · 2.3 broker MCP WorldGateway · 2.4 ctx threading · 2.5 session + single-flight refresh · 2.6 turnstile (middleware, /login /login/start /auth/callback /logout, login page) · 2.7 composition-root swap (`DEMARKUS_TRANSPORT=quic|broker`). Broker side done earlier (demarkus `117e2a6`). **To close 1b:** deploy latest broker → register library in `webClients` (plaintext secret to library env; lowercase sha256-hex to broker config; exact-match https redirect URI) → real end-to-end login. Then Phase 2 (restricted collections) per the plan.