Survey testing whether Obsidian remains the right substrate for a vault that agents read and write, against Trilium, Logseq, Anytype, SiYuan, Foam, org-roam, Backstage and Graphiti. Covers Obsidian’s current programmatic surface and the typed-edge conventions that make a vault machine-extractable. One of four source surveys behind knowledge-graph-research-2026-09-07.

For Agents

Survey date: 2026-09-07. Repo stats were pulled live from the GitHub API on that date and are point-in-time facts. Verdict: stay on Obsidian. Nothing else offers typed edges, plain text and an agent API together. The gap is a machine-readable layer, not the app. This is additive, not a migration. Recommended addition: Basic Memory pointed at the same vault folder, giving typed edges in the markdown itself, build_context traversal, MCP, and a bm tool CLI for hooks. It is pre-1.0 and its syntax broke once at v0.21.0. Do not adopt Dataview. It has been stale since 2025-11-17 and Bases cannot read its inline fields. Use frontmatter properties, not inline fields. Breadcrumbs changed hands and now lives at michaelpporter/breadcrumbs. Fallback if Basic Memory misbehaves: the convention alone, parsed by a roughly 50-line script into SQLite. That still beats every alternative surveyed. The ## Relations heading appearing inside this note’s fenced code blocks is illustrative syntax, not a section of this note. Kept verbatim from the source report apart from this header and the ## Related footer.

Research date: 2026-09-07. All repo stats pulled live from the GitHub API on that date. Requirements under test: typed traversable links · programmatic agent read/write · plain-text local-first · two-Mac sync · supersession handling.


Verdict up front

Stay on Obsidian. Add basic-memory as the typed-graph and agent layer, pointed at the same vault folder. Sync with git, not Obsidian Sync.

Nothing on the market beats Obsidian at the whole conjunction, and the one tool that adds what Obsidian genuinely lacks — machine-typed edges with a real agent API over plain markdown — is designed to run on top of an existing Obsidian vault, not instead of it. This is an additive change, not a migration.

Two findings drive this:

  1. Obsidian’s programmatic surface got dramatically better in 2026 and most write-ups have not caught up. The official CLI (v1.12, Feb 2026) ships ~100 commands including links, backlinks, unresolved, orphans, deadends, and — the sleeper feature — base:query with format=json, which makes Bases queryable from a shell script. That was not true a year ago.
  2. The plain-text competition collapsed. Logseq’s markdown edition was demoted to “Logseq OG” in maintenance mode (security patches only) while the flagship moved to SQLite. Dendron has been dead since Feb 2023. Foam has no agent API. Every tool with genuinely good typed edges — Trilium, Anytype, Tana, Roam — abandons plain text.

PART A — Obsidian’s current programmatic and graph surface

Current version: 1.13.7 desktop (2026-08-12), 1.13.8 mobile (2026-08-21). Early access 1.14.0 (2026-09-02) adds Kanban views. Changelog: https://obsidian.md/changelog/

A1. The official CLI

Docs: https://help.obsidian.md/cli · source: https://github.com/obsidianmd/obsidian-help/blob/master/en/Extending%20Obsidian/Obsidian%20CLI.md

Shipped in 1.12 (February 2026), free for all users, requires installer 1.12.7+. Enable at Settings → General → Command line interface. macOS symlinks /usr/local/bin/obsidian.

The critical constraint: it is not headless. The docs say plainly, “Obsidian CLI requires the Obsidian app to be running.” If the app is closed, your first command launches it. For an agent running on a machine where Obsidian isn’t open, every CLI call has a cold-start cost. Obsidian Headless is a separate product.

Command groups (parameters as key=value, flags bare, --copy the sole exception):

GroupCommands
Filesfile files folder folders open create read append prepend move rename delete
Linksbacklinks links unresolved orphans deadends
Basesbases base:views base:create base:query
Propertiesproperties property:set property:read property:remove aliases
Searchsearch search:context search:open
Tags / Taskstags tag · tasks task
Historydiff history history:list/read/restore/open
Syncsync sync:status sync:history sync:read sync:restore sync:deleted
Daily / Templates / Uniquedaily daily:path/read/append/prepend · templates template:read/insert · unique
Plugins / Themes / Workspaceplugins plugin:* · themes theme:* snippets · workspace tabs recents
Devdevtools dev:cdp dev:errors dev:screenshot dev:console dev:css dev:dom eval

JSON output is available on base:query (default json; also csv/tsv/md/paths), search, search:context, backlinks, unresolved, links, tags, tasks, properties (yaml/json/tsv), outline, plugins, bookmarks, hotkeys.

Scripting gotchas:

  • Exit codes are always 0. A failed command reports the error in stdout and still exits 0. Parse output, never $?. This is the single most important thing for an agent wrapper to know.
  • No batching. Each command is a round trip to the running app, roughly a second. A per-file property:set across a large vault takes minutes.
  • move/rename update internal links only if that vault setting is on.
  • vault= must be the first parameter; vault:open works only in the TUI.

Verdict: stable, genuinely scriptable, and the right tool for writing (it applies templates and emits valid YAML). For bulk reading, parse the files directly — it is orders of magnitude faster.

A2. Bases

Docs: https://obsidian.md/help/bases/syntax

A .base file is YAML with filters, formulas, properties, summaries, views. Core plugin, data stays in markdown frontmatter.

