Local Sync

approved

by Liu Bo

LAN bidirectional vault sync — CRDT auto-merge, PSK auth, UDP discovery - This plugin has not been manually reviewed by Obsidian staff.

1 stars178 downloadsUpdated 29d agoMIT

🔄 Local Sync

Zero-cloud · Zero-conflict · Zero‑config — LAN bidirectional sync for Obsidian vaults

MIT License Obsidian Plugin Version Tests Dependencies PRs Welcome


🌐 LAN | 🔒 WSS/TLS | 🤝 CRDT | 📡 UDP | 🔐 PSK


✨ Features

🔗 LAN Direct Sync Peer‑to‑peer WebSocket/WSS connection. Zero cloud dependency — your data stays on your network.

🤝 Zero‑Conflict CRDT Yjs CRDT automatically merges concurrent text edits. No manual conflict resolution. Ever.

🔒 TLS Encrypted WSS transport with auto‑generated ECDSA P‑256 certificates. Certificate PIN verification prevents MITM attacks.

📡 Auto Discovery UDP broadcast finds devices on the same LAN. No IP typing needed.

🔐 Security First PSK challenge‑response authentication. Path traversal protection against rogue file writes.

🎯 Selective Sync Exclude folders & file types. Ignore .trash, .tmp, node_modules — your choice.

🖥️ Cross‑Platform macOS (NSFileCoordinator aware) Linux (writeFile + chmod)
Windows (write + lock‑retry)

🔌 Zero Config Install, set IP (or auto‑discover), and go. Certificates auto‑generate on first launch.


🚀 Quick Start

Installation

📁 Plugin ID: local-sync · Folder: your-vault/.obsidian/plugins/local-sync/

  1. Download the latest release from the Releases page
  2. Copy main.js + manifest.json + styles.css to your-vault/.obsidian/plugins/local-sync/
  3. Open Obsidian → ⚙️ SettingsCommunity pluginsEnable Local Sync

Usage

StepAction
1️⃣Open ⚙️ **Settings** → **Local Sync**
2️⃣Enter the **other device's IP** in "Target Address"
3️⃣Click **Connect**
4️⃣🟢 Status bar turns green — files are syncing!

💡 Pro tip: On the same subnet? Enable UDP discovery for zero‑config auto‑connect.
🔒 TLS is on by default with auto‑generated ECDSA P‑256 certificates.


🏗️ Architecture

┌──────────────────────────────────────────────────┐
│              UI Layer                            │
│  SettingTab / ConflictModal / StatusBar          │
│  └─ TLS settings, fingerprint display, reset     │
├──────────────────────────────────────────────────┤
│           Service Layer                          │
│  SyncEngine + CrdtEngine(Yjs) + ConflictDetector │
├──────────────────────────────────────────────────┤
│         Discovery Layer                          │
│  UDP broadcast + QR pairing (desktop↔mobile)     │
├──────────────────────────────────────────────────┤
│          Security Layer                          │
│  TLS encryption + PSK auth + path validation     │
│  └─ CertManager (ECDSA P‑256, auto‑generate)     │
├──────────────────────────────────────────────────┤
│       Network & IO Layer                         │
│  WebSocket(WSS/WS) + chokidar + OsWriter         │
│  └─ http/https dual‑protocol server              │
└──────────────────────────────────────────────────┘

Module Overview

LayerModuleResponsibility
🔒 Securitycert-manager.tsECDSA P‑256 certificate lifecycle (generate/load/reset)
🔒 Securityauth-handshake.tsPSK challenge‑response authentication
🔒 Securitypath-validator.tsPath traversal protection
🌐 Networkconnection-manager.tsWebSocket/WSS server/client/duplex + TLS fallback
🌐 Networkprotocol.tsMessage serialization protocol
🔄 Syncsync-engine.tsSync orchestration & file state tracking
🔄 Synccrdt-engine.tsYjs CRDT auto‑merge for text files
🔄 Syncconflict-detector.tsBinary‑only conflict detection
🔄 Syncinitial-sync.tsTwo‑phase initial sync (manifest → transfer)
📡 Discoverydiscovery-manager.tsUDP broadcast device discovery
📂 IOfile-watcher.tsChokidar‑based file change detection
📂 IOos-writer.tsPlatform‑aware file writing (macOS/Linux/Win)
🖥️ UIsetting-tab.tsSettings panel with 6 configuration sections
🖥️ UIsync-status-bar.tsStatus bar indicators
🖥️ UIconflict-resolver.tsBinary file conflict resolution dialog

🔄 How It Works

Sync Flow

┌─────────────────────────────────────────────────────────────────────┐
│                        Initial Sync                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Device A ── FILE_LIST_BATCH (manifest, 100 files/batch) ──→ Device B │
│                ←── FILE_LIST_ACK (missing + different) ──────────┤  │
│                ── FILE_RESPONSE (missing files, 10 concurrent) ──→   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                     Incremental Sync                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  File change detected                                                │
│      ├── TEXT (.md/.txt/.canvas) → CRDT incremental update          │
│      └── BINARY (.png/.pdf/.zip) → Full file transfer               │
│                                                                     │
│  Both paths: protected by recentlyPushed(2s) + originDeviceId       │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

TLS Handshake

Client (WSS)                  Server (WSS)
     │                            │
     │──── wss://connect ──────→│  TLS 1.2/1.3 handshake
     │←── TLS established ─────│  Transport encrypted
     │                            │
     │── cert-fingerprint ────→│  Exchange SHA‑256 fingerprints
     │←── cert-fingerprint-ack─│  PIN verification
     │                            │
     │── PSK challenge ───────→│  Inside encrypted channel
     │←── authenticated ──────│
     │                            │
     │── sync data (encrypted) →│  All traffic protected

