soul.demarkus.io:6309/plans/memory-knowledge-layering.md

Graph: memory-knowledge-layering

memory-knowledge-layering0043-memory-model-in-…/adr/0043-memory-model-in-client-memory.md — soul.demarkus.io:63090045-write-gates-run-…/adr/0045-write-gates-run-in-the-store.md — soul.demarkus.io:6309architecture/architecture.md — soul.demarkus.io:6309index/index.md — soul.demarkus.io:6309active-plans/index/active-plans.md — soul.demarkus.io:63092026-10-16/journal/2026-10-16.md — soul.demarkus.io:63092026-10-17/journal/2026-10-17.md — soul.demarkus.io:6309collection-dx/plans/memory-knowledge-layering/collection-dx.md — soul.demarkus.io:6309delivery/plans/memory-knowledge-layering/delivery.md — soul.demarkus.io:6309doctor-apply/plans/memory-knowledge-layering/doctor-apply.md — soul.demarkus.io:6309file-worlds/plans/memory-knowledge-layering/file-worlds.md — soul.demarkus.io:6309gates/plans/memory-knowledge-layering/gates.md — soul.demarkus.io:6309local-main/plans/memory-knowledge-layering/local-main.md — soul.demarkus.io:6309model-carve-out/plans/memory-knowledge-layering/model-carve-out.md — soul.demarkus.io:6309one-full-binary/plans/memory-knowledge-layering/one-full-binary.md — soul.demarkus.io:6309one-memory-plugin/plans/memory-knowledge-layering/one-memory-plugin.md — soul.demarkus.io:6309prompt-mechanics/plans/memory-knowledge-layering/prompt-mechanics.md — soul.demarkus.io:6309roadmap/roadmap.md — soul.demarkus.io:63090025-watch-verb/adr/0025-watch-verb.md — soul.demarkus.io:63090026-client-sdk-mit-s…/adr/0026-client-sdk-mit-server-never-imports.md — soul.demarkus.io:63090042-soul-entries-are…/adr/0042-soul-entries-are-agent-written.md — soul.demarkus.io:63090043-memory-model-in-…/adr/0043-memory-model-in-client-memory.md — soul.demarkus.io:63090044-mdoutline-split/adr/0044-mdoutline-split.md — soul.demarkus.io:63090045-write-gates-run-…/adr/0045-write-gates-run-in-the-store.md — soul.demarkus.io:6309architecture/architecture.md — soul.demarkus.io:6309agent-ux/plans/agent-ux.md — soul.demarkus.io:6309demarkus-sdk/plans/demarkus-sdk.md — soul.demarkus.io:6309durable-core/plans/durable-core.md — soul.demarkus.io:6309embracing-staleness/plans/embracing-staleness.md — soul.demarkus.io:6309engine-not-wire/plans/engine-not-wire.md — soul.demarkus.io:6309collection-dx/plans/memory-knowledge-layering/collection-dx.md — soul.demarkus.io:6309delivery/plans/memory-knowledge-layering/delivery.md — soul.demarkus.io:6309doctor-apply/plans/memory-knowledge-layering/doctor-apply.md — soul.demarkus.io:6309file-worlds/plans/memory-knowledge-layering/file-worlds.md — soul.demarkus.io:6309gates/plans/memory-knowledge-layering/gates.md — soul.demarkus.io:6309local-main/plans/memory-knowledge-layering/local-main.md — soul.demarkus.io:6309+3 more backlinks+7 more links
soul.demarkus.io:6309/plans/demarkus-sdk.md active reader meta

demarkus-sdk: client SDK

An active plan for demarkus-sdk: a permissively licensed client SDK that lets any application talk to a demarkus server the way it talks to a database through its driver. Go first. Audience: demarkus maintainers. Drafted 2026-09-25; narrowed the same day to a client SDK only. Phase 0, ADR 0026, drafted 2026-09-27 on branch feat/live-track-front, awaiting acceptance. No SDK code yet.

Why

There is no client library today. demarkus/client/fetch is the de facto Go client, but it is internal, sits in a module with 30+ packages including CLI, TUI, and MCP code, has no stability promise, and is AGPL. The only other client, Caztor's Java DemarkusConnection, is FETCH only and embedded in a browser (ecosystem).

License conflict. demarkus-library is MIT (ADR 0009) but imports AGPL client/fetch, protocol, client/mdoutline, and client/graphstore, so its binary carries AGPL obligations. Any third-party Go application would hit the same wall.

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

What the SDK is

A client SDK and nothing else: connect to a server, speak the protocol, return typed results.

