Self-hosted LiveSync for Obsidian

repository·main·Indexed 11 days ago

https://github.com/vrtmrz/obsidian-livesync

A synchronization plugin for Obsidian that enables users to host their own data using CouchDB, S3-compatible object storage, or WebRTC peer-to-peer connections. Version 1.0.12 provides a CLI for database management and Docker profiles for deployment via Caddy, Tailscale, and Cloudflare Tunnels to ensure privacy and data sovereignty.

Tokens
91.4K
Snippets
167
Records
404
Agent score
95%

What's inside Self-hosted LiveSync

  1. Overview of Self-hosted LiveSync

    main

    Self-hosted LiveSync is a community-implemented synchronization plugin for Obsidian, available on all compatible platforms. It uses CouchDB or Object Storage (such as MinIO, S3, R2, etc.) as a server. It is designed for users who require fully self-hosted, private, and secure note synchronization.

    Key Features

    • Efficient Synchronization: Minimizes network traffic.
    • Conflict Management: Automatic merging for simple conflicts.
    • Open Source Backend: Supports various open-source server solutions.
    • Security: End-to-end encryption support.
    • Customization Sync: Syncs settings, snippets, themes, and plugins via Customization Sync (Beta) or Hidden Files Sync.
    • WebClip: Integration with obsidian-livesync-webclip for Chrome.

    ⚠️ Critical Warnings

    • Backup First: Always perform a vault backup before installing or updating this plugin.
    • No Co-existence: Do not enable this plugin alongside other synchronization solutions (e.g., iCloud or official Obsidian Sync).
    • Not a Backup Tool: This is a synchronization plugin, not a backup solution. Do not rely on it for data backups.
  2. Resolve configuration mismatch dialogues

    main

    LiveSync pauses synchronization if local and remote settings differ to prevent silent, unexpected changes.

    Automatic Alignment

    Settings controlling chunk creation (chunk hash algorithm, chunk size, and splitter version) are automatically aligned by current releases where possible. Using different settings won't break readability but may reduce chunk reuse.

    Manual Mismatches

    Settings involving encryption, path obfuscation, or file-name case handling always require manual decisions.

    Available Actions

    • During remote profile check:
      • Use configured settings: Accepts the shared values in the remote.
      • Dismiss: Keeps local settings unchanged.
    • Before synchronization:
      • Apply settings to this device: Accepts remote values.
      • Update remote database settings: Makes the current device's values the new shared values.
      • Dismiss: Postpones the mismatch; synchronization remains paused.
    • When reconstruction is required: The dialogue will explicitly state if a Fetch or Rebuild will follow. Ensure the authoritative copy is available before proceeding.
  3. Understand CouchDB Setup Actions

    main

    The behavior of the CouchDB setup dialogue depends on the context in which it is opened:

    • First device onboarding: Uses Create or connect to database and continue. It may create the database if the account has permissions.
    • Additional device onboarding: Uses Connect to existing database and continue. It will not create a missing database.
    • Adding/Editing a profile: Uses Test connection and save. It will not create a missing database.
    • Settings mode: Offers Save without connecting. This updates the profile but may cause sync to fail until the connection is corrected.

    Server Requirement Check: The setup includes an optional check that reads CouchDB configuration via Obsidian's internal API. This is a read-only operation that can suggest specific CouchDB setting fixes.

  4. Handling independent creation of the same path

    main

    If two devices create the same file path independently before synchronizing:

    1. Identical Bytes: LiveSync deletes one duplicate leaf. The device that previously showed the deleted version will rebind its local provenance to the surviving revision.
    2. Different Bytes: Because there is no shared revision (generation-one leaves), a three-way merge is impossible. LiveSync will leave both versions for manual user selection rather than attempting to concatenate or guess a base.
  5. Understand the Fast Fetch initialization lifecycle

    main

    Fast Fetch manages the suspension of certain LiveSync activities to ensure a clean setup.

    Suspension Policy:

    • While Fast Fetch is running, both file watching and replication result parsing are suspended.
    • On Success: Fast Fetch clears the suspension, allows the offline scan and rebuild to finish, reflects the Vault, and cleans up flag files.
    • On Failure:
      • The local checkpoint and durably fetched documents are retained for retry.
      • The local database is not marked as resolved.
      • Vault reflection remains suspended.
      • The offline scan and rebuild finalization are not run.
      • Flag files and the remembered initialization choice are preserved to allow recovery on restart.
  6. How chunk arrival waiting works

    main

    LiveSync uses a lifecycle-based waiting mechanism instead of fixed timeouts to handle cases where metadata is available but the referenced content chunks have not yet been locally written. This prevents 'Load failed' errors caused by race conditions between metadata and chunk replication.

    Instead of guessing how long a network request might take, the system waits for observable lifecycle signals from two specific producers:

    1. Finite Replication Activity: Tracked via finiteReplicationActivityCount. This represents replication operations (like initial catch-up or pull-only catch-up) that are actively placing documents in the local database. Once a finite replication completes, it is considered the authoritative 'latest' boundary.
    2. On-demand Chunk Fetching: Tracked via a ChunkDeliveryCoordinator claim. This represents the complete lifetime of an accepted request for a specific missing chunk identifier (including queueing, throttling, fetching, and local persistence).

    The Waiting Process:

    • The system registers a waiter and dispatches missingChunks.
    • If a ChunkDeliveryCoordinator claim or finite replication is active, the waiter waits for that specific producer to finish.
    • Once the producer completes, the system performs a final local database recheck (bypassing the cache) to see if the chunk arrived.
    • If the chunk is found, it is returned; otherwise, it returns unavailable immediately without an additional grace period.
  7. How the mirror command works

    main

    The mirror command synchronizes your storage (the vault) with your local database. It is the process that runs upon startup in the Obsidian plugin.

    Workflow:

    1. Precondition checks: Aborts if isConfigured is false, file watching is suspended, or remediation mode is active.
    2. State restoration: Restores the previous storage state from the last successful run.
    3. Expired deletion cleanup: Permanently removes documents marked deleted if their mtime exceeds the retention period set in automaticallyDeleteMetadataOfDeletedFiles.
    4. File collection: Gathers files from both the Storage (vault) and the Local database.
    5. Categorisation and synchronisation:
      • UPDATE DATABASE: If file exists in storage only $\rightarrow$ Store in database.
      • UPDATE STORAGE: If file exists in database only (and is not deleted/conflicted) $\rightarrow$ Restore to storage.
      • SYNC DATABASE AND STORAGE: If file exists in both $\rightarrow$ Compare mtime. Newer file wins.
    6. Initialisation: Writes initialized = true to the database on the first successful run.

    Warning: mirror does not respect file deletions. If you delete a file in your vault, mirror will restore it. To delete a file, you must use the rm command.

  8. Understand the P2P Room and Transport Lifecycle

    main

    In Self-hosted LiveSync, P2P connectivity is managed through a layered ownership model involving LiveSync, Trystero, and Nostr relays. It is critical to distinguish between 'leaving a room' and 'destroying a physical transport'.

    Key Abstractions

    • LiveSync P2P Service: Owns commands, RPC sessions, advertisements, and room membership.
    • Trystero Room: A logical grouping. Calling room.leave() removes room actions and callbacks and detaches the room's shared-peer binding, but it does not necessarily destroy the underlying WebRTC peer. Trystero may retain a healthy physical peer for reuse in other rooms.
    • Trystero WebRTC Peers: Owned by Trystero. You should not manually call .close() on RTCPeerConnection objects returned by room.getPeers(), as this bypasses Trystero's shared-peer manager and can prevent new replicators from discovering the same remote peer.
    • Nostr Relay WebSockets: These have a separate lifecycle. Disconnecting from LiveSync involves pausing relay reconnection and closing these sockets explicitly.

    Disconnection Contract

    When an explicit disconnect operation is performed, the following state transitions occur:

    ResourceState after operation
    LiveSync P2P service & RPC roomClosed immediately
    Trystero room membershipLeft; room actions and advertisements are no longer available
    Nostr relay WebSocketsClosed, with automatic reconnection paused
    Underlying WebRTC peerMay remain idle under Trystero ownership for reuse (but cannot carry the departed room's traffic)
  9. Understand the performance trade-offs of Data Compression

    main

    While Data Compression reduces storage and transfer volume, it introduces significant processing costs.

    Performance Impact

    • CPU & Memory: Compression is executed asynchronously (using Web Workers in browsers or worker-threads in CLI) to avoid blocking the Obsidian UI thread. However, because a new worker is currently created for each attempted chunk, it can consume significant CPU and memory during bulk operations.
    • Wall-time: Enabling compression increases upload wall-time and CPU-time significantly compared to uncompressed uploads.
    • Break-even point: For E2EE users, the estimated wall-time break-even point for the benefits of compression is approximately 0.27 Mbit/s.

    Compression effectiveness by file type

    Compression effectiveness depends on the content, not the file extension. Based on benchmarks:

    • High reduction: JSON (~72.80%) and TypeScript (~74.11%).
    • Moderate reduction: Markdown (~16.30%), PNG (~6.16%), and JPEG (~4.72%).
    • No reduction: gzip files and high-entropy binary files (as they are already compressed or incompressible).
  10. Understand the Commonlib package structure and entry points

    main

    The Commonlib (Common Library) is distributed as a single compiled ESM package designed with explicit host boundaries. Instead of importing from a monolithic root or using source aliases (like @lib/*), consumers should use specific entry points tailored to their runtime environment. This prevents accidental leaks of Node-only modules into browser environments or vice versa.

    Available entry points include:

    • context: Core logic and context results.
    • settings: Configuration and settings management.
    • remote-configurations: Handling of remote settings.
    • browser: Browser-specific implementations and services.
    • node: Node.js-specific implementations.
    • rpc: Remote Procedure Call interfaces.
    • root: The package entry point.

    When building for specific platforms, use these subpaths to ensure that only the necessary code is bundled and that environment-specific dependencies (like Node built-ins) are correctly handled.

  11. Understand the Fast Setup execution sequence

    main

    Once you confirm your configuration settings, the plugin executes the following sequence automatically:

    1. Fast download: Executes a high-speed download of the remote database via fetchLocalDBFast.
    2. File synchronization: Automatically runs a full scan in the foreground via synchroniseAllFilesBetweenDBandStorage to reflect changes in local storage.
    3. Completion: The plugin returns to its normal operational state once the process is finished.
  12. Feature maturity and support policy for Self-hosted LiveSync 1.0

    main

    The 1.0 release categorizes features into different maturity levels, which dictates how they are maintained and whether they are enabled by default:

    • Supported, opt-in: Features like Peer-to-Peer Synchronisation, Hidden File Sync, and Customisation Sync. These are maintained and tested but require manual activation.
    • Maintained, advanced: Data Compression is available as a trade-off between storage/bandwidth and CPU/memory usage. It is disabled by default.
    • Beta or experimental: Features like JWT authentication, ignore files, automatic newer-file conflict resolution, and Garbage Collection V3 for CouchDB are intended for specialized testing and are disabled by default.
    • Compatibility only: Legacy features (e.g., V1 dynamic iteration counts, old IndexedDB adapter, non-current hash algorithms) are kept to ensure existing data remains readable, but new Vaults use current defaults.