Roadmap
Where demarkus has been and where it's going.
Phase 1: MVP (Read-Only) — COMPLETE
Everything shipped:
- QUIC server serving markdown files
- FETCH, LIST, VERSIONS verbs
- TUI client with Bubble Tea + Glamour
- Link following, navigation history
- Document graph visualization
- CLI client with all verbs
- MCP integration for LLM agent access
- Docker multi-arch images
- GoReleaser CI/CD with per-module versioning
- Conditional fetch (if-none-match, if-modified-since)
- SIGHUP certificate reload
Phase 2: Publish Operations — COMPLETE
Done:
- PUBLISH verb with version creation
- ARCHIVE verb
- APPEND verb — sends only new content, server handles concatenation
- Capability-based auth (token generation, SHA-256 hashes, path/op scoping)
- Versioned store with symlinks and hash chain
- Document editing via $EDITOR
- Client-side token management
- Conflict resolution (optimistic concurrency with expected-version)
- No-op on duplicate content
- Structured logging with slog (replaced console logging)
- Protocol-level size limits (1 MiB body, 64KB frontmatter)
- Audit logging with token_label on all write operations
- Usability audit: documentation accuracy, CLI help, install script cleanup, CI lint
Phase 2 is fully complete. No remaining items.
Phase 3: Agent-Native & Advanced Features — COMPLETE
Done:
- Agent manifest discovery (
/.well-known/agent-manifest.md)- Convention spec: markdown document at well-known path, no server changes needed
WellKnownManifestPathconstant in protocol modulemark_discoverMCP tool — fetches manifest from connected serverdemarkus infoCLI subcommand — fetches and displays manifest- Spec doc at
docs/site/reference/agent-manifest.md - First live manifest published on demarkus-soul
- Directory listing for servers (auto-generated when no index.md)
- Bookmarks/favorites
- Markdown-backed store at
~/.mark/bookmarks.md - TUI:
btoggle,Bview,Escexit, star indicator in status bar - CLI:
bookmark add/remove/listsubcommands - MCP: not exposed (local-only feature)
- Markdown-backed store at
- MCP
mark_appendauto-resolveexpected_versionexpected_versionis now optional inmark_append— when omitted or 0, the tool calls VERSIONS to get the current version automatically- Optimistic concurrency still enforced — the server always receives an
expected_version - Introduced
markClientinterface for handler testability - Tests cover auto-resolve (happy path), not-found, explicit version, negative version, no token
- Does NOT apply to
mark_publish(publish requires the agent to have read the document first)
- Content-addressed fetch
- FETCH responses include
content-hash: sha256-<64hex>— SHA-256 of the stripped body FETCH /sha256-<64hex>retrieves documents by content hash from any server that has them- In-memory hash index (
map[string]string) built on startup, updated on writes/archives - Current versions only, archived docs excluded
- No new verbs — FETCH handles it via path pattern detection
- Foundation for distributed mirroring and caching
- FETCH responses include
- Federation via MCP tools
mark_index— crawls a source server, collects content hashes, publishes index to a hubmark_resolve— resolves content by SHA-256 hash using a hub index documentclient/internal/indexpackage — Parse, Build, Merge for markdown hash index documents- Manifest check enforced by tool,
forceoverride,dry_runmode, 1000 doc cap
Phase 4: The Information Graph — COMPLETE
Discovery through linking, not searching. Information is findable because it's connected — servers link to each other through documents, clients and agents traverse the graph.
Done
- The Demarkus Hub pattern — live at
mark://hub.demarkus.io. A server whose sole purpose is linking to content on other demarkus servers. No original content, just curatedmark://links organized by topic. CI auto-publishes from Git repo. See github.com/latebit-io/demarkus-hub. - Persistent graph store —
client/internal/graphstorepackage. Graph stored as JSON at~/.mark/graph.jsonwith nodes (documents), edges (links), etags, and crawl timestamps. Atomic writes, schema versioning, incremental merge. - Shared
CrawlAndPersist— unified crawl + merge + save method on*Store, nil-safe. All three clients (CLI, TUI, MCP) share the same code path.EtagFetcheradapter collects etags during crawl. - Backlinks —
mark_backlinksMCP tool andStore.BacklinksEnriched()method. Reverse edge lookup from the persistent graph — "what links here?" Returns sorted list with document titles and status. - Graph seeding from store — TUI graph view loads instantly from stored graph while crawl runs in background.
- Graph as content — export the persistent graph as a publishable markdown document.
Store.Export()renders nodes as markdown table rows withmark://links, edges as a separate table.ParseExport()parses it back. CLI:demarkus graph export [-o file.md]. MCP:mark_graph_exporttool. Crawling the exported doc naturally discovers the topology — no special import needed. Cell escaping handles pipes and backslashes in titles. - Graph-aware navigation — TUI graph view with three sub-views: Links (BFS tree from current doc with
[N←]density indicators), Backlinks (reverse links to current doc), Topology (all explored nodes sorted by importance). Sub-views toggled withd/r/tkeys. Sharedgraphstore.BacklinksEnriched()eliminates duplication between TUI and MCP.graph.InDegrees()for O(E) backlink counting. Rune-safe truncation for multi-byte UTF-8. - Agent discovery — agents crawl the graph to build knowledge maps using
mark_graph(crawl + persist),mark_graph_export(publish as markdown), andmark_resolve(hash-based lookup via hub index). Graph documents are themselves crawlable, so sharing and merging happens naturally through the protocol. No special agent-specific code needed — the existing primitives compose.
No new verbs needed. FETCH reads content, LIST enumerates, links connect. The protocol already has the primitives.
See DESIGN.md Phase 4 section for full sketch.
Verb Set — Complete
The protocol verb set is finalized at 6 verbs: FETCH, LIST, VERSIONS, PUBLISH, APPEND, ARCHIVE.
Removed from spec:
- SEARCH — Full-text search is better handled as an external tool built on top of the existing primitives (LIST + FETCH) rather than as a protocol-level concern.
- SUBSCRIBE — WebSub-style subscriptions for change notifications were considered but deferred. Polling with conditional FETCH (if-none-match) is simpler and works for most use cases. Real-time push adds complexity and requires maintaining subscriber state. Better addressed by higher-level tools if needed.
Distribution & Package Management — PLANNED
Homebrew tap for easier installation, especially on macOS and Linux developers.
What it takes:
- Create public
latebit/homebrew-demarkustap repo - Add formula for
demarkus-client(CLI + TUI) — pull pre-built binaries from GitHub releases - Formula needs SHA256 checksums for each architecture
- Update README with Homebrew install instructions
- Test installation on macOS and Linux
Notes:
- Server is better deployed via Docker, keep Homebrew focused on the client
- Start with tap (owned by us), not Homebrew Core (requires upstream submission)
- User installs:
brew tap latebit/homebrew-demarkus && brew install demarkus-client - Already have GoReleaser CI/CD and multi-arch builds — just need packaging
Build Targets
Supported platforms: macOS, Linux, Windows (WSL only — runs Linux binaries). Native Windows builds removed from GoReleaser configs.
Plugins — IN PROGRESS
Obsidian Plugin — v0.1.0 RELEASED
Standalone repo: latebit-io/obsidian-demarkus on GitHub. Source lives in plugins/obsidian/ in the monorepo, copied to standalone repo for releases.
Done:
- Fetch, publish, and list documents from Obsidian
- Shells out to
demarkusCLI binary - Token passed via
DEMARKUS_AUTHenv var (never CLI args) - Preserves existing Obsidian frontmatter (tags, aliases, etc.) on fetch and publish
- Path traversal prevention, YAML injection prevention, filesystem-safe sanitization
- Multi-line stderr parsing, concurrent folder creation handling
- Installable via BRAT:
latebit-io/obsidian-demarkus - GitHub release 0.1.0 with manifest.json + main.js assets
Next:
- Test in real Obsidian usage
- Submit to Obsidian Community Plugins directory
- Auto-sync from monorepo to standalone repo (CI)
Features Not Prioritized — Backlog
These are features that were discussed, proposed, or noted as missing but deliberately not prioritized. They don't block core functionality.
WebSub-style Subscriptions (SUBSCRIBE verb)
Status: Removed from protocol spec
WebSub-style push subscriptions for change notifications. Servers would notify registered subscribers when documents change. Deferred because:
- Adds complexity: requires maintaining subscriber state and callback reliability
- Polling works well enough: clients use conditional FETCH (if-none-match/if-modified-since) to check for changes efficiently
- Adds a new verb and protocol concern (push delivery, retry logic, subscriber management)
- Most use cases are adequately served by periodic polling or push at a higher level (e.g., webhook integrations built on top of demarkus)
- Simpler protocols are more resilient — no dependency chains for delivery
If real-time updates become critical, webhooks can be built as an application layer on top of PUBLISH without modifying the protocol.
Offline Mode (Client)
Status: Deferred to backlog
Cache and read-only access when network is unavailable. Low priority because:
- Most use cases have intermittent network, not prolonged offline periods
- Graph store already caches documents locally
- Not a blocking use case for current users
Full-Text Search
Status: Removed from protocol spec, available as external tool
Full-text search across all documents was considered as a protocol verb but removed because:
- Search is a policy concern (what to index, how to rank, which fields matter)
- The Mark Protocol handles data, not policy
- Better handled by building external search tools on top of LIST + FETCH
- Clients can build their own indexes (e.g., SQLite FTS, Bleve)
Diff / Changelog Between Versions
Status: Noted as useful, not implemented
Display what changed between two versions of a document. Would be useful for:
- Journals and thoughts that evolve over time
- Understanding revision history at a glance
- Change summaries in versioned stores
Not prioritized because:
- Clients can fetch v1 and v2 and do their own diff
- Could be a TUI/CLI feature rather than protocol-level
- Low-priority UX improvement
Blind Append with Content Deduplication
Status: Proposed, not pursued
Allow APPEND without expected_version and have the server silently de-dupe based on content hash. Rejected because:
- Hides the non-idempotent nature of append operations
- Retries become unsafe — can't distinguish "legitimate second append" from "accidental duplicate"
- Making version-checking mandatory forces callers to understand current state first
- One extra fetch-for-version is the correct cost of safe operations