Wires the encrypted sync transport into a working replication between one
identity's own devices, reusing the CRDT-ready model (HLC + tombstones +
LWW) the data model was built for.
- SocialSession gains a 5th interface, sync, over the shared connection,
built with a per-install deviceId + the app's inventory namespace.
- DeviceIdStore: a stable random per-install id in the keystore — distinct
from the seed-derived CRDT node id, which is IDENTICAL across a user's
devices (so it couldn't tell them apart / they'd clobber each other's
replaceable record).
- InventorySnapshotIO (implemented by ExportImportService): build/apply the
inventory as the SAME interchange JSON as backups but UNSEALED — the sync
transport does the encryption, so it must not double-seal. Apply = the
existing idempotent LWW importInventory.
- SyncService: on (re)connect publishes this device's snapshot + subscribes
to the others'; a local edit (debounced, off tableUpdates) republishes;
incoming snapshots merge idempotently; skips its own echo; a byte check
skips re-publishing an unchanged snapshot so the ping-pong converges.
Foreground only, like the inbox.
- DI/Bootstrap/switchSocialAccount wire and lifecycle it per identity.
Tests: DeviceIdStore, ExportImportService snapshot round-trip (unsealed +
idempotent), SyncService.handleRemote (applies others', skips own).
Follow-ups: deltas vs full-snapshot pushes; background sync; conflict UI.