Demarkus / Knowledge System FAQ
For the RFC review session. Sources: demarkus repo, demarkus-knowledge-system-deploy repo. Status: WIP.
What is the core principle?
Berners-Lee's 1989 "Information Management: A Proposal": documents joined by typed links, navigated by following the links. demarkus keeps that model and adds what the Web lacks for agents.
- Documents are nodes. Markdown links and
rel-<predicate>metadata are typed edges. A document carries its own exits, so no side channel is needed to move on (HATEOAS for knowledge). - Servers store, version, and catalog documents. They never crawl or hold traversal state. Navigation belongs to the agent.
- Backlinks (who links here), which HTTP does not provide, come from a graph store the agent builds and a world can publish as
/graph.md. - Versions, hash chains, and content addressing give every link a durable target.
- Test for a proposal: does it make knowledge more linked, findable, or durable? If it only makes an application more convenient, it does not belong in the protocol.
How do you interact with the knowledge system?
Agents use MCP over HTTPS. Humans use the web app or the CLI.
- Agents:
mark_*MCP tools against the broker, URLs asmark://<world>/<path>. - Auth: the broker is its own authorization server. Claude Code runs OAuth authorization code with PKCE and dynamic client registration; device flow covers CLI-style joins.
- Humans: the library web app, or
demarkusCLI /demarkus-tuidirect to a world over QUIC. The library reads one world with no login by default; registering it with the broker adds sign-in and browser writes. - Entry point: the
roothub. Policy and templates live undermark://root/.well-known/demarkus/.
What are a world, a hub, root, and an index?
- World: one demarkus server, addressed by logical name. Own namespace, store, tokens, and release. An optional
world.mddescribes it. - Hub: an ordinary world that receives published aggregates: content-hash indexes from
mark_indexand the/graph.mdexport.mark_resolveand backlink seeding read from it. The server has no hub mode;hub: trueis a deploy flag the crawler reads. - Root: the conventional name for the hub world. The protocol does not require it, but the secret store and library config hardcode
root, so in practice the hub is called root. - Index:
index.mdis a curated entry-point document, the backstop for what lookup cannot surface. The crawler writes machine hash indexes to/index/<host>.mdwhenperServeris set (as here); with the chart default it writes/index.mdand overwrites the curated one.
How does content get into the knowledge system?
Through the promote bridge: /promote <soul-path> runs the knowledge-promote cascade.
- Triage, then distill for a shared audience (strip personal framing, secrets, PII).
- Dedup against the catalog, tag to the system taxonomy, route to a writable world.
- Human gate, then
mark_publishwith provenance. The cascade never writes the soul;/promoteback-stamps the source afterwards. - Direct
mark_publishto a joined system also works and passes the same gate and policy.
How does finding knowledge work?
Primary: mark_lookup catalog queries. Secondary: hub pages and graph traversal.
- Lookup matches a query against declared tags and titles and returns an importance-ranked table (path, importance, title, tags).
- Filters:
tag=,modified-after=,modified-before=, or anykey=valuematching a declared metadata value exactly. Not full-text search: an untagged document is invisible to it. - From a hit:
mark_exploreto orient, thenmark_fetch url#anchorfor the sections needed. - Backstops:
index.mdhubs,mark_backlinks/mark_graphfor link traversal,mark_discoverfor a server's manifest.
How is relevance decided?
Deterministic scoring, no embeddings (server/internal/catalog/catalog.go).
- Score = number of distinct query terms matching tags (exact, case-insensitive) or title (substring, case-insensitive).
- Sort: score desc, then declared
importance, then modification time, then path. - Filters apply before ranking.
How is importance stamped on a document?
The publisher declares it.
metadata.importanceonmark_publish, float in [0,1], stored out of band, indexed into the catalog.- Absent, unparseable, or out-of-range values default to 0.5.
- APPEND merges: the new version carries the base version's metadata with the request's layered over it (SPEC §6.6), so importance and tags survive an append.
retentionis the exception and is never inherited.
How does the agent decide importance?
The agent chooses it, guided by instructions injected at session start. Nothing computes it.
- SessionStart context and the
soul-memory/knowledge-promoteskills say: 0.8+ for hubs, architecture, key decisions; routine notes lower. - The server never infers it; the gate only validates the range.
What does the agent use to decide whether to read a document?
Catalog and graph evidence before bodies.
- The
mark_lookuprow: path, importance, title, tags, modified time. - Backlink provenance: label, anchor, count,
rel-type. mark_explore: outline, links, backlinks, siblings in one call.mark_discovermanifests and hubindex.mdpages for context.
How does the agent read a large document?
The outline is a nudge, not a gate. The server always sends the full body; the MCP layer decides what to show.
- At 8KB
mark_fetchreturns the heading tree with#anchorsand per-section line counts instead of the body. mark_fetch url#anchorreturns one section at any size.mark_fetch url force=truereturns the whole body regardless of size or session history.mark_explore urllists the anchors for any document; a bad anchor error names every available anchor.- The
demarkusCLI fetches raw bytes with no outline gate.
How does an agent navigate from a document it has fetched?
Forward from the document itself, backward from the graph store.
- A
mark_fetchresponse holds every exit: body links andrel-<predicate>metadata. Following them needs nothing beyond FETCH. - Incoming edges (who links here, what supersedes this) come only from the local graph store via
mark_backlinksormark_explore. The store fills from the world's/graph.mdon first use, then frommark_graphcrawls. A plain fetch does not add to it. - Empty store and no
/graph.md: backlinks return nothing until someone runsmark_graph. Forward navigation,mark_list, andmark_lookupstill work because they hit the live server. - Nodes carry no relation data. Every
rel-fact is an edge, read frommark_backlinksrows or theEdges:list ofmark_graph. - Store keys are canonical
mark://host:port/pathstrings matched exactly. A link written with a different host alias becomes a separate node.
How does the document graph work?
mark_graph crawls outbound mark:// links (depth default 2, max 5, capped at 200 nodes, so deep crawls usually stop on the node cap).
- Edges carry provenance: link label, source section anchor, occurrence count.
- Typed relations come from
rel-<predicate>publisher metadata, e.g.rel-supersedes. - Crawls persist to a graph store that answers
mark_backlinks. The store seeds from a published/graph.md; local crawls take precedence. mark_graph_publishrepublishes the store as a crawlable/graph.md(generated doc, default retention 20).
Is edge information there to guide agents?
Yes (ADR 0004).
- Label: what the linker calls the target.
- Anchor: the exact source section, a direct
mark_fetch url#anchorjump. - Count: link strength.
rel-types: "what superseded this" rather than bare "mentions".- Constraint: the agent owns judgment, the server owns accumulation.
How is the graph built world to world?
A scheduled agent builds it; the broker reads what it publishes.
- The agent has a seed URL per content world and crawls each one. The hub is not crawled; it is where results are published.
- It merges every world's edges into one graph and publishes
/graph.mdon the hub, alongside the content-hash index. Graph publishing is off by default and on in this deployment. - Each run rebuilds from scratch. A world that fails to answer drops out of the published graph until the next successful crawl.
- Cross-world links are ordinary
mark://{worldName}/{path}links. - The broker's own graph store is in-memory and pod-scoped. It loads the hub's
/graph.mdon demand, so a fresh pod answers backlinks without crawling. - A world with no published graph falls back to on-demand
mark_graphcrawls.
How does the system keep data fresh?
- Reads go to the world's server every time. The client revalidates with
if-none-match/if-modified-since; a cached body is reused only when the server confirms it. - Each write creates a new hash-chained version. Rewriting identical content is a no-op that returns the existing version.
- Optimistic concurrency (
expected_versionplus merge on conflict) stops stale writes clobbering newer ones. - A scheduled agent re-crawls every world and republishes the content-hash index and graph on the hub.
- Soul copies of promoted documents can go stale.
/soul-refreshcopies the knowledge system's version down;/promoteis the only way an edit goes back up. There is no two-way sync.
How do diffs and merges work?
One document, two concurrent editors. (Merging separate documents is dedup, below.)
- A conflicting
mark_publishreturns a diff3 merge candidate: base, ours, theirs, git-style markers where both sides touched the same lines. - The machine merges structure; the agent reviews the result and republishes at the returned version.
- Effect: a stale body cannot silently overwrite a newer one.
How are documents updated, and how do they keep their meaning?
- No partial update: fetch, edit, republish the whole body at the version you read. Conflicts return a merge candidate instead of overwriting.
- Old meaning is never lost; every version stays fetchable at its pinned number.
- One subject, one owner: updates go to the existing document, and
rel-supersedesrecords meaning that moved elsewhere. - Limit: nothing verifies an update preserved meaning. That is agent judgment plus the human gate. Renaming a heading silently breaks inbound anchors.
How does dedup work?
Before the write, not as a cleanup pass. The agent looks for an existing owner of the subject and updates it.
- The promote cascade looks the subject up in the catalog and fetches close matches.
- If a document already covers it, the agent updates that document instead of adding a second.
- If the new content contradicts the existing one, the human decides at the gate.
/knowledge-doctorcatches exact duplicates after the fact by comparing content hashes across paths and worlds. It fetches each document, so it runs on a bounded scope.- Limit: candidates are found by tag and title match. A near-duplicate sharing no tags or title terms is never a candidate.
How are versions and integrity handled?
- Each write creates a new version linked by a SHA-256 hash chain over the previous version's raw bytes.
mark_versionsvalidates the chain oldest-first and reportschain-validorchain-error.- Nothing is deleted by default.
mark_archivekeeps full history but withdraws the document: no body at its path, no catalog entry, no further writes until unarchived by publishing an empty body. - The one destructive path is
metadata.retention, which permanently prunes old versions. It exists for generated documents; clients warn before applying it elsewhere.
What is the auth and security model?
Capability tokens on the server; SSO at the broker.
- Writes are denied unless a token store is configured.
- Reads are public unless a token grants
readon a path pattern, which makes matching paths require one (protect/**for a private intranet). - Knowledge system: OIDC SSO is the org gate. Reads are open to any authenticated identity; writes pass a per-world writer allowlist. Credentials live at the broker: it holds one long-lived token per world and dispatches writes with it. Per-world tokens exist only inside a broker deploy and never reach a client. A world reached directly over QUIC uses ordinary capability tokens.
- Policy forbids publishing secrets, credentials, or PII.
How is knowledge formatted and held to a standard?
The standard is published on root under .well-known/demarkus/.
policy.md: strictness, required tag axes such ascategory:, optional required OKF fields.template.md: per-world layout.style.md: H1 as name, one-sentence summary under it, unique headings (headings are anchors), no em dashes, no frontmatter fences.- Joining copies the policy into local write-time gates as a snapshot: tags, axes, importance range, mechanically checkable style rules, at the declared severity (warn / block / ask). The gate runs offline, so a policy change takes effect on the next join or mirror.
/knowledge-doctoraudits the corpus after the fact.
What automated cues push agents to record memory?
Five hooks in the memory plugin.
- SessionStart: injects the routing table (decisions to
/adr/, gotchas todebugging.md, progress tojournal/). - Stop: journal nudge when files changed but nothing was written to the soul.
- PostToolUse on
mark_publish: promote nudge when an ADR lands in the soul and a promote destination exists. - UserPromptSubmit: recall-first reminder on "did we decide" questions.
- Pre/PostToolUse write gate: tags and importance, write destination, retention, style. Defaults differ per check: tags warn, destination blocks, retention asks.
What tools does the memory plugin expose?
Fifteen mark_* MCP tools.
| Tool | Description |
|---|---|
mark_fetch |
Fetch a document or #section; bodies of 8KB or more return an outline |
mark_list |
List documents and subdirectories; archived hidden by default |
mark_explore |
One doc's outline, outbound links, backlinks, and siblings in one call |
mark_lookup |
Catalog lookup: importance-ranked matches on tags and titles |
mark_publish |
Create or update a document; metadata, optimistic concurrency, diff3 on conflict |
mark_append |
Append to an existing document; catalog metadata carried forward |
mark_archive |
Archive a document; body and catalog entry withdrawn, history preserved |
mark_versions |
Version history with hash-chain validation |
mark_graph |
Crawl outbound mark:// links; persists edges to the graph store |
mark_backlinks |
What links here, with edge provenance |
mark_graph_export |
Export the graph store as publishable markdown |
mark_graph_publish |
Export and publish the graph as /graph.md (retention default 20) |
mark_discover |
Fetch a server's agent manifest |
mark_resolve |
Resolve content by SHA-256 hash via a hub index |
mark_index |
Crawl a server, publish its content-hash index to a hub |
What tools does the knowledge plugin expose?
The same 15 MCP tools, kept at parity so the vocabulary does not change with transport. URLs address worlds by logical name instead of host:port. Additions:
| Addition | Kind | Description |
|---|---|---|
mark_worlds |
MCP tool | List the system's worlds: name, URL, address, and whether you can write to each. Broker-only |
/knowledge-join |
command | Validate an org broker URL, register it as an MCP server, copy its policy into the local gates as a snapshot. Claude Code runs the OAuth flow on the first tool call; the command handles no tokens |
/knowledge |
command | List joined systems and show each root hub index |
/knowledge-doctor |
command | Read-only hygiene audit: orphans, broken links, untagged and policy-noncompliant docs, ADR gaps, duplicates |
knowledge-promote |
skill | The curation cascade that lands a staged document in the catalog; invoked by the memory plugin's /promote |
Why does federation matter, and why a server per world or memory?
The protocol has no central authority or registry. This deployment does not inherit that property.
- Anyone can run a server; content mirrors freely; every caching client is a mirror.
- Each version carries a
content-hash, so agents on different mirrors can confirm they hold identical content. The hash chain is a separate guarantee: it proves one server has not rewritten its own history. VERSIONS returns no per-version hashes, so the chain is not a cross-mirror check. - A server per world makes the ownership boundary physical: own store, token file, writer allowlist, release. Blast radius stays contained.
- A soul is the same
demarkus-serverbinary at personal scale. mark_resolvefetches by hash via a hub index across the worlds the broker knows. Cross-org resolution is out of scope today.- This instance is centrally administered by design: one broker, an OIDC org gate, a domain allowlist, per-world writer allowlists. The protocol is decentralized; the deployment is not.
Why deploy a knowledge system on k8s?
A knowledge system is many independent servers behind one broker.
- Each world is its own server with its own namespace, store, tokens, and release, and its own failure domain. Other worlds keep serving and answering lookups.
- A world being down degrades shared state: the next crawl omits its nodes and edges from the hub graph until a later successful crawl. If the hub is down, cross-world discovery goes with it.
- Adding a world is one entry in
deployment.yaml. Upgrading is not per-world: all worlds share one chart revision and image tag, so one merge rolls every world. StatefulSet changes to immutable fields need manual intervention per world. - The world list and deployment identity are declared once and reconciled by ArgoCD. Versions, secrets, bootstrap, and the hub name are not.
- Smaller needs: the reference deployment's README says one or two plain deploys would do for a team.
Why QUIC?
- Encryption is mandatory: TLS 1.3 built in, no plaintext fallback.
- Fast multiplexed streams, one per request.
- No HTTP layer: no cookies, tracking headers, or query strings. Seven text verbs.
- The broker fronts worlds over HTTPS, so clients need no direct QUIC access.
Why no full-text or semantic search?
Deliberate. LOOKUP is defined in SPEC §6.7; the argument is in DESIGN.md under "Why not search?".
- LOOKUP is a per-world catalog over author-declared tags and titles. Servers must not read bodies at query time, and there is no centralized index.
- Full-text and semantic search stay out of core as an opt-in sidecar reading demarkus over LIST/FETCH.
- Keeps the server simple and deterministic; ranking judgment stays in the agent.
What are the known limitations?
- The graph is not yet optimal: the broker's store is in-memory and pod-scoped, crawls are capped and rebuilt from scratch each run, and edge semantics are recent (ADR 0004).
- Full-text or semantic search requires an added component; until then recall depends on tagging discipline.
- Availability is not yet distributed: single-replica broker, single-replica worlds, and a hub whose loss takes cross-world discovery with it.
- Inside a broker deploy the broker writes to a world with one per-world token, so per-user attribution rests on the provenance recorded in documents, not on the credential.
- Behavior at large scale is unproven: catalog size, crawl cost, and cross-world graph growth have not been tested at enterprise corpus sizes.