From 8d21860d982f5169cd72ad7db2a6c733c1c03fe2 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 27 May 2026 17:43:58 +0400 Subject: [PATCH] wip --- plans/2026-05-26-public-groups-via-relays-unified.md | 2 +- src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/plans/2026-05-26-public-groups-via-relays-unified.md b/plans/2026-05-26-public-groups-via-relays-unified.md index c3d61b2dde..91f7c3a6ce 100644 --- a/plans/2026-05-26-public-groups-via-relays-unified.md +++ b/plans/2026-05-26-public-groups-via-relays-unified.md @@ -64,7 +64,7 @@ There is **no in-place key rotation**: a genuine re-key is modeled as a *new mem **Anti-replay / rollback.** The relay cannot forge a signed roster but can replay an older one. -- The current roster `version` is anchored in the owner-controlled link mutable data (which already holds `OwnerAuth`, profile, subscriber count). The relay cannot forge it. A roster change that bumps the version also updates link data. +- The current roster `version` is anchored in the owner-controlled link mutable data (which already holds `OwnerAuth`, profile, subscriber count). The relay cannot forge it. A roster change that bumps the version also updates link data. **Status:** the write side is implemented; the join-time **read/detect** is deferred — comparing the anchor against the relay-served roster at join is racy (the forwarded roster may not have arrived yet → false positives), so correct staleness detection must be triggered by roster arrival, not at join. The residual new-joiner rollback gap below stands; the hard anti-replay (member + relay) is in place. - **Existing members** reject any roster with `version` below the highest already accepted — full anti-replay for them. - **New joiners** process the latest version the relay actually serves, even if it lags the link anchor, so honest relay propagation lag never blocks a join. The anchor is used for staleness *detection*, not a hard gate, in v1. - **Documented residual gap:** a stale/malicious relay can serve an old-but-valid roster to a brand-new joiner. Documented in `channels-overview.md` with future mitigations: escalate verification to an owner, or have the client compare the relay's version to the link anchor and refuse/retry above a staleness threshold. diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql index c710b2c5cb..fd7855579e 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql @@ -178,7 +178,9 @@ CREATE TABLE groups( relay_request_retries INTEGER NOT NULL DEFAULT 0, relay_request_delay INTEGER NOT NULL DEFAULT 0, relay_request_execute_at TEXT NOT NULL DEFAULT '1970-01-01 00:00:00', - relay_inactive_at TEXT, -- received + relay_inactive_at TEXT, + roster_version INTEGER, + roster_msg BLOB, -- received FOREIGN KEY(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name) ON DELETE CASCADE @@ -223,6 +225,7 @@ CREATE TABLE group_members( relay_link BLOB, member_pub_key BLOB, removed_at TEXT, + delivered_roster_version INTEGER, FOREIGN KEY(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name) ON DELETE CASCADE