Repository navigation
[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 | shasks1) Obsidian device / 2) Hub. The Hub path starts the Hub's own Syncthing, the newvaultsync-hubcoordinator and thenotifywake-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 asTULIP-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 explicitvault 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 indocs/hub.md; decisions 035–037. Shipped ashub-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
.stignoreused 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