soul.demarkus.io:6309/demarkus-library/roadmap.md/v5 draft reader meta

demarkus-library — Roadmap & Resume

Pick-up-where-we-left-off doc. Authoritative plan is /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/<path> document or stacks listing (path ending /) · /search?q= card catalog (LOOKUP, debounced htmx, click-through, and a real <form> so it works without JS) · /versions/<path> editions · /health.
    • Link rewriting maps internal doc/dir/version links → in-app /d/<path>.
    • 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)

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=<token from .mcp.json>  /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):

  1. Broker WorldGateway adapter. Implement port.WorldGateway over the MCP client + the request's session token. New internal/adapter/outbound/broker/.
  2. Cookie session store. Server-side session holding tokens (never to the browser — the XSS boundary); Echo session middleware; login/logout routes.
  3. Login turnstile + UI. htmx-hard login page; middleware redirecting unauthenticated users; logout. Keep ADR 0003 (degrade without JS — real form).
  4. Composition-root swap. Env flag selects direct-QUIC vs broker adapter so the Phase-0/1a demo path still works.
  5. 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://<world>/... 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

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.

Status update — floor + federation on main (2026-06-11 late)

Beyond Phase 1b, also merged: multi-world floor + distributed graph traversal (fc4277c) — (world, path) addressing, /w/<world>/... routes, federated composite gateway (broker names + direct QUIC hosts, DEMARKUS_FEDERATION default true), token-scoped-to-home rail, cross-world mark:// link rewriting, session-pooled MCP transport (cd88e7d).

Next candidates: deploy manifests for the library (Dockerfile + helm, cluster ingress TLS); drop broker rateLimit back (~30/min); Phase 2 restricted collections; backlinks/"cited by" (graph crawl) still deferred.

trail
  1. soul.demarkus.io:6309 v5