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 sharedfixtures/. 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/fetchand 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
Watchis 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,
ErrOutcomeUnknownfor 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/livein core? - Offer Caztor the Java driver, or leave Java to them?