Conflict Resolution

File TypeStrategy
📝 .md / .txt / .canvasYjs CRDT auto‑merge — no user intervention needed
🖼️ .png / .pdf / .zipDetected → User prompted to keep local / remote / both

Synchronization Safeguards

MechanismPurpose
recentlyPushed (2s TTL)Prevents sync loop
originDeviceIdIgnores changes originated from self
Debounce (500ms)Avoids redundant sync on rapid saves
UUID dedupPrevents duplicate message processing
Heartbeat (120s ping/pong)Detects dead connections
Exponential backoff (1s→60s)Smart reconnection
Version trackingDiscards stale file versions
Pending queueBuffers changes when offline, flushes on reconnect

🔒 TLS Encryption (v1.1.0)

FeatureDescription
ProtocolWSS (WebSocket Secure) — TLS 1.2/1.3
CertificateECDSA P‑256 self‑signed, auto‑generated on first launch
Storage~/.obsidian-sync/certs/cert.pem + key.pem
ValidationSHA‑256 fingerprint exchange + PIN code verification
FallbackAutomatic downgrade to plain WS (configurable)
DependenciesZero — all Node.js built‑ins (crypto, tls, http, https)

No CA needed. No OpenSSL setup. No external dependencies. Just works — with or without TLS.


🆕 What's New in v1.2.0

  • 🔍 Sync History Viewer — Category Filters: Filter the sync log by log level (Debug / Info / Success / Warning / Error) and by event type (file pushed / connected / sync completed …). Combines with existing path, date, and quick filters via AND.
  • 🌐 Full Log Localization (incl. DEBUG): All log levels are now Chinese, including the previously‑English DEBUG diagnostics (32 debugLog calls translated). Technical tokens (WS / WSS / TLS / UDP / CRDT / port / deviceId) and prefixes ([ObsSync] / [Obsidian Local Sync]) are preserved.
  • 📝 Diff Preview: Preview text/Markdown differences before they sync, powered by diff-preview-modal.ts + diff-preview-service.ts.
  • 📈 Quality: Build & type‑check clean; test suite grows to 239 passing (incl. 10 new filter unit tests). Zero breaking changes; minAppVersion stays 1.6.6.

🔍 Security & Review Notes

This plugin intentionally uses a few APIs that trigger Obsidian's automated review warnings. Each is required for the plugin's core LAN‑sync function and is scoped as tightly as possible:

  • Direct filesystem access (fs)By design. A sync plugin must read and write the files it synchronizes, which live outside the Obsidian vault. Access is limited to (a) the user‑configured sync paths and (b) the plugin's own data directory ~/.obsidian-sync/ (logs, CRDT snapshots, TLS certs). It never touches unrelated files.
  • System identity (os.networkInterfaces, os.homedir) — Used only for LAN peer discovery and to locate the plugin data dir. The plugin does not call os.hostname() or os.userInfo(), and no longer reads identity‑related environment variables (replaced process.env.HOME/USERPROFILE with os.homedir()). The device identifier is a randomly generated UUID, not derived from machine identity.
  • documentactiveDocumentFixed. All DOM creation in the sync‑history view now uses Obsidian's activeDocument global for popout‑window compatibility.
  • diff dependency advisory (GHSA‑73rr‑hh4g‑fpgx)Not affected. The dependency is diff@^7.0.0 (resolved 7.0.0); npm audit reports 0 vulnerabilities. The advisory only affects earlier diff versions.

📊 Project Stats

MetricValue
📁 Source Files26 .ts files, ~9,900 lines
🧪 Tests239 passing (unit + integration + E2E)
⏱️ Test Duration~1 second
📦 Build OutputSingle main.js (~620KB)
🔗 External Depsws · chokidar · yjs (all bundled)
🖥️ PlatformsmacOS ✅ · Linux ✅ · Windows ✅
🔒 TLS DepsZero (all Node.js built‑in)
🏷️ Latestv1.2.0 — Sync history filters, log i18n, diff preview

🛠️ Development

# Clone & build
git clone https://github.com/liuboacean/obsidian-local-sync-plugin.git
cd obsidian-local-sync-plugin
npm install
npm run build     # Production build (main.js)
npm run dev       # Watch mode (for Hot Reload plugin)
npm test          # Run all 239 tests

# Quick local test with two vaults:
cp main.js manifest.json styles.css /path/to/vault-a/.obsidian/plugins/local-sync/
cp main.js manifest.json styles.css /path/to/vault-b/.obsidian/plugins/local-sync/
# Open both vaults in Obsidian, enable plugin, connect

📋 Changelog

VersionDateHighlights
1.2.0Jul 7, 2026✅ Sync history viewer with category filters, full DEBUG log i18n, diff preview, 239 tests
1.1.0Jul 2, 2026✅ TLS encryption (WSS), ECDSA P‑256 certs, 148 tests
1.0.9Jul 2, 2026Obsidian community review fixes
1.0.8Jul 2, 2026Release format fix
1.0.7Jul 2, 2026Initial release, 131 tests

🗺️ Roadmap

  • Yjs CRDT auto‑merge
  • PSK auth + path security
  • UDP auto‑discovery
  • Cross‑platform file writer
  • TLS encryption (WSS) ← v1.1.0
  • Sync history viewer (P2)
  • Diff preview before sync (P2)

🤝 Contributing

PRs are welcome! Check the issues for areas to contribute.

📄 License

MIT © 2026 Obsidian Local Sync Team


Made with ❤️ for the Obsidian community

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.