# 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](/ecosystem.md)): | 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](/adr/0009-license-split-core-agpl-plugins-mit.md)) 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](/plans/watch-agent-messaging.md)) 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. ```go 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](/plans/quic-first.md) 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](/plans/durable-core/fortifications.md) 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?