kastor6 downloadsSync your vault through your own ovaultd server: end-to-end encrypted, revision-based, no third parties.
The Obsidian plugin for ovaultd — self-hosted, end-to-end encrypted sync for your vault: github.com/kastormdz/ovaultd.
Everything runs inside Obsidian. The plugin embeds the same durable core as the CLI (versioned IndexedDB state, save-first outbox, apply journal), so your vault stays in sync with your own server — and the server never sees your notes: content and paths are encrypted on your device with keys derived from your vault password, which never leaves it.
The server, the shared libraries (@ovaultd/protocol, @ovaultd/crypto, @ovaultd/core) and the CLI live in the main repository. This repository holds the plugin alone — the community directory requires the manifest and the release assets at the root of the submitted repository.
pull → push cycle runs under a lock. No timers, no polling: a change is what wakes it up. The status bar shows its state (al día, sincronizando…, ⚠ conflictos, error).obsidian/electron external and fails the build if any Node builtin (fs, path, child_process, …) leaks in — the same main.js must run on mobile, where Node does not exist. Crypto, state and sync are plain browser JavaScript.estado-local.json (account token, vault id/epoch, derived subkeys), sesion-cuenta.json (your account session, survives unlinking) and a bounded motor.log. Passwords are never written to disk.Your vault password is stretched into a KEK, which unwraps the vault key; from those, per-content keys and signing keys are derived. Changed files are encrypted and uploaded as opaque blobs; the server stores them with signed revision records chained by expected_rev. Pulling is a delta since your last known revision; pushing signs every operation. If someone else edited ahead of you, you get a conflict instead of a lost edit.
The plugin travels on a release train with the server: the same MAJOR.MINOR on both sides is a pair that was tested together (patches are independent).
| Server | Plugin | Wire |
|---|---|---|
0.2.x |
0.2.x |
v2 |
Before linking anything, the plugin asks the server GET /api/v1/meta and checks that contract: if the server requires a newer plugin than this one, it refuses to link and says so (with a message pointing at the plugin); if this plugin is a train ahead, it warns once and goes on. It also identifies itself on every request with x-ovaultd-client: oVaultd-plugin/<version>, so the server's logs show which client is talking.
Releases are cut by tag: X.Y.Z — exactly the version in manifest.json, no prefix, which is what the community directory requires. The GitHub Release carries main.js, manifest.json and styles.css as loose binary assets (signed with a build-provenance attestation), so Obsidian can install it directly, and so can BRAT.
Unpack those three files into <vault>/.obsidian/plugins/ovaultd/, or point BRAT at this repository. What changed in each version lives in CHANGELOG.md, and the release notes are taken from there.
Following the community directory's developer policies:
/setup. It never leaves your infrastructure./setup; further accounts come from the admin panel or the ovaultd-admin CLI.../ovaultd) while @ovaultd/* is not published to npm.Build it and install into a vault:
npm install
npm run build
npm run install:dev -- --vault "$HOME/path/to/your/vault"
Then enable it in Obsidian: Settings → Community plugins → oVaultd Sync. (The same three files — main.js, manifest.json, styles.css — can be copied by hand into <vault>/.obsidian/plugins/ovaultd/.)
Settings → oVaultd Sync:
Two different secrets, easy to confuse: the account password (which the server knows how to verify) and the vault password (which the server never sees — it opens your notes). Losing the vault password is not fatal: the recovery key is the second lock.
Commands in the palette (Ctrl+P): Probar conexión, Sincronizar ahora, Estado del motor, Exportar el vault a una carpeta…, Importar una carpeta al vault…, Resolver conflicto….
npm install
npm run dev # esbuild in watch mode → main.js
npm run build # typecheck (tsc -noEmit) + production build (with the anti-Node gate)
The plugin imports @ovaultd/{core,crypto,protocol} from vendor/, a generated copy of the
monorepo's packages, so this repository builds on its own (clone it, npm install, npm run build — no sibling checkout needed). To pull the current core in:
node scripts/sincronizar-core.mjs # clona el monorepo si no está al lado
Never edit vendor/ by hand: changes go to the monorepo and come in through that script. The
release workflow re-runs it and fails if the copy is stale, so a published plugin can never
ship a different core than the one it was tested with.
Sync is the risky part, so the loop for changes that touch it: build, copy to a real vault, and watch motor.log and the status bar — the engine is designed to be observed. The repository ships docs/spike-eventos.md, a measured report on how Obsidian's watcher actually behaves (external renames arrive as create+delete, dotfiles emit no events, content is stable by the time the event lands) — those measurements are what the engine is built on.
Before every push: scripts/scan-leaks.sh (plus .githooks/pre-commit, activated with git config core.hooksPath .githooks). Personal patterns go in scripts/leaks.local (never versioned).
MIT — see LICENSE.