Obsidian Connector
Two-way sync between an Obsidian vault and the Fabric graph — local, file-watcher driven, no plugin install.
What it does
Points the self-hosted Fabric daemon at one or more Obsidian vault folders. The connector:
- Ingests every
.mdnote as a Fabric node — YAML frontmatter, body,#tags, and[[wikilinks]]extracted automatically. - Stays current via a filesystem watcher (changes flow into Fabric within ~200 ms), with a 60 s interval poll as a fallback.
- Emits back when Fabric writes a note — atomic tempfile + rename, conflict-safe sidecar on concurrent external edits.
- Tracks renames by content-derived stable IDs (frontmatter
id:if present, else SHA-256 of body) so wikilink targets survive moves.
No Obsidian plugin, no auth flow. The daemon reads the vault directory directly.
Prerequisites
- Self-hosted Fabric daemon (
rsc-api-server) running on the same host as the vault — local-host only in v1. - Obsidian vault is a plain directory of
.mdfiles (no encrypted/iCloud-locked vaults). - POSIX filesystem for atomic emit. macOS and Linux are first-class; Windows is best-effort (non-atomic rename across volumes).
Configure
Set OBSIDIAN_VAULTS before starting the daemon:
export OBSIDIAN_VAULTS="research=/Users/me/Obsidian/Research,journal=/Users/me/Obsidian/Journal"The value is a comma-separated list of vault_id=path pairs. The vault_id is a stable handle you choose — it appears in install IDs and the provenance.source_id of every ingested record.
Optional:
| Variable | Default | Purpose |
|---|---|---|
OBSIDIAN_TENANT_ID | 0b51d1a4-0b51-4d1a-b51d-1a40b51d1a40 | Tenant UUID for ingested records. Override when running multi-tenant. |
OBSIDIAN_USER_ID | (per-vault derived) | Pin every install to one user profile. Single-vault only — rejects 2+ vaults. |
Bad UUIDs in either variable fail boot with a clear error. Bad paths in OBSIDIAN_VAULTS skip just that install and log a warning; other vaults still register.
Verify
Start the daemon and check the boot log for one line per vault:
INFO Obsidian per-install scheduler started (watcher + interval)
install_id=… vault_id=researchThen drop a note in the vault — it should appear in the graph within a few seconds. Tags become tag edges, wikilinks become link edges, frontmatter fields land on the node.
What gets ingested
For each .md file:
- Frontmatter — full YAML mapping kept on the node. Top-level sequences or scalars are rejected (the daemon logs and dead-letters the note rather than silently dropping the frontmatter).
- Tags — both YAML
tags:and inline#hashtagsyntax. Tags inside fenced``` or inline`code`are ignored. - Wikilinks —
[[Target]]and[[Target|alias]]become edges. Pipe-aliases are stripped; the target is canonical. Wikilinks inside code blocks are ignored. - Body — the markdown after the frontmatter, verbatim.
Hidden directories (.obsidian/, .git/, .trash/) are skipped.
Emit behavior
When Fabric writes a note back to the vault:
- The write goes to a tempfile in the same directory, then a
rename(2)swaps it onto the canonical path — readers never see a partial file. - If the canonical file's mtime has advanced past Fabric's last-seen value (something else changed it), the emit writes to
<basename>.conflict-<UTC-ts>-<rand>.mdinstead. No clobber. Operators can greptracingwarns foremit_conflictto find them. - A
rel_pathcontaining..or absolute path is rejected up front.
Dead-letter persistence for emit conflicts is deferred to v1.5 — the sidecar file IS the recovery surface in v1.
Replay a backfill
If the cursor gets out of sync (e.g. you restored the vault from backup), drive a fresh backfill without restarting the daemon:
curl -X POST "http://localhost:3000/api/v1/connectors/installs/<install_id>/backfill"The endpoint refuses if the install isn't active.
Limitations
- Local-host only. Vault and daemon share a filesystem. Multi-tenant SaaS needs a per-tenant agent (deferred — see issue #233).
- Last-writer-wins on the canonical path; concurrent writes during the brief read-mtime → rename window can be overwritten silently. Acceptable for a single-user local vault.
- No
PATCH /configfor Obsidian installs — vault path is fixed at boot. ChangeOBSIDIAN_VAULTSand restart.
See also
- Fabric Indexer — the graph engine that stores ingested notes
- MCP Reference — query the indexed vault from your AI client
