diff --git a/docs/rfcs/2025-10-20-chat-relays.md b/docs/rfcs/2025-10-20-chat-relays.md index b19d155dd1..ae0eb24fcc 100644 --- a/docs/rfcs/2025-10-20-chat-relays.md +++ b/docs/rfcs/2025-10-20-chat-relays.md @@ -70,7 +70,7 @@ Notes: - Owner should reject contact requests to their group link. -- Chat relay should reject contact requests to its relay link until chat relay confirms it is attached to the group link data. Can be based on `RelayStatusInGroup`, see below. +- Chat relay should reject contact requests to its relay link until chat relay confirms it is attached to the group link data. Can be based on `GroupRelayOwnStatus`, see below. - Owner public key for signing group actions and messages can be stored as part of group short link data (`GroupShortLinkData`). @@ -87,13 +87,13 @@ Notes: This action is local but user action should be remembered - at this point `group_relays` records should be created and associated with group, in status `CRSNew`. Further recovery can be done per relay record based on status. - Step 5. Contact request to relay. Contact request should be done via asynchronous agent action. New connection (for contact request) should be associated with relay, relay status moves to `CRSInvited`. - - New type connection entity via new `connections.group_relay_id` field? - - Member connection, and link `group_members` to `group_relays`? This option seems simpler. + - Member connection, link `group_members` to `group_relays`. Recovery from `CRSInvited` status is not needed, at this point owner waits for relay response. - Step 7. Receive `x.grp.relay.acpt` to relay connection. Save relay link on relay record, relay status moves to `CRSAccepted`. - Step 8. Collect all relay links for relays with status `CRSAccepted` and greater (further down protocol), update short link data. Update of link data should be done via asynchronous agent action. Relay status moves to `CRSAdded`. - Step 9. Upon receiving response for link data update, send `x.grp.info` to relay, relay status moves to `CRSNotified`. - Response is received asynchronously in receive loop - need to introduce correlation between link and events. + - Alternatively could use synchronous agent api with retry. - `x.grp.info` doesn't have to contain actual group profile update in this case, here it's only a trigger for relay to check link data. (Relay should know based on its state to not dismiss even if profile doesn't change). Could be a special protocol event, but not necessary. - Step 12. Receive confirmation from relay, relay status moves to `CRSConfirmed`. At this point owner knows relay is functional for the group and can advertise link with it, or wait for remaining relays. - Relay status updates can be displayed to owner in UI via events. @@ -105,11 +105,12 @@ Notes: - For relay: - - Relay tracks its own status in group (`RelayStatusInGroup`). + - Relay tracks its own status in group (`GroupRelayOwnStatus`). - Step 6. Upon receiving invitation from group owner, initial relay status is `RSGInvited`. Relay should create its link for the group, should be done via asynchronous agent action, status moves to `RSGLinkCreated`. - Continuation is to save and associate relay link with group record (`groups.relay_link`), send `x.grp.relay.acpt`, + Continuation is to save and associate relay link with group record, send `x.grp.relay.acpt`, status moves to `RSGAccepted`. + - Relay link connection is a user contact link, more specifically group link - we can re-use group link machinery that links `user_contact_links` to `groups`. - Step 10, 11. Upon receiving `x.grp.info` from owner, if relay is in `RSGAccepted` status, status moves to `RSGNotified`. Retrieve short link (asynchronous agent action? synchronous with retry?). If relay link is present send confirmation to owner `x.grp.relay.ready`, status moves to `RSGConfirmed`. Otherwise break (recovery for owner is to re-add relay). - Should recover in `RSGInvited`, `RSGLinkCreated`, `RSGNotified` statuses. diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20251016_chat_relays.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20251016_chat_relays.hs index cb81f2ef5a..2cc8ce5e88 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/M20251016_chat_relays.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20251016_chat_relays.hs @@ -5,6 +5,17 @@ module Simplex.Chat.Store.SQLite.Migrations.M20251016_chat_relays where import Database.SQLite.Simple (Query) import Database.SQLite.Simple.QQ (sql) +-- - chat_relays - user's list of chat relays to choose from (similar to protocol_servers) +-- - users.is_user_chat_relay - indicates that the user can serve as a chat relay +-- (TBC usage, e.g. agree to invitations to be relay) +-- - group_relays - group owner's list of relays for a group +-- - group_relays.relay_link - links for all relays of a group are included in GroupShortLinkData +-- - group_relays.relay_status - group owner's status for each relay (GroupRelayStatus) +-- - group_members.is_chat_relay - indicates that the member is a chat relay (to all group members) +-- - group_members.group_relay_id - associates group_members record with a group_relays record for a group owner; +-- receiving event to member connection, owner can match it to the relay +-- - TBC also inverse link from group_relays to group_members? (group_relays.group_member_id) +-- - groups.relay_own_status - indicates for a relay client that it is chat relay for the group (GroupRelayOwnStatus) m20251016_chat_relays :: Query m20251016_chat_relays = [sql| @@ -22,39 +33,41 @@ CREATE TABLE chat_relays( UNIQUE(user_id, address), UNIQUE(user_id, name) ); - CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id); ALTER TABLE users ADD COLUMN is_user_chat_relay INTEGER NOT NULL DEFAULT 0; -ALTER TABLE group_members ADD COLUMN is_chat_relay INTEGER NOT NULL DEFAULT 0; - --- Relay's link for group -ALTER TABLE groups ADD COLUMN relay_link BLOB; - --- Owner's list of relays for group --- TBC relay_status: invited/accepted/added/notified/confirmed --- relay_link: links for all relays are included in GroupShortLinkData CREATE TABLE group_relays( group_relay_id INTEGER PRIMARY KEY, group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE, relay_status TEXT NOT NULL, relay_link BLOB ); +CREATE INDEX idx_group_relays_group_id ON group_relays(group_id); + +ALTER TABLE group_members ADD COLUMN is_chat_relay INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE group_members ADD COLUMN group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL; +CREATE INDEX idx_group_members_group_relay_id ON group_members(group_relay_id); + +ALTER TABLE groups ADD COLUMN relay_own_status TEXT; |] down_m20251016_chat_relays :: Query down_m20251016_chat_relays = [sql| -DROP TABLE group_relays; - -ALTER TABLE groups DROP COLUMN relay_link; - -ALTER TABLE group_members DROP COLUMN is_chat_relay; +DROP INDEX idx_chat_relays_user_id; +DROP TABLE chat_relays; ALTER TABLE users DROP COLUMN is_user_chat_relay; -DROP INDEX idx_chat_relays_user_id; +DROP INDEX idx_group_relays_group_id; +DROP TABLE group_relays; -DROP TABLE chat_relays; +ALTER TABLE group_members DROP COLUMN is_chat_relay; + +DROP INDEX idx_group_members_group_relay_id; +ALTER TABLE group_members DROP COLUMN group_relay_id; + +ALTER TABLE groups DROP COLUMN relay_own_status; |] diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index b75a8ae17b..10ab4da63f 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -958,7 +958,7 @@ data GroupMember = GroupMember updatedAt :: UTCTime, supportChat :: Maybe GroupSupportChat, isChatRelay :: BoolDef, - relayStatus :: Maybe RelayStatusInGroup + relayStatus :: Maybe GroupRelayOwnStatus } deriving (Eq, Show) @@ -967,17 +967,17 @@ data GroupMember = GroupMember -- TODO - GroupMember? -- TODO - separate list of relays in GroupInfo? -- TODO - only on request? -data ChatRelayStatus - = CRSNew - | CRSInvited - | CRSAccepted - | CRSAdded - | CRSNotified - | CRSConfirmed +data GroupRelayStatus + = GRSNew + | GRSInvited + | GRSAccepted + | GRSAdded + | GRSNotified + | GRSConfirmed deriving (Eq, Show) --- Own status tracked by relay in group -data RelayStatusInGroup +-- Own status tracked by relay in a group +data GroupRelayOwnStatus = RSGInvited | RSGLinkCreated | RSGAccepted