Can it query properties and links? Yes, partially. File properties include file.links (all internal links including frontmatter), file.backlinks, file.tags, file.embeds, file.properties. Functions include file.hasLink(), file.hasTag(), file.inFolder(). this resolves to the embedding note, so file.hasLink(this.file) reproduces a backlinks pane.

Can it be driven from the CLI? Yesobsidian base:query file="X.base" view="Y" format=json. This is the most underrated capability in the current Obsidian stack: a declarative saved query with structured output, callable from a script.

The hard limits, quoted from the docs:

  • “There is no from or source like in SQL or Dataview.” Every file in the vault is the input set; filters narrow it.
  • No joins, no subqueries, no multi-hop traversal. You can test whether a file links to something. You cannot query the linked note’s properties, and you cannot follow a chain two hops.
  • file.backlinks is flagged performance-heavy and “does not automatically refresh results when the vault is changed.” The docs recommend reversing the lookup with file.links.

So Bases gives you a filterable table over one hop. It does not give you a graph query language.

A3. Dataview and Datacore — both in trouble

StarsLast pushOpen issuesStatus
blacksmithgu/obsidian-dataview9,3242025-11-17663~10 months stale, maintenance at best
blacksmithgu/datacore2,2322026-06-2279Still self-described “work-in-progress successor”

Dataview’s differentiator over Bases is inline fields (key:: value anywhere in the body), which Bases does not read. If you build on inline fields you are betting on a stalled plugin. Datacore has been the promised successor for years and still is not the answer.

Recommendation: do not build the typed-graph convention on Dataview inline fields. Use frontmatter, which Bases, the CLI, and any external YAML parser all read.

A4. Breadcrumbs — alive, and it changed hands

Repo: https://github.com/michaelpporter/breadcrumbs819 stars, pushed 2026-09-02, 3 open issues.

This is a repo move most references get wrong. As of May 2026, Breadcrumbs is maintained by michaelpporter, not SkepticMystic (credited as original creator). Current v4.15.0 requires Obsidian 1.13+ and uses the new declarative settings API; users on 1.12 stay on v4.14.2 (1.12-compat branch). Docs: https://breadcrumbs-docs.michaelpporter.com/

Three open issues on an 819-star plugin is a healthy signal, not a dead one.

What it does that Obsidian cannot: “Where Obsidian’s own graph only shows that two notes link, Breadcrumbs shows how they relate.” Explicit edge builders: Typed Links (frontmatter), Tag Notes, List Notes, Dendron Notes, Johnny.Decimal, Date Notes, Regex Notes, Traverse Notes, Dataview Notes, Folder Notes. Plus implied relations (A up B ⇒ B down A) and user-defined transitive rules. Views: breadcrumb trail, Matrix, Tree, ```breadcrumbs ``` codeblocks rendering Mermaid or Markmap, export to Canvas.

Typed-link syntax is exactly what you want:

---
up: "[[Parent Note]]"
---

Dataview inline (up:: [[note]]) also works, and as of v4.14.0 Dataview is no longer required — native inline fields are supported.

Caveat: Breadcrumbs is an in-app plugin. It has no CLI and no API. An external agent cannot query the Breadcrumbs graph; it can only read the same frontmatter Breadcrumbs reads. Use it as the human visualization of a convention your agent parses independently.

A5. Other graph plugins — mostly stale

PluginStarsLast pushVerdict
HEmile/juggl8212025-02-27~18 months stale. Do not adopt.
SkepticMystic/graph-analysis5302026-01-12Quiet, 31 open issues
zsviczian/excalibrain9942026-05-31Semi-active, 147 open issues
brianpetro/obsidian-smart-connections5,4362026-09-04Very active, but 490 open issues

Smart Connections is semantic similarity (local embeddings, no API key) — “related notes,” not typed edges. Useful for discovery, irrelevant to a typed graph. Smart Graph is its visualization layer.

A6. Obsidian’s graph view — the honest limits

Confirmed across the docs: the built-in graph has no typed edges, no query language, no edge properties, and no filtering beyond simple text/tag/path search. It renders undifferentiated link topology. It is a mood board, not a knowledge graph. Every typed-edge capability in Obsidian comes from a plugin or a convention you enforce yourself.

A7. Local REST API and the MCP ecosystem

coddingtonbear/obsidian-local-rest-api2,896 stars, pushed 2026-08-31, 2 open issues. Description now reads: “A secure REST API and Model Context Protocol (MCP) server for your vault.”

The plugin now ships its own MCP server, at https://127.0.0.1:27124/mcp/, Bearer auth. v3.6.0 (April 2026) added URL-based sub-document targeting for section-level GET/PUT/POST/PATCH. Two open issues on a 2,896-star plugin is an excellent health signal. Because it binds 127.0.0.1, Claude Desktop needs the mcp-remote bridge; Claude Code connects directly.

Community MCP servers, verified today:

ServerStarsLast pushStatus
bitbonsai/mcpvault1,6492026-08-31Active. Filesystem-based, Obsidian need not be running. Frontmatter-preserving edits. Renamed on npm to @bitbonsai/mcpvault at Obsidian’s request (March 2026). MCP v2 / 2026-07-28 spec as of v0.16.0. Patched a symlink path-traversal bug in v0.9.1.
StevenStavrakis/obsidian-mcp7332026-09-03Active again. v2 supports 2026-07-28 spec, needs Node 22+.
cyanheads/obsidian-mcp-server6742026-09-04Active. STDIO or Streamable HTTP, surgical edits + frontmatter.
jacksteamdev/obsidian-mcp-tools8322026-05-13ARCHIVED. Do not adopt.