In scope:

  • Open a handle to a mark:// host with a token, pooled QUIC connections, close.
  • The seven verbs plus WATCH as typed calls: Fetch, List, Versions, Lookup, Publish, Append, Archive, Watch.
  • Typed results parsed from the wire: document metadata and body, LIST pages as an iterator, VERSIONS entries, LOOKUP rows, WATCH events with cursor resume.
  • Typed errors: not found, conflict with current version, unauthorized, rate limited, outcome unknown for writes, resync for watches.
  • Connection vs stream error classification and retry of reads; writes never resent after bytes left the client.
  • Append with expected version 0 resolves the current version, the documented protocol pattern.

Out of scope, staying in core on top of the SDK: response cache, recursive LIST walk, markdown outline, link extraction, three-way merge, graph and graphstore, token file discovery (~/.mark/tokens.d), MCP tools and formatting, federation crawl, client/live.

Package mapping

Moves into the SDK Stays in core, imports the SDK
client/fetch, client/listing, client/lookuptable, the write rules from client/docwrite, client-side wire codec from protocol/ Everything else in client/: marktools, mcpbind, mcpfmt, metaguard, lookupexpand, fetchdedup, listwalk, mdoutline, links, merge, graph, graphstore, index, generation, joinurl, internal/*, fetchtest

About 4,400 lines including tests move or are copied. Git history shows one author for every listed file (checked 2026-09-25, repeated 2026-09-27), so relicensing them to MIT is possible; ADR 0026 lists the exact files.

Decisions

  • Repo: latebit-io/demarkus-sdk, Go module github.com/latebit-io/demarkus-sdk/go, package mark. Other languages get directories in the same repo later, only when a named consumer exists.
  • License: MIT, matching ADR 0009 satellites; wire fixtures CC0, like the spec. Settled in ADR 0026: Apache-2.0 was rejected because its patent grant buys nothing for a single-author protocol client and would add a third license to the ecosystem.
  • The server never imports the SDK. The SDK carries its own client-side codec (encode requests, decode responses and WATCH blocks); the server keeps the server-side codec in protocol/ (decode requests, encode responses). The two directions share little code. Agreement is enforced by golden wire fixtures both sides test against, the producer-consumer contract pattern.
  • Core client surfaces use the SDK. CLI, TUI, MCP, broker, and agent import mark; client/fetch is deleted.
  • Ordering with realtime: the Go SDK lands before realtime PR 5, so Watch is written once, in the SDK. Realtime PR 1 (error classification) landed in client/fetch on 2026-09-27 (branch feat/live-track-front) because extraction had not started; it moves with the file in phase 2.
  • Versioning: own semver in its own repo, v0 until WATCH ships, then v1 with semantic versioning, so the API does not freeze before its biggest addition. The monorepo's 1.0 lockstep covers the monorepo modules only; core pins the SDK in go.mod like any dependency, and core 1.0 pins an SDK v1 (ADR 0026).

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}) { ... }

Phases

Each phase 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): client SDK only, MIT and CC0, the server never imports it, the exact file list relicensed. Mirror to /adr/. Drafted 2026-09-27 on feat/live-track-front; file list verified with git log --format=%an per file. ADR accepted; file list matches the authorship check.
1 Repo, fixtures, codec. Create demarkus-sdk; golden request and response transcripts for every verb and status in fixtures/; the client-side codec with fuzz targets. Core protocol/ tests consume the same fixtures. SDK codec and core server codec both pass every fixture.
2 Client. Move fetch, listing, lookuptable, and the write rules into package mark with the API above. SDK unit tests plus an integration test against a real demarkus-server.
3 Core switches over. One surface per PR, mechanical moves with no behavior change: CLI, TUI, MCP, broker, agent. Delete client/fetch. No client/fetch import remains; all core tests pass.
4 demarkus-library switches over for fetch and protocol. Library builds against the SDK; its remaining AGPL imports are only mdoutline and graphstore.
5 Docs and examples. Package docs, runnable examples per verb and a WATCH mailbox once realtime lands. A new app uses the SDK from its README alone.

Other languages follow the QUIC library matrix in Lean into QUIC: Rust (quinn) and Python (aioquic) are straightforward, TypeScript needs WebTransport or the broker. None is scheduled.

Risks

  • Codec drift between SDK and server. Two codecs for two directions; fixtures are the contract, and a wire change without a fixture change is a review finding.
  • Relicensing scope. Only listed files change license; anything with another author stays in core.
  • Core churn. Phase 3 touches every client surface; one surface per PR, no behavior changes mixed in.

Open questions

  • demarkus-library still imports AGPL mdoutline and graphstore after phase 4: move that code into the library, accept AGPL for the library, or publish those two separately under MIT?
  • Does token file discovery belong in the SDK after all, so every app resolves tokens like the CLI does?
trail
  1. soul.demarkus.io:6309 graph: memory-knowledge-layering
  2. demarkus-sdk