Skip to content

VaultSync 2.1.0

Latest

Choose a tag to compare

@psimaker psimaker released this 30 Sep 11:09
· 27 commits to main since this release

[2.1.0] — 2026-09-30

Added

  • VaultSync Hub: a self-hosted stack you install with one link and pair with a short code — curl -fsSL https://vaultsync.eu/setup.sh | sh asks 1) Obsidian device / 2) Hub. The Hub path starts the Hub's own Syncthing, the new vaultsync-hub coordinator and the notify wake-up helper as a Docker Compose stack under /srv/vaultsync (plain bind mounts: vaults/, syncthing/, hub/), applies Hub defaults (unlimited conflict copies, staggered versions kept for 30 days, no usage reporting) and prints a pairing code such as TULIP-ANCHOR-42. The device path finds the Hub on the local network, turns the code into a session key with SPAKE2 (RFC 9382), lets the Hub create or share a vault and accepts it into an empty directory. Guards match the app's doctrine: vaults live only under one root, never overlap, are never created over existing content without an explicit vault adopt, and the Hub never changes a folder's path or deletes anything. Codes expire after 24 hours and lock after five wrong attempts; the pairing port answers only private, loopback and link-local source addresses and must not be forwarded. The iPhone app pairs with a Hub by Device ID for now; pairing by code or QR from the app is a follow-up. Documented in docs/hub.md; decisions 035–037. Shipped as hub-v0.1.0.

Changed

  • The withdrawn 2.0.2 "containment" candidate is not the release line (#150, #167, #168, #169) — the branch that froze receive-capable vaults and disabled vault creation to avoid Syncthing's conflict-copy limit is not merged. Conflict retention on devices follows upstream Syncthing; the Hub keeps every conflict copy and 30 days of versions, which is where lost edits are recovered from. Recorded in decision 035.

Fixed

  • Vaults with a long deletion history on the server no longer drop the connection over and over at index exchange (#185, reported in #135) — the app now keeps exactly one connection to each device, and closes a second one that two devices dialling each other at the same moment can open. Syncthing 2 opens several and can end up sending the folder index over a connection the other side is not listening on, which closed the connection right after connect; a heavy index made that window wide enough to never finish. Existing devices are updated automatically on the next start. Recorded in decision 042.
  • A share is no longer accepted when its offer cannot be read, and sync filters no longer show as empty when the filter file cannot be read (#182) — accepting a share used to go ahead on a failed read of the pending offers and create a local vault that no device shares, looking exactly like a successful accept; the accept is now refused with the error. An unreadable .stignore used to display as “no filters”; the Sync Filters screen now says the filters are unavailable and hides the toggles, so nothing rewrites the file from that false picture. Recorded in decision 039.
  • The app now notices when the sync engine stops on its own, instead of showing “Ready” over a dead engine (#181) — the engine’s “running” state follows its supervisor’s real state, so the one automatic restart (decision 009) actually runs and a second stop shows why the engine exited. Every wait in starting and stopping the engine has a deadline and reports a timeout as an error rather than freezing every later call; a stop that overruns its deadline keeps a second engine from starting over the same data until it has finished. Recorded in decision 038.
  • Foreground and background no longer fight over the sync engine (#183) — a background wake-up that is stopping the engine now claims that stop first, so opening the app in that moment cold-starts a fresh engine instead of attaching to one about to stop; after the app goes to the background, a stop by a background wake-up no longer triggers a foreground restart; and when two background wake-ups overlap, the second reports what the first actually achieved instead of a stand-in “already idle”. Recorded in decision 040.
  • Very large conflicting notes no longer freeze the app when opened for comparison (#184) — a note above 1 MB is not loaded for comparison; the conflict screen says so, names both sizes and keeps the Keep This / Keep Both / Keep Other choices, so you compare the versions in Obsidian and decide here. Skipping a file with its conflict copies and the foreground rescans no longer run on the main thread. Recorded in decision 041.

Full changelog: https://github.com/psimaker/vaultsync/blob/main/CHANGELOG.md