Do these support search-by-property and link traversal? Partially and inconsistently. Most expose search, read, write, tags, and backlinks. None exposes a typed-edge traversal primitive, because Obsidian has no such primitive to expose. Link traversal, where offered, is untyped backlinks/outlinks.

Blunt assessment: for Claude Code specifically, an Obsidian MCP server is largely redundant. Claude Code already reads, greps, and edits local files natively and faster. The only real reasons to add one are frontmatter-safe YAML editing and daily-note routing. mcpvault is the best-maintained choice if you want it, precisely because it does not require the app to be running.

A8. Sync across two Macs, with an agent writing on both

This is where the risk actually lives, and the answer changes because an agent is involved.

Obsidian Sync8/mo annual (Plus: 10 vaults, 10 GB, 12 months history). Only option that merges markdown inside the app, using Google’s diff-match-patch. Non-markdown files are last-modified-wins. Since 1.9.7 you can choose per-device: “Automatically merge” (default) or “Create conflict file.”

The documented failure mode is directly relevant: if a note is created locally and Sync pulls a remote version of the same note within a couple of minutes, Sync keeps the remote version without merging. Obsidian’s docs call this out for daily notes and Templater. An agent writing notes on two machines is exactly that pattern at higher frequency.

iCloud Drive — Obsidian does not control the sync engine. Conflicts become (Conflicted copy from Computer A).md files you find weeks later, or silent overwrites. “Optimize Storage” can offload the vault to placeholders. Not suitable when an agent writes unattended.

