⚡ Jync
July 10, 2026 · View on GitHub
⚡ Jync
Sync your Obsidian vault over JMAP — the protocol your mail server already speaks.
Change-driven. Self-hosted. Conflict-safe. Runs even in browser-based Obsidian.
Most self-hosted Obsidian sync bolts onto WebDAV or S3 and polls-and-diffs your whole vault to guess what changed. Jync takes a different path: it talks JMAP FileNode to a backend like Stalwart and asks the server one question —
"What changed since state
X?"
The server answers with an exact delta. No scanning the world. No guessing. Just the diff.
And because a JMAP server like Stalwart already serves your mail, calendar, and contacts, your notes live right next to them. One server. One protocol. One backup.
✨ Features
- 🔁 True incremental sync — driven by
FileNode/changesstate tokens, not directory polling. - 🧠 Bidirectional — edits flow both ways: local→remote and remote→local.
- 🛟 Conflict-safe by design — edit the same note in two places and you get a
… (remote conflict).mdcopy, never a silent overwrite. - 📦 Any file type — markdown, canvas, JSON, images, PDFs, large files — content is synced byte-for-byte.
- 🌳 Scoped & safe — sync one folder or the whole vault; local deletes are off by default.
- ♻️ Idempotent — a no-op sync does nothing, and pointing Jync at a backend that already has your notes adopts them instead of duplicating.
- 🌐 Browser-ready — 100% browser-safe (
requestUrl+ web-crypto), so it runs on desktop, mobile, and inside ignis browser Obsidian alike. - 🪶 Lightweight core — the JMAP client is plain TypeScript on native primitives, no heavy deps.
🚀 Quick start
# 1. Spin up a JMAP backend (or point Jync at an existing Stalwart server)
docker compose up -d
docker compose logs stalwart | grep -A2 password # grab the admin password
# 2. Build the Obsidian plugin
cd plugin && npm install && npm run build
# → drop manifest.json (repo root) + main.js into <vault>/.obsidian/plugins/jync/ and enable it
Or install the beta with BRAT (no build needed):
install BRAT from the community store → Add beta plugin → enter 1ARdotNO/Jync → enable Jync
in Settings → Community plugins. BRAT tracks each new release automatically.
Open Settings → Jync, enter your server URL and credentials, pick a sync-root folder, and hit sync. That's it.
🔐 Credentials & security
Please read before connecting Jync to a real server:
- Use a dedicated, least-privilege account — never your server's admin credential.
- Prefer a bearer token (an OAuth access token or app token) over a password: choose Authentication → Bearer token in settings.
- Use
https://for any non-local server. Jync warns you in settings if you point it at a plain-HTTP non-local host. - Credentials are stored in the plugin's
data.json(Obsidian's standard plugin storage), in plain text — the same as other plugins that hold tokens. Treat your vault's.obsidiandirectory accordingly.
Full threat model and remediation status: docs/SECURITY-REVIEW.md.
🎬 Conflict-safe sync in action
[jync] push new meeting-notes.md
[jync] push new diagram.canvas
[jync] push new screenshot.png
[jync] pull update roadmap.md
[jync] CONFLICT — kept both: roadmap.md and "roadmap (remote conflict).md"
[jync] sync done {pulled: 2, pushedNew: 8, pushedEdit: 1, deletedRemote: 0, conflicts: 1}
Edit a note on your laptop and your phone before they sync, and Jync keeps both versions — your work is never silently clobbered.
🧭 How it works
pull: FileNode/changes since <token> push: hash-diff the subtree
┌───────────────────────────────────────┐ ┌───────────────────────────────────┐
│ remote creates / edits / deletes ──► │ │ ──► upload blob + create/update │
│ applied to the vault │ │ or destroy on the server │
└───────────────────────────────────────┘ └───────────────────────────────────┘
Content rides on JMAP blobs; a FileNode links name + parent + blob. The pull baseline
advances past your own writes each cycle, so nothing ping-pongs. Full design in
docs/ARCHITECTURE.md.
📚 Documentation
| Doc | What's inside |
|---|---|
🚀 docs/SETUP.md | Step-by-step: server, account, install, configure |
📐 docs/ARCHITECTURE.md | The FileNode model, the two-way sync loop, conflict handling, and safety guarantees |
📋 docs/CAPABILITIES.md | Supported operations, file types, and behavior; plus running under ignis |
🔬 docs/FINDINGS.md | Design notes: why JMAP FileNode, Stalwart specifics, and the ignis integration paths |
🔐 docs/SECURITY-REVIEW.md | OWASP/STRIDE review + remediation status |
Contributing? See CONTRIBUTING.md. Reporting a vulnerability? SECURITY.md.
🗂️ Repository layout
| Path | What |
|---|---|
plugin/ | The Obsidian plugin — browser-safe client, sync engine, settings |
src/ | Standalone Node JMAP client + example scripts (roundtrip.ts, probe-edit.ts) |
scripts/remote.ts | Server-side helper: inspect / reset the FileNode tree |
e2e/run.mjs | Headless-browser harness for driving the plugin inside Obsidian |
docker-compose.yml | Local Stalwart with FileNode enabled |
🚦 Status
Jync is pre-1.0 and under active development toward the Obsidian community store. Known limitations on the roadmap:
- 🔐 Authentication supports HTTP Basic or a bearer token (OAuth access/app token); interactive OAuth sign-in is planned.
- ✏️ Rename re-uploads content instead of moving the node in place.
- 📈 Push rescans the sync-root each cycle (an mtime fast-path is planned for very large vaults).
- 📄 JMAP FileNode is an IETF draft (
draft-ietf-jmap-filenode); the wire format may still change.
Contributions and issues welcome.