Repository navigation
Multi-device sync: folder, WebDAV and S3 locations, three-way merge, conflicts that travel with versions - #350
Merged
Merged
Conversation
…conflicts that travel with versions - tw-config: a local-only `sync` section (device, encryption, passphrase, excluded upstreams, target) with validation; http:// only for this machine and the local network. Documented in the configuration manual. - tw-sync (new): a Store trait (list, get, put_new, put, delete, create_exclusive) with folder, WebDAV and S3 implementations (S3 addressing chosen from the endpoint host); an age X25519 keyring sealed with the passphrase; version naming and retention that keeps every head and its parents; device records; the unit-wise three-way merge with table tests; order merging; conflict records; the sync round (heads merged on their common parent, base from the local base, local backups or the location), joining, encryption on/off and passphrase change, stop and purge. - tw-control: the sync service (10-second settle, upload before exit, 30-minute polls for WebDAV and S3, a folder watcher, checks at start, after wake and with retries; paused in safe mode; unreachable only after two hours); the /sync endpoints and Event::SyncStatus; local writes through the same order as import and restore, with the source device kept on the local backup; RemoteBackups registered while sync is on; pinned keys from the configuration manager; joining with the import preview and apply of tw-pack. - tw-pack: a backup version can record the device it was synced from. - tw-bedrock: sign_service, additive SigV4 for other services (S3). - tw-secret: the sync passphrase is masked in diagnostics. - twcore: sync status, now, enable and disable; the service started and flushed with serve. - CONTROL_API_VERSION 50; message codes sync.*. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Multi-device configuration sync (the sync half of sync and backup).
What it does
Several devices (desktop cores and remote cores) keep one configuration in step through a sync location the user picks: a folder carried by a cloud drive, a WebDAV server, or S3-compatible storage.
<seq>-<device>.twconfigand never overwritten; only the keyring is created exclusively (If-None-Match: */ hard link), and device records are written by their own device. Folder and WebDAV writes go to a dot-named temporary file first, then rename/MOVE. Files that cannot be read yet (cloud placeholders, partial uploads, wrong key) count as not arrived. Retention keeps every head and their parents, so parallel or late versions survive until merged.tw-sync::merge), ontw_pack::model::Snapshot: upstreams merge by conn/models/billing/other, plugins by script/settings/enabled, global settings and client prefs by field, everything else whole. Table-driven tests cover every row of the plan's merge table plus no-base, held items and held units. Orders (upstreams, routes, plugins) merge content and relative order separately, so two devices adding different items is not a conflict.sync/base.twconfig), then local backups, then the location; without a base it compares two ways and keeps one-sided objects.name (device)), with deletions, a gateway key in use here (key_in_use) and merged results that fail validation (invalid) handled as conflicts.keyring; versions and device records are encrypted to it. Enable / disable / change passphrase; other devices golockeduntil the new passphrase is entered, and follow when encryption is turned off. Unencrypted versions carry a SHA-256 of their content; plugins that arrive or change through unencrypted sync stay disabled on the receiving device, and address changes are flagged on the upstream.tw-control::sync): local changes settle for 10 s and go up as one version; upload before exit; WebDAV and S3 every 30 minutes, folders through a watcher; check at start, after wake and with 1/2/5/10-minute retries after failures. Safe mode pauses sync.unreachableonly after 2 hours of failures.Contract
CONTROL_API_VERSION50. New endpoints under/sync(status, test, enable/preview, enable, settings, now, conflicts, encryption, unlock, disable),Event::SyncStatus { id, view, at_ms },ProviderView.sync_excludedandProviderView.sync_url_changed,ConfigOrigin::Sync. A local-onlysyncsection in config.yaml with validation (http://only for this machine and the local network), documented in the configuration manual. New message codes aresync.*. CLI:twcore sync status | now | enable | disable [--purge].Other changes
tw-bedrock:sign_service, an additive SigV4 entry point for other services (S3 single-encodes and does not normalize the path); tested against an independent implementation.tw-secret:passphraseis masked in diagnostics like the other secret fields.Tests
Store conformance against a folder and in-process fake WebDAV and S3 servers (including servers without MOVE or conditional writes); two devices alternating edits, simultaneous uploads merged on their common parent, a late arrival, a conflict handled once on both devices, keep both, delete against edit, coming back after the base was deleted, encryption on/off and passphrase change, one keyring only, a newer core pausing, excluded upstreams, a key in use, a purged location, and the same flow over WebDAV and S3. Control-plane tests drive two full control planes through the endpoints, including the 10-second settle and the folder watcher.
Integration with the pack and backup half: local writes go through the same order as import and restore (plugin files, client preferences, then the configuration, under the plugin edit lock); the local backup of an applied version records the device it came from (
tw_pack::backup::note_device, shown asBackupEntry.device);RemoteBackupsis registered while sync is on (sync-<content_id>for versions only at the location); pinned keys come fromConfigManager::pinned_keys(); the merge base can come from a local backup with the same content id; joining with a configuration uses the import preview and apply oftw-pack, except that plugins keep the sync data's on/off state.🤖 Generated with Claude Code