obsidian-git — real three-way merges, full history, free. The recurring failure is the .obsidian folder: plugin data.json timestamps change on every launch, so two machines conflict on every pull. The fix is a thorough .gitignore of workspace and plugin state. Mobile conflict resolution is genuinely bad (issue #906).

The rule every source agrees on: never point two sync systems at one vault. Obsidian Sync plus a cloud folder is the same trap as Syncthing plus Dropbox.

Recommendation for this use case: git. Reasoning specific to agent writes:

  • An agent write is a reviewable change. Git gives you a diff and a revert; diff-match-patch gives you a silently merged file.
  • The vault should live beside the code it describes anyway, since half the graph is about code.
  • Merge conflicts become explicit and blocking, which is correct. A silent merge of two agent-authored notes produces plausible-looking garbage you will not notice.
  • basic-memory’s index is rebuildable per machine (bm reindex), so only the markdown needs to sync.
  • .gitignore .obsidian/workspace*.json, .obsidian/plugins/*/data.json, and the basic-memory SQLite file.

Obsidian Sync is the better choice only if mobile matters and you accept auto-merge. If you do use it, set “Create conflict file” on both Macs so agent writes never silently merge.

A9. Supersession and validity dates

Obsidian has no native concept of note supersession or validity dates. None. The only frontmatter keys it treats specially are tags, aliases, cssclasses (the singular forms tag/alias/cssclass were deprecated in 1.9). Everything else is your convention.

There is also no plugin that implements supersession properly. What exists is the ADR convention ported into frontmatter, which is what the ADR world has used for a decade:

status: superseded        # draft | active | deprecated | superseded | archived
superseded_by: "[[New Decision]]"
supersedes: "[[Old Decision]]"

Two distinctions worth stealing from ADR practice:

  • Deprecated ≠ superseded. Deprecated means no longer valid. Superseded means deprecated and there is a named replacement.
  • Supersede ≠ amend. A superseded record’s status flips; an amended record stays Accepted and gains an amended_by link. Two relation pairs, not one.

The governing principle, worth writing into the convention: old notes remain readable as written; their relevance to today lives in status + superseded_by, not in a rewrite. Never edit a decision to reverse it. Write a new one and link back.

adr-tools (npryce/adr-tools, 5,672 stars, last push 2024-04-25, dormant) automates the bidirectional rewrite: adr new -s 9 "Title" creates the successor and sets ADR 9’s status to “Superseded by [4. …]“. Its adr generate graph | dot emits Graphviz. Adopt the vocabulary, not the tooling — it has been dormant 2.4 years. Same for MADR (v4.0.0, 2024-09-17) and log4brains (v1.1.0, 2024-12-17).

A10. Best convention for machine-extractable typed edges

Ranked, for an external parser:

  1. Frontmatter list-of-links properties. Use this.

    depends_on: ["[[Cache Layer]]", "[[Auth Service]]"]
    supersedes: "[[ADR-0004 Old Approach]]"

    Read by: Bases (file.links includes frontmatter links), the CLI (property:read, properties format=json), Breadcrumbs (typed links), Obsidian’s link index (so backlinks work), and any YAML parser in three lines of code. It is the only option every consumer understands. Obsidian also rewrites these wikilinks on rename.

  2. Breadcrumbs fields — same frontmatter syntax, so this is free if you name your fields as Breadcrumbs edge fields. You get Matrix/Tree views and transitive rules as a bonus. No lock-in.

  3. A ## Relations section with - relation_type [[Target]] lines — this is basic-memory’s format (see B1). Machine-parseable, human-readable, diffs beautifully in a PR, and unlike frontmatter it supports repeated edges of the same type with different context. Obsidian renders it as ordinary links, so backlinks and graph view still work.

  4. Dataview inline fields (key:: value) — avoid. Bases cannot read them and Dataview has been stale since 2025-11-17.

The recommended convention combines 1 and 3: frontmatter for the note’s own typed attributes and lifecycle (status, supersedes, type), and a ## Relations section for the outbound edge list. That is precisely what basic-memory parses, and it is all valid Obsidian markdown.


PART B — Alternatives

B1. basic-memory — the recommendation

basicmachines-co/basic-memory3,878 stars, pushed 2026-09-06, 64 open issues, AGPL-3.0, created 2024-12-02. Latest release v0.23.2, published 2026-08-25. Repo topics literally include obsidian, obsidian-md, local-first, markdown, mcp. https://github.com/basicmachines-co/basic-memory · https://docs.basicmemory.com

Typed edges live in the markdown itself:

## Relations
- depends_on [[Caching Layer]]
- implements [[RFC-042 Async Processing]]
- supersedes [[ADR-0004 Old Approach]]
- "reviewed by" [[Platform Team]]

The token before the wikilink is the relation type. Multi-word types get quoted. Bare - [[X]] and inline body wikilinks index as generic links_to. Observations use - [category] fact #tag.

Edge properties: one. The relation table is (from_id, to_id nullable, to_name, relation_type indexed, context, project_id, generation) with uniqueness on (from_id, to_name, relation_type). context is free text on the edge. The nullable to_id plus to_name is how unresolved forward references work — an agent can write - supersedes [[Note That Doesn't Exist Yet]] and sync links it up when the target appears. That behavior matters a lot for an agent writing notes incrementally.

Files are the source of truth. SQLite is disposablebm reindex / bm reset rebuild the index from files without touching them. This is the architectural property that makes git sync work.

Agent surface, the most complete of anything surveyed:

  • MCP: write_note, read_note, edit_note, move_note, delete_note, search_notes, recent_activity, list_directory, build_context (graph traversal over memory:// URLs), schema_infer/schema_validate/schema_diff, plus readOnly/destructive annotations and output_format="json".
  • CLI: bm status|doctor|reindex|reset|orphans|inspect|project ..., and critically bm tool <name> invokes any MCP tool from a shell — so hooks and scripts get the identical surface the agent has.
  • HTTP: bm mcp --transport streamable-http --port 8000.
  • Ships an official Claude Code integration with lifecycle hooks and a memory-notes agent skill.

Query: memory://permalink, memory://docs/* wildcards, cross-project [[research::specs/api]], plus full-text and semantic/hybrid search with tag: and metadata filters.

Obsidian integration is a first-class, documented featurehttps://docs.basicmemory.com/integrations/obsidian

basic-memory project add main ~/path/to/your/obsidian-vault
basic-memory project default main

No plugin, no import/export layer. Both sides read and write identical files. Obsidian’s Graph View, Backlinks, and Tags panes all light up from the relations and observations. This is the whole reason the recommendation is “stay and add,” not “switch.”

Rename handling: permalinks are stable identifiers derived from path and survive rename and move. Docs do not promise automatic inbound-link rewriting on move_note — but Obsidian does that for wikilinks natively, which is a genuine reason to keep Obsidian in the loop rather than running basic-memory headless.

Supersession: not a built-in temporal model. You express it as relations (- supersedes [[X]]) plus frontmatter status:, and it becomes queryable. Convention, enforced by you.

Caveats, stated plainly:

  • AGPL-3.0. Fine for personal and internal use. Matters if it would ever be embedded in a distributed or network-served product.
  • v0.23.x, pre-1.0, and the syntax has already broken once — unquoted prose stopped being treated as a relation type in v0.21.0. Expect more churn before 1.0.
  • Cloud sync ($15/mo) is manual push/pull via rclone and unnecessary if you use git.
  • The docs do not address frontmatter rewriting or file-watching conflicts with Obsidian open simultaneously. Worth testing before trusting it with a large vault.

B2. Trilium / TriliumNext — the best typed graph, wrong substrate

TriliumNext/Trilium37,756 stars, pushed 2026-09-07, 687 open issues, AGPL-3.0. Latest v0.105.0, 2026-08-19. Repo id 92111509 created 2017-05-23 — it is the original zadam repo, transferred. TriliumNext/Notes is archived. Now led by eliandoran; zadam stepped back and, per the TriliumNext lead, “never came back.” https://triliumnotes.org

The strongest relation model anywhere in this survey. Attributes are labels (#name=value) and relations (~name=targetNoteId). A relation cannot exist without a target. On top of that:

  • Relation definitions give a real schema: mark a relation Inheritable to apply it to a subtree, and define an inverse relation so Trilium auto-maintains the reciprocal edge. Your supersedes ↔ superseded_by pair is maintained for you, by the database, for free. Nothing else surveyed does this.
  • The only native multi-hop traversal query language found: ~author.relations.son.title = 'Christopher Tolkien'. Plus regex %=, orderBy, limit, and searchable relationCount / revisionCount.
  • Note revisions are first-class and searchable.

Agent APIs, two, both shipped: ETAPI — REST since v0.50, Basic Auth since v0.56, OpenAPI spec in-repo, full CRUD on attributes including relations, Python client trilium-py. This is the oldest and most stable agent surface of anything surveyed. Built-in MCP since v0.103.0; v0.105.0 made it require Bearer auth (breaking change). Trilium’s own docs call the MCP server experimental and possibly less mature than third-party ones.

Why it loses: not plain text — one SQLite document.db, text stored as HTML. Two-Mac sync requires you to run a sync server, with matching Trilium versions and clocks within 5 minutes, or sync is rejected. You cannot put document.db in Dropbox. For “a lazy senior developer who does not want to run a server just for notes,” this is disqualifying on its own terms.

If typed queryable edges were the one requirement that could not bend, this is the tool. It is not, and they do.

B3. Logseq — the plain-text edition was demoted

logseq/logseq — 44,806 stars, pushed 2026-09-07.

The 2026 split is the headline. Logseq split into two products:

  • Logseq OG — file-based markdown, moved to https://github.com/logseq/og. Official position: “Logseq OG will continue to be maintained. It will receive security and Electron upgrades, but no new features.” Maintenance mode.
  • Logseq (the name now belongs to the DB version) — 2.0 Beta, v2.0.1 released 2026-07-13, SQLite is the canonical store. You no longer hand-edit files.

Roadmap status is “Beta testing,” with Logseq CLI and Logseq MCP still unshipped roadmap items. Whiteboards removed from the DB version. Markdown export from DB is one-way and not committed to. OG users are told official sync’s future is uncertain and to use git or Syncthing.

Rules itself out. The plain-text edition is frozen; the maintained edition is not plain text and its agent story does not exist yet.

B4. Anytype — excellent typed model, unreadable on disk

anyproto/anytype-ts — 8,758 stars, pushed 2026-09-07.

Typed edges: yes — the objects property format is a reference to other objects, so depends_on holding object IDs is a real named edge. Official MCP @anyproto/anytype-mcp (517 stars, pushed 2026-08-24) generated from the OpenAPI spec. Official CLI (published 2026-06-12) embeds anytype-heart and runs headless. API versioning is the most disciplined in the survey (Anytype-Version: 2025-11-08, new versions minted only for breaking changes).

Disqualified on plain text, harder than anyone else. The on-disk store is doubly encrypted per change, files sit in flatfs as encrypted fragments over a private IPFS network. No external parser can read it. Anytype’s own docs warn that syncing the working directory through a third-party cloud both leaks unencrypted index metadata and “will likely cause data corruption.” Also: query-param filters only, no relation traversal.

Self-hosted any-sync exists (anyproto/any-sync-dockercompose, 1,704 stars, pushed 2026-09-01) — but that is a server, which the brief rules out.

B5. SiYuan — best agent surface, no typed edges

siyuan-note/siyuan46,214 stars, pushed 2026-09-07, 30 open issues, AGPL-3.0. v3.8.3 released 2026-09-07.

Three first-party agent surfaces: kernel HTTP API on 127.0.0.1:6806, a built-in CLI with -f json and --dry-run that works without a running server, and a built-in MCP server shipped in v3.8.0 (2026-08-12) at /mcp. Query is real SQL over the SQLite index. Dejavu content-addressed snapshots for versioning.

Fails twice. No typed edges — block refs ((id "anchor")) are bidirectional but untyped, and custom attributes are a flat string bag. Not markdown.sy files are JSON block ASTs (deliberately, because CommonMark cannot anchor stable block IDs), though at least an external parser can read them. Sync is a trap: the README says “Data synchronization through third-party synchronization disks is not supported, otherwise data may be corrupted,” so no git, no Dropbox, no Syncthing — you need SiYuan’s own sync or the $64 PRO unlock for S3/WebDAV.

Also note a run of 2026 kernel CVEs (CVE-2026-32767 auth bypass to arbitrary SQL; CVE-2026-59834 SQL injection), fixed in v3.7.1+.

B6. Foam, Dendron, org-roam — the plain-text dev tools

Foam (foambubble/foam, 17,384 stars, pushed 2026-09-05, v0.44.6 released 2026-09-01). Healthy and actively maintained, but the README still says “High tolerance for alpha-grade software.” No typed links — only node types via frontmatter type:; edge styling is global. No agent API at all: no CLI, no HTTP, no MCP. The graph is computed in memory at runtime and never persisted, so there is nothing external to read. It is a viewer, not a store.

Dendrondead, confirmed. 7,464 stars, not archived (misleading). Last substantive commit 2023-08-12; zero GitHub releases ever published. Founder Kevin S. Lin announced the wind-down in Discord in February 2023: “as a business, we were ultimately not able to find product market fit for a venture backed business.” Never handed to the community. Users went to Obsidian.

org-roam (6,025 stars, pushed 2026-04-27, v2.3.1 released 2025-06-26). Maintained, not developed. SQLite schema v20: files, nodes, aliases, citations, refs, tags, links. links.type is the Org link protocol (id, http, cite, file) — not a user-defined relation. No typed edges.

Two hard blockers for an external agent: emacsql serializes values with prin1-to-string, so strings are stored with literal surrounding double quotes — you must query WHERE id = '"abc-123"'. And there is no non-Emacs writer: an agent can append .org text, but the index only updates when org-roam-db-sync runs inside Emacs, so round-tripping needs a live Emacs daemon.

B7. Backstage — right idea, wrong scale

backstage/backstage — 34,353 stars, pushed 2026-09-07, v1.54.6 published 2026-08-28.

Genuinely typed, paired, directional relations derived from catalog-info.yaml: ownedBy/ownerOf, dependsOn/dependencyOf, partOf/hasPart, providesApi/apiProvidedBy, consumesApi/apiConsumedBy, parentOf/childOf, memberOf/hasMember. Extensible. Mature REST API with a predicate language ($all/$any/$not/$exists/$in/$contains) and relations filterable and returned by default. Official MCP @backstage/plugin-mcp-actions-backend v0.2.1, introduced in v1.40.0 and described there as “highly experimental.”

Fails on three counts. Relations are bare (source, type, targetRef) triples — no properties on edges. No supersession — entities are current-state only, spec.lifecycle is a free string. And the graph exists only inside a running backend: the docs say “All paths are relative to a running catalog backend.” A Node service plus a database, for one developer on two Macs, for a fixed relation vocabulary. It is a platform team’s tool and it is service-catalog-shaped, not concept-shaped.

B8. Graphiti — the only real supersession model

getzep/graphiti — 30,658 stars, pushed 2026-09-06, Apache-2.0. Core v0.30.x published 2026-09-01; MCP server mcp-v1.1.0, 2026-09-01.

From graphiti_core/edges.py, EntityEdge carries name (the relation), fact, attributes, created_at, valid_at, invalid_at, expired_at. That is a genuine bi-temporal model with automatic fact invalidation — “when information changes, old facts are invalidated, not deleted” — and you can query as-of any point in time. This is the only tool anywhere in this survey where supersession is a first-class database concept rather than a naming convention. Custom entity and edge types via Pydantic.

Disqualified on substrate. Requires Neo4j 5.26+ or FalkorDB. Requires an LLM and an embedder on every write (default gpt-5.5), so ingest costs tokens and is nondeterministic. Nothing human-readable or git-diffable. Two-Mac sync means replicating a graph database. Its own README calls the MCP server “experimental.”

Worth knowing about. Not worth adopting here.

B9. The rest, briefly

ToolWhy it loses
TanaNot local-first — Google Cloud is the source of truth; offline mode is a cache and shared workspaces are read-only offline. Correction to a common claim: the API is no longer write-only — a local MCP shipped 2026-01-30 at localhost:8262 with read+write and get_tag_schema. But it dies with the laptop lid. Also renamed in March 2026: the outliner is now “Tana Outliner.” $8–14/mo.
CapacitiesNot plain-text, explicitly “not compatible with a folder structure based on markdown files.” API 2.0 + hosted MCP shipped July 2026; the legacy beta API was killed 2026-09-01. No query language. API requires Pro ($9.99/mo).
HeptabaseWhiteboard arrows carry no semantic relation. Filtering by property value is explicitly unsupported, so traversal is impossible. $8.99/mo.
ReflectNo typed edges. E2EE means the REST API cannot read note contents at all; MCP is the only read path. ~$10/mo.
RoamIntellectually the best edge model — Datalog over real datoms, and Key:: value compiles to a block that contains the ref, so edges are reified and can carry children. But: not local-first, official MCP/CLI self-labelled ALPHA, SDK last published ~3 years ago, and no undo that can reverse bulk API operations or deletions. A real data-loss hazard for an agent with write access.
NotionThe agent-ergonomics benchmark and worth stealing from: Markdown-native page read/write (GET/POST/PATCH /v1/pages/{id}/markdown), capability introspection, and MCP notion-query-data-sources with mode: "sql" doing real JOINs across data sources. But not local-first, export is explicitly not round-trippable, no version-history endpoint exists, and the two features that make it a usable agent graph require Business + Notion AI (~$240/yr/seat).
AFFiNE72,250 stars, v0.27.4 (2026-08-18). No typed edges. yjs CRDT binary. Official MCP is read-only shipped; write tools still rolling out. Backend packages are under an Enterprise license, not MIT.
Zettlr13,476 stars, v4.7.0 (2026-07-26), bus factor 1. The only unambiguously plain-text local-first tool besides Obsidian — and it has no typed edges, no CLI, no MCP, and no plugin system, with the maintainer on record against building one. Standard markdown links are not even counted in its graph (issue #4660).
DeepWikiHosted, read-only, no graph, lags your commits. Free MCP at https://mcp.deepwiki.com/mcp (announced 2025-05-22), tools read_wiki_structure, read_wiki_contents, ask_question. Register it read-only for third-party library questions. Never the store.
mem0 / Letta / MCP memory server / cogneemem0’s MCP went hosted-only in July 2026 (standalone repo archived March 2026, OpenMemory deleted 2026-07-29). Letta has no graph — it is an agent runtime with text memory blocks. The official @modelcontextprotocol/server-memory is a JSONL reference demo, self-described “a basic implementation,” no versioning, no prose. cognee is real and very active (v1.5.4, 2026-09-04) but database-backed with a weaker temporal model than Graphiti.

B10. Code symbols ↔ concept notes

No tool unifies these, and the industry has converged on why. Every code-graph tool treats its graph as derived and disposable, regenerated from source on change: colbymchenry/codegraph (69,901 stars, pushed 2026-08-31), DeusData/codebase-memory-mcp (42,518 stars, pushed 2026-09-07), abhigyanpatwari/GitNexus (47,105 stars, pushed 2026-09-07, includes a rename tool for coordinated multi-file symbol renames), p1va/symbols (LSP-backed rename).

That is the correct answer to symbol renames: do not persist symbol identity, re-derive it. Durable hand-authored typed edges belong on the concept-note side, joined to code by a stable anchor the note side owns. Two graphs, one anchor. Accept that a path+symbol string will occasionally go stale and need repair.


Comparison table

ToolTyped edgesPlain-text on diskAgent API / MCP (maturity)QuerySupersession2-Mac syncHealth (2026-09-07)Cost
Obsidian + basic-memoryYes, in markdown + frontmatterYes, markdownMCP + CLI (bm tool) + HTTP; plus Obsidian CLImemory:// traversal, FTS/semantic; Bases via base:queryConvention (supersedes + status); stable permalinks survive renamegit, no serverbm v0.23.2 (2026-08-25), pushed 2026-09-06; Obsidian 1.13.7Free (AGPL)
Obsidian aloneConvention onlyYesCLI (needs app running) + REST/MCP pluginsBases, 1 hop, no joinsNone nativegit / Sync $4–8 moExcellentFree
TriliumYes — schema + auto inverseNo (SQLite)ETAPI (stable) + MCP v0.103 (experimental)Multi-hop traversalRevisions; inverse relationsNeeds a sync serverv0.105.0 (2026-08-19)Free
SiYuanNoNo (.sy JSON)HTTP + CLI + MCP v3.8.0Real SQLDejavu snapshotsNo 3rd-party sync; $64 PROv3.8.3 (2026-09-07)Free / $64
AnytypeYesNo — encryptedOfficial MCP + CLI, versioned APIFilters, no traversalInternal onlyany-sync (server)pushed 2026-09-07Free–$16 mo
Logseq OGNoYesNoneDatalog (in-app)Nonegit/SyncthingMaintenance modeFree
Logseq 2.0PartialNo (SQLite)Roadmap, unshippedDatalogNoneCloudv2.0.1 beta (2026-07-13)Free
GraphitiYes + edge attrsNo (Neo4j)MCP v1.1.0, experimentalHybrid semanticBi-temporal, realReplicate a DBv0.30.x (2026-09-01)Free + tokens
BackstageYes, 7 pairsYAML, graph needs serverMature REST; MCP 0.2.1 experimentalPredicate APINoneNeeds a backendv1.54.6 (2026-08-28)Free + ops
FoamNoYesNoneNoneNonegitv0.44.6 (2026-09-01), “alpha”Free
org-roamNoYesRead-only SQL w/ quoting trap; no writes without EmacsSQLNonegitv2.3.1 (2025-06-26)Free
DendronNoYesNoneNoneNonegitDead (Feb 2023)
TanaYesNoLocal MCP (2026-01-30), read+writesearch_nodesNoneCloudActive$8–14 mo
RoamReifiedNoALPHA, no undoDatalogNoneCloud~10 staff, quiet$15 mo
NotionYes, thinNoBest ergonomics, hosted MCPREST 1 hop / SQL JOINs (paywalled)Exists, not in APICloudVery active$0–20/seat
ZettlrNoYesNoneNoneNonegitv4.7.0 (2026-07-26)Free

Answers to the three questions

One, and it is not a note-taking app: basic-memory. Typed relations live in the markdown (- depends_on [[X]]), files are the source of truth with SQLite as a disposable index, and the agent surface is MCP + CLI + HTTP. Bidirectionality is derived (the index knows inbound edges; build_context traverses them), not stored on both sides.

Two honest qualifications. It is v0.23.x, pre-1.0, and the relation syntax already broke once at v0.21.0. And “queryable” means memory:// traversal plus full-text and semantic search — not a declarative graph query language. If you want ~author.relations.son.title = 'X', only Trilium has it, and Trilium is SQLite behind a server.

Everywhere else the trade is forced: every tool with a strong typed-edge model abandons plain text (Trilium, Anytype, Tana, Roam, Graphiti, Backstage), and every strictly plain-text tool lacks either typed edges or an agent API (Obsidian, Foam, org-roam, Zettlr, Logseq OG). basic-memory is the only thing that refuses the trade, and it does so by being a layer rather than an app.

(2) Minimal conventions and plugins for a machine-extractable typed graph in Obsidian, and the best two-Mac sync

Conventions — four rules, nothing more:

  1. Typed edges as frontmatter list-of-links, and/or a ## Relations section with - relation_type [[Target]]. Frontmatter for the note’s own attributes and lifecycle; the Relations section for the outbound edge list. Both are read by Bases, the Obsidian CLI, Obsidian’s own link index, Breadcrumbs, basic-memory, and any YAML parser.
  2. A fixed, small relation vocabulary. Start with depends_on, implements, supersedes, superseded_by, amends, relates_to. Sprawl is the documented failure mode of every frontmatter convention.
  3. ADR lifecycle in frontmatter: status: draft|active|deprecated|superseded|archived plus superseded_by: / supersedes:. Never edit a note to reverse a decision — write a new one and link back. Keep deprecated (no replacement) distinct from superseded (named replacement), and amends (original stays active) distinct from supersedes.
  4. Never Dataview inline fields. Bases cannot read them and Dataview has been stale since 2025-11-17.

Plugins — two, both optional:

  • Breadcrumbs v4.15.0 (needs Obsidian 1.13+) for human-facing typed navigation: Matrix/Tree views, implied and transitive relations, Mermaid codeblocks. No lock-in; it reads the same frontmatter your parser does. Note the repo moved to michaelpporter/breadcrumbs in May 2026.
  • Local REST API v3.6.x only if you want section-level HTTP edits. Its built-in MCP server is the best-maintained plugin-based route (2,896 stars, 2 open issues, pushed 2026-08-31).

Skip Juggl (stale since 2025-02-27), obsidian-mcp-tools (archived), and Dataview.

Sync: git. An agent write is a reviewable change, and git is the only option that gives you a diff and a revert instead of a silent merge. The vault should live beside the code it describes anyway. .gitignore .obsidian/workspace*.json, .obsidian/plugins/*/data.json, and the basic-memory SQLite index; each machine rebuilds the index with bm reindex.

If mobile forces Obsidian Sync (8/mo Plus), set conflict resolution to “Create conflict file” on both Macs — it is per-device, and the default auto-merge will silently interleave two agent-authored versions of the same note. And never run two sync systems against one vault.

(3) Actual recommendation for a lazy senior developer who will not run a server

Stay on Obsidian. Add basic-memory pointed at the same vault. Sync with git. Steal the ADR status vocabulary. Add a code-graph MCP for symbols.

Concretely:

uv tool install basic-memory
basic-memory project add main ~/levandor_obsidian
basic-memory project default main
claude mcp add basic-memory -s user -- basic-memory mcp

Why this and not the alternatives:

  • It is additive. Your vault stays a vault. Obsidian’s Graph View, Backlinks, and Tags panes light up from basic-memory’s relations because they are ordinary wikilinks. No import, no export, no plugin, no lock-in. If basic-memory disappears tomorrow you still have a folder of markdown with typed frontmatter, which is exactly what you have today.
  • It closes the two real gaps. Obsidian gives you plain text, sync, rename-safe wikilinks, and a decent CLI, but no typed edges and no traversal for an agent. basic-memory adds typed edges in the files and a traversal API. Neither tool has to compromise.
  • No server. git plus bm reindex per machine. That rules out Trilium, Backstage, Anytype self-hosted, and Graphiti on the brief’s own terms, regardless of their merits.
  • Renames are handled by the right layer. Obsidian rewrites wikilinks on rename; basic-memory permalinks survive rename and move. Keeping Obsidian in the loop is a feature here, not overhead.
  • Supersession is a convention, and that is fine. Only Graphiti implements it properly, at the cost of Neo4j and an LLM call per write. The ADR convention has worked for a decade in plain text, and once it is in frontmatter it is queryable via base:query and greppable.

For code symbols, run a separate derived graphcodegraph or GitNexus as an MCP, regenerated per machine. Do not try to store symbol identity in notes. Anchor notes to code with a path+symbol string, accept occasional staleness, repair when it breaks.

Register DeepWiki read-only (claude mcp add -s user -t http deepwiki https://mcp.deepwiki.com/mcp) for third-party library questions. Free, no auth, no write-back.

What to watch: basic-memory is pre-1.0 and its syntax broke once at v0.21.0 — pin the version and read release notes before upgrading. Its AGPL-3.0 license is fine for personal use and matters only if this ever ships inside a distributed product. And test the “Obsidian open while basic-memory syncs” case on a copy first; the docs do not address file-watching conflicts.

If you try basic-memory and dislike it, the fallback is the convention alone: frontmatter typed links plus a ## Relations section, parsed by a 50-line script, with Breadcrumbs for navigation and base:query --format=json for tabular reads. That still beats every alternative in this survey on the stated requirements, and it costs one afternoon.


Sources

Obsidian: https://help.obsidian.md/cli · https://obsidian.md/help/bases/syntax · https://obsidian.md/changelog/ · https://github.com/obsidianmd/obsidian-help Plugins: https://github.com/michaelpporter/breadcrumbs · https://breadcrumbs-docs.michaelpporter.com/ · https://github.com/coddingtonbear/obsidian-local-rest-api · https://github.com/blacksmithgu/obsidian-dataview · https://github.com/blacksmithgu/datacore · https://github.com/HEmile/juggl · https://github.com/zsviczian/excalibrain · https://github.com/brianpetro/obsidian-smart-connections MCP servers: https://github.com/bitbonsai/mcpvault · https://github.com/StevenStavrakis/obsidian-mcp · https://github.com/cyanheads/obsidian-mcp-server · https://github.com/jacksteamdev/obsidian-mcp-tools (archived) basic-memory: https://github.com/basicmachines-co/basic-memory · https://docs.basicmemory.com · https://docs.basicmemory.com/integrations/obsidian · https://docs.basicmemory.com/concepts/observations-and-relations Alternatives: https://triliumnotes.org · https://docs.triliumnotes.org/user-guide/advanced-usage/attributes/relations · https://github.com/logseq/og · https://discuss.logseq.com/t/logseq-og-markdown-vs-logseq-db-sqlite/34608 · https://developers.anytype.io · https://doc.anytype.io/anytype/data/storage · https://github.com/siyuan-note/siyuan/blob/master/docs/API.md · https://github.com/foambubble/foam · https://www.orgroam.com/manual.html · https://backstage.io/docs/features/software-catalog/well-known-relations/ · https://github.com/getzep/graphiti · https://outliner.tana.inc/learn/features/local-api-mcp · https://cognition.com/blog/deepwiki-mcp-server Conventions: https://adr.github.io/madr/ · https://github.com/npryce/adr-tools · https://backstage.io/docs/architecture-decisions/