Neural network background

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 .md note 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 .md files (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:

VariableDefaultPurpose
OBSIDIAN_TENANT_ID0b51d1a4-0b51-4d1a-b51d-1a40b51d1a40Tenant 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=research

Then 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 #hashtag syntax. 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>.md instead. No clobber. Operators can grep tracing warns for emit_conflict to find them.
  • A rel_path containing .. 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 /config for Obsidian installs — vault path is fixed at boot. Change OBSIDIAN_VAULTS and restart.

See also