AI-agent heeft eigen Obsidian-client: headless vault synchronisatie met CouchDB LiveSync

· 7 min lezen

In mijn vorige artikel beschreef ik hoe ik Obsidian LiveSync heb opgezet met self-hosted CouchDB. Daar noemde ik al kort dat een AI-agent het client-side installatieproces kan automatiseren. Maar toen was er nog een fundamenteel probleem: hoe krijgt die AI-agent zelf toegang tot de vault?

Dit artikel gaat over de volgende stap: een headless CLI die rechtstreeks met CouchDB praat, precies zoals de LiveSync plugin, waardoor AI-agents (OpenCode, Claude Code, etc.) notities kunnen lezen, schrijven en bewerken — en die wijzigingen via LiveSync naar al mijn apparaten synchroniseren.

Het probleem: WebDAV versus LiveSync

Voordat ik CouchDB had, gebruikte ik Nextcloud’s WebDAV om de vault te benaderen. Mijn AI-agent (OpenCode) had een skill die via curl-commando’s bestanden schreef naar de Nextcloud WebDAV share.

Het probleem: LiveSync synchroniseert ook naar diezelfde bestanden op de schijf. Het resultaat was een race condition:

  1. AI-agent schrijft notitie.md via WebDAV naar Nextcloud
  2. LiveSync detecteert het nieuwe bestand op schijf en probeert het naar CouchDB te uploaden
  3. Tegelijkertijd probeert LiveSync via CouchDB replicatie wijzigingen van andere apparaten binnen te halen
  4. Conflict — en soms dubbele of corrupte bestanden

De oplossing is simpel: schrijf niet via het bestandssysteem, maar rechtstreeks naar CouchDB, precies zoals de LiveSync plugin dat doet.

De oplossing: obsidian-vault-cli

Ik vond obsidian-vault-cli van Luca Fanselau, een headless CLI die dezelfde livesync-commonlib bibliotheek gebruikt als de officiële Obsidian plugin. Het verbindt direct met CouchDB via PouchDB en handelt alle chunking, hashing en encryptie transparant af.

Hoe het werkt

obsidian-vault CLI
  └── DirectFileManipulator (livesync-commonlib)
        ├── PouchDB → CouchDB (direct HTTP, geen lokale replica)
        ├── AES-256-GCM encryptie (transparant via transform-pouch)
        └── Rabin-Karp content chunking (content-addressed, deduplicatie)

Het maakt niet uit of je vault versleuteld is of niet — de CLI detecteert de instellingen automatisch door het _local/obsydian_livesync_milestone document in CouchDB uit te lezen. Chunk size, hash algoritme, encryptie algoritme, path obfuscation — alles wordt van de server gehaald. Geen handmatige configuratie nodig.

Installatie

De installatie is eenvoudig:

# Clone met submodules (bevat livesync-commonlib)
git clone --recursive https://github.com/fanselau/obsidian-vault-cli.git
cd obsidian-vault-cli

# Installeer dependencies
npm install

# Configuratie
cp .env.example .env

Het .env bestand bevat de CouchDB credentials:

COUCHDB_USER=obsidian-admin
COUCHDB_PASSWORD=<sterk-wachtwoord>
E2EE_PASSPHRASE=
DB_NAME=obsidian
COUCHDB_URL=http://<jouw-tailscale-hostname>:5984

Over encryptie: Mijn vault heeft geen E2EE ingeschakeld (encrypt: false in de LiveSync instellingen). De E2EE_PASSPHRASE is optioneel — als de vault encryptie uit heeft, wordt die niet gebruikt. Let op: de CLI gaat er standaard van uit dat encryptie aan staat en vereist een niet-lege passphrase. Zie de sectie over de bugfix verderop.

Installeren en klaar:

bash install.sh
# → Installed: ~/.local/bin/obsidian-vault

Alle commands

De CLI biedt alles wat je nodig hebt om een vault te beheren:

CommandoFunctieVoorbeeld
listBestanden overzichtobsidian-vault list "Projecten/"
readBestand lezenobsidian-vault read "Notities/idee.md"
writeNieuw bestandecho "inhoud" | obsidian-vault write "Notities/nieuw.md"
patchGerichte bewerkingobsidian-vault patch "Notities/doc.md" --old "foo" --new "bar"
grepZoek in inhoudobsidian-vault grep "TODOs" --path "Projecten/"
searchZoek in padenobsidian-vault search "\.md$"
metaBestand metadataobsidian-vault meta "Notities/idee.md"
deleteBestand verwijderenobsidian-vault delete "Notities/oud.md" --yes
dumpHele vault exportobsidian-vault dump ./export

Opvallend is de patch command, die speciaal ontworpen is voor AI-agents. Het werkt als een chirurgische vervanging — lees het bestand, pas de wijziging toe, schrijf terug — in één atomic operatie. Net zoals de Edit tool van Claude Code of OpenCode, maar dan direct in de LiveSync vault.

Voorbeelden van de CLI in actie

# Alle notities in een projectmap
obsidian-vault list "Projecten/MijnApp/"

# Een specifieke notitie lezen
obsidian-vault read "Projecten/MijnApp/ideeen.md"

# Zoeken naar architectuur-beslissingen
obsidian-vault grep "architectuur" --path "Projecten/MijnApp/" --long

# Gerichte bewerking (preferred boven write)
obsidian-vault patch "Projecten/MijnApp/ideeen.md" \
  --old "## Status: Concept" \
  --new "## Status: Goedgekeurd"

# Toevoegen aan een changelog
obsidian-vault patch "Projecten/MijnApp/changelog.md" \
  --append "\n## $(date +%Y-%m-%d)\n- Eerste versie"

# Nieuwe notitie aanmaken
obsidian-vault write "Projecten/MijnApp/notulen.md" "# Notulen\n\n## Besluiten\n- ..."

Integratie met OpenCode

De echte kracht zit in de integratie met mijn AI-agent. Eerder had ik een skill die WebDAV curl-commando’s gebruikte. Die heb ik vervangen door een skill die de obsidian-vault CLI aanroept.

Het skill-bestand (te vinden in de repo onder skills/obsidian-livesync.md) leert de AI-agent:

  • Wanneer welke command te gebruiken (prefer patch boven write voor bewerkingen)
  • Hoe paden te structureren (vault-relatief, niet absoluut)
  • Waarom grep altijd een --path scope nodig heeft (performance)
  • Dat wijzigingen direct zichtbaar zijn via LiveSync

Het resultaat: ik kan tegen OpenCode zeggen “zoek in mijn vault naar notities over Tailscale configuratie en schrijf een samenvatting” — en de agent doorzoekt de vault, leest relevante notities, schrijft een nieuwe notitie, en die verschijnt binnen seconden in Obsidian op al mijn apparaten.

Skill plaatsen

# Voor OpenCode
cp skills/obsidian-livesync.md ~/.config/opencode/skills/obsidian-livesync/SKILL.md

# Voor Claude Code
cp skills/obsidian-livesync.md ~/.claude/skills/obsidian-livesync/SKILL.md

Waarom dit beter is dan WebDAV

AspectWebDAV (oud)CouchDB CLI (nieuw)
SyncRace conditions met LiveSyncVolledig compatibel
SnelheidBestand via HTTP + server schrijft naar schijfDirect naar database
EncryptiePlain text (Nextcloud)Ondersteunt E2EE transparant
BetrouwbaarheidBestand kan locked zijn door LiveSyncAltijd consistent
FeaturesAlleen read/writelist, grep, patch, search, meta

Bug: "File seems to be corrupted" en hoe we het fixten

Bij de eerste test schreven we twee notities naar CouchDB. Ze verschenen in de database, maar synchroniseerden niet naar de desktop Obsidian client. Het log gaf:

File Homelab/Software Infrastructuur/CouchDB.md seems to be corrupted!
Writing prevented. (3253 != 5582)

Dit bleek een encryptie-bug in de CLI.

Oorzaak

De CLI’s loadConfig() functie vereiste een niet-lege E2EE_PASSPHRASE. Gebruikers zonder E2EE vulden een dummy waarde in om de foutmelding te omzeilen. Maar de DirectFileManipulator in livesync-commonlib doet interne logica die het encrypt veld afleidt uit de passphrase:

// DirectFileManipulatorV2.ts
encrypt: this.options.passphrase ? true : false

Het resultaat: een niet-lege string → encrypt: true → alle chunks worden versleuteld. Maar de size in het metadatadocument wordt berekend over de onversleutelde data. De LiveSync plugin op de desktop, die encrypt: false in zijn config heeft, probeert de chunks te lezen als plain text, ziet dat het binaire garbage is (AES-GCM versleuteld), en weigert te schrijven omdat de size niet matcht.

Fix

De root cause was dat fetchVaultSettings() wel het E2EEAlgorithm veld uit het milestone document uitleest, maar niet het encrypt veld. De oplossing in src/lib/connection.ts:

  1. E2EE_PASSPHRASE optioneel gemaakt — als de variabele niet is gezet of leeg is, wordt undefined gebruikt
  2. encrypt toegevoegd aan vault settings — het encrypt veld uit het milestone document (encrypt: false) wordt nu wél uitgelezen
  3. Passphrase alleen doorgeven als encryptie aan staat — het options object naar DirectFileManipulator krijgt alleen een passphrase als vaultSettings.encrypt === true
// Voor (connection.ts — fout)
passphrase:         config.passphrase,         // altijd
obfuscatePassphrase: config.passphrase,         // altijd

// Na (connection.ts — gefixt)
passphrase:         vaultSettings.encrypt ? config.passphrase : undefined,
obfuscatePassphrase: vaultSettings.encrypt ? config.passphrase : undefined,

Na deze fix matchen de size velden weer met de daadwerkelijke chunk bytes, en accepteert LiveSync de documenten zonder corrupted foutmelding.

Lessen voor andere gebruikers

Als je obsidian-vault-cli gebruikt en "File seems to be corrupted" fouten ziet in je Obsidian log:

  • Check of je vault encrypt: false heeft (te zien in het milestone document in CouchDB)
  • Zorg dat E2EE_PASSPHRASE leeg of ongedefinieerd is als encryptie uit staat
  • Pas zo nodig connection.ts aan zoals hierboven beschreven
  • De auteur van de CLI is op de hoogte van de bug en werkt aan een upstream fix

Technische details

Zonder E2EE

Mijn vault gebruikt geen end-to-end encryptie. De LiveSync plugin slaat data in CouchDB dan op als plain text chunks:

  • Metadatadocument: homelab/infra/couchdb.md — lowercase pad als ID, bevat timestamps en chunk lijst
  • Chunkdocumenten: h:+<xxhash64> — bevat de daadwerkelijke content

Het encrypt: false veld in de vault configuratie zorgt dat de plugin data onversleuteld laat. De CLI leest deze instelling uit het milestone document en past zich aan — als encryptie uit staat, wordt de passphrase niet doorgegeven aan de encryptielaag.

Settings auto-detectie

De CLI leest bij opstarten het _local/obsydian_livesync_milestone document uit CouchDB. Dit document bevat alle instellingen zoals ze door de LiveSync plugin zijn geconfigureerd:

{
  "tweak_values": {
    "PREFERRED": {
      "customChunkSize": 0,
      "minimumChunkSize": 20,
      "hashAlg": "xxhash64",
      "chunkSplitterVersion": "v3-rabin-karp",
      "E2EEAlgorithm": "v2",
      "encrypt": false,
      "usePathObfuscation": false,
      "enableCompression": false,
      "doNotUseFixedRevisionForChunks": true,
      "usePluginSyncV2": true
    }
  }
}

Hierdoor werkt de CLI altijd met dezelfde instellingen als de Obsidian plugin — ook als die instellingen later wijzigen. Het encrypt veld is hierbij het meest kritisch: als dit overschreven wordt door een default (zoals in de bug), ontstaan er corrupte bestanden.

Conclusie

Met obsidian-vault-cli heb ik een brug geslagen tussen mijn AI-agent en mijn persoonlijke kennisbank. De CLI is klein, heeft geen draaiende service nodig, en synchroniseert naadloos met LiveSync.

Voor mij is dit de ideale setup: Obsidian als GUI voor dagelijks gebruik, de CLI als API voor automatisering — en LiveSync als de lijm die alles verbindt. Het mooiste is dat ik niet hoef te kiezen: een notitie die ik via OpenCode laat schrijven, verschijnt gewoon in mijn Obsidian op mijn telefoon en desktop, zonder dat ik er iets voor hoef te doen.

De tool is open source (MIT) en te vinden op github.com/fanselau/obsidian-vault-cli. Wie een AI-agent wil laten meeschrijven in een LiveSync vault kan er direct mee aan de slag.