soul.demarkus.io:6309/plans/demarkus-sdk.md/v1 active reader meta

demarkus-sdk: client drivers

An active plan for demarkus-sdk: permissively licensed client drivers that let any application use a demarkus server the way it uses a database through its SQL driver. Audience: demarkus maintainers. Drafted 2026-09-25. No code yet.

Why

There is no driver today. What exists (ecosystem):

Component Language Problem as a library
demarkus/client/fetch Go Internal client, not an SDK: the module carries 30+ packages including CLI, TUI, and MCP code, has no API stability promise, and is AGPL
Caztor DemarkusConnection Java FETCH only, no auth, embedded in the browser
Obsidian plugin TypeScript Shells out to the CLI
Broker MCP over HTTPS Any An agent surface, not a data driver

License conflict. demarkus-library is MIT (ADR 0009) but imports four AGPL core packages: client/fetch, protocol, client/mdoutline, client/graphstore. Its binary carries AGPL obligations, so the MIT label does not hold for what it ships. Every third-party Go application would hit the same wall.

Realtime makes it urgent. WATCH and live views (realtime plan) are only useful if applications can subscribe from their own code.

Decisions

  • Repo: latebit-io/demarkus-sdk, one repo with a directory per language (go/, rust/, python/, ts/) and shared fixtures/. One repo keeps wire fixtures and behavior changes in one PR across drivers.
  • License: MIT for drivers, matching ADR 0009 satellites; fixtures CC0 like the spec. Relicensing the extracted code is possible because git history shows one author for client/fetch and the protocol wire files (checked 2026-09-25); the ADR records exactly which files move.
  • Dependency direction: core imports the SDK, never the reverse. The Go SDK owns the wire codec (request, response, frontmatter, auth header, status values, WATCH blocks, cursors) and the client. Core protocol/ keeps stored formats (store, storefmt) and imports wire types from the SDK. Nothing is duplicated.
  • Order relative to realtime: the Go SDK extraction lands before realtime PR 5, so Watch is written once, in the SDK. Realtime PR 1 (error classification) lands directly in the SDK if extraction is done by then.

Scope

In the driver:

  • The seven verbs plus WATCH, typed requests and results.
  • Token auth, connection pooling, connection vs stream error classification, retry of reads, ErrOutcomeUnknown for writes.
  • Conditional fetch (etag), LIST pagination as an iterator, LOOKUP results parsed into rows, WATCH as an event iterator with cursor resume.
  • Typed errors: not found, conflict with current version, unauthorized, outcome unknown, resync.
  • Markdown outline helper (today client/mdoutline), since readers need anchors to fetch sections.

Out of the driver, staying in core or in higher-level packages: response cache, graph and graphstore, merge, MCP, federation crawl, client/live. The library's graphstore use is resolved separately (see open questions).

Go API shape

Modeled on database/sql: open a handle, pass contexts, get typed results.

w, err := mark.Open("mark://soul.demarkus.io", mark.Options{Token: tok})
defer w.Close()

doc, err := w.Fetch(ctx, "/index.md", mark.FetchOptions{})
res, err := w.Publish(ctx, "/notes/a.md", body, mark.PublishOptions{ExpectedVersion: doc.Version})
for ev, err := range w.Watch(ctx, "/agents/me/inbox/", mark.WatchOptions{Since: cursor}) { ... }

Module path github.com/latebit-io/demarkus-sdk/go, package mark. Semantic versioning; v1 only after WATCH ships, so the API does not freeze before its biggest addition.

Language matrix

Driver availability follows QUIC library maturity:

Language QUIC stack Plan
Go quic-go Phase 2
Rust quinn Phase 4
Python aioquic Phase 4
TypeScript, browser WebTransport through the broker (Lean into QUIC workstream 7) Phase 5
TypeScript, Node Core QUIC is experimental Phase 5 through the broker until Node QUIC is stable
Java kwik On demand; offer Caztor the driver
.NET System.Net.Quic (needs msquic) On demand

JavaScript is the weakest QUIC ecosystem and the largest agent-developer audience; that cost is recorded in the transport review.

Phases

One PR or small PR series per phase. Each is done when its proof exists and bash pre-commit.sh passes in every repo it touches.

Phase Scope Proof
0 ADR in core (docs/adr/0026): the SDK split, MIT and CC0 licenses, dependency direction, the exact file list relicensed. Mirror to /adr/. ADR accepted; file list matches git authorship check.
1 Wire fixtures and Go codec. fixtures/ holds golden request and response transcripts for every verb and status (CC0). The Go SDK codec is extracted from protocol/; core protocol/ imports it. Core tests pass unchanged; core and SDK both consume the same fixtures (the producer-consumer contract pattern); fuzz targets move with the codec.
2 Go client. Extract client/fetch into package mark; move every caller in core (CLI, TUI, MCP, broker, agent) to it; delete client/fetch. Add mdoutline. No client/fetch import remains; demarkus-library builds against the SDK for fetch, protocol, and outline; its remaining AGPL import is only graphstore.
3 Go docs and examples. Package docs, runnable examples for each verb and for a mailbox with WATCH. v0 releases until WATCH ships, then v1. go test ./... runs the examples; a new app uses the SDK from its README alone.
4 Rust and Python drivers over quinn and aioquic, fixture-tested, plus an integration test against a server container. Both pass fixtures and the integration test in CI, including WATCH resume.
5 TypeScript driver over WebTransport for browsers and the broker for Node. A browser example subscribes to WATCH; a Node example reads and writes through the broker.
6 Conformance. Durable-core fortification 5 ships the black-box server suite; drivers run it as their integration suite. Every driver passes the same suite against the reference server.

Risks

  • Relicensing scope. Only files moved into the SDK change license; the ADR lists them. Any file with another author stays in core.
  • Driver drift. Behavior lives in fixtures and the conformance suite, not in prose; a driver change without a fixture change is a review finding.
  • Maintenance per language. Go is first class; other drivers ship only when a named consumer exists or community maintainers take them.
  • Core churn. Phase 2 touches every client surface. Land it as mechanical moves with no behavior change, one surface per PR.

Open questions

  • MIT or Apache-2.0? Apache adds a patent grant that database-style adopters often ask for; MIT matches ADR 0009.
  • Does demarkus-library keep graphstore (and its AGPL obligation), move graph building into the library, or does a graph package join the SDK?
  • Should the SDK ship a WATCH-backed cache, or leave caching to client/live in core?
  • Offer Caztor the Java driver, or leave Java to them?
trail
  1. soul.demarkus.io:6309 v1