AI-agent heeft eigen Obsidian-client: headless vault synchronisatie met CouchDB LiveSync
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:
- AI-agent schrijft
notitie.mdvia WebDAV naar Nextcloud - LiveSync detecteert het nieuwe bestand op schijf en probeert het naar CouchDB te uploaden
- Tegelijkertijd probeert LiveSync via CouchDB replicatie wijzigingen van andere apparaten binnen te halen
- 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: falsein de LiveSync instellingen). DeE2EE_PASSPHRASEis 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:
| Commando | Functie | Voorbeeld |
|---|---|---|
list | Bestanden overzicht | obsidian-vault list "Projecten/" |
read | Bestand lezen | obsidian-vault read "Notities/idee.md" |
write | Nieuw bestand | echo "inhoud" | obsidian-vault write "Notities/nieuw.md" |
patch | Gerichte bewerking | obsidian-vault patch "Notities/doc.md" --old "foo" --new "bar" |
grep | Zoek in inhoud | obsidian-vault grep "TODOs" --path "Projecten/" |
search | Zoek in paden | obsidian-vault search "\.md$" |
meta | Bestand metadata | obsidian-vault meta "Notities/idee.md" |
delete | Bestand verwijderen | obsidian-vault delete "Notities/oud.md" --yes |
dump | Hele vault export | obsidian-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
patchbovenwritevoor bewerkingen) - Hoe paden te structureren (vault-relatief, niet absoluut)
- Waarom grep altijd een
--pathscope 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
| Aspect | WebDAV (oud) | CouchDB CLI (nieuw) |
|---|---|---|
| Sync | Race conditions met LiveSync | Volledig compatibel |
| Snelheid | Bestand via HTTP + server schrijft naar schijf | Direct naar database |
| Encryptie | Plain text (Nextcloud) | Ondersteunt E2EE transparant |
| Betrouwbaarheid | Bestand kan locked zijn door LiveSync | Altijd consistent |
| Features | Alleen read/write | list, 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:
E2EE_PASSPHRASEoptioneel gemaakt — als de variabele niet is gezet of leeg is, wordtundefinedgebruiktencrypttoegevoegd aan vault settings — hetencryptveld uit het milestone document (encrypt: false) wordt nu wél uitgelezen- Passphrase alleen doorgeven als encryptie aan staat — het options object naar
DirectFileManipulatorkrijgt alleen een passphrase alsvaultSettings.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: falseheeft (te zien in het milestone document in CouchDB) - Zorg dat
E2EE_PASSPHRASEleeg of ongedefinieerd is als encryptie uit staat - Pas zo nodig
connection.tsaan 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.