From 8d45c2cca9df0d0ec8843e9aeb9a9623f07990aa Mon Sep 17 00:00:00 2001 From: shum Date: Mon, 4 May 2026 12:27:49 +0000 Subject: [PATCH] cli: add --relay-address-server option for chat relay New CLI flag --relay-address-server SERVER selects the SMP server used for the chat relay address link created at startup. Only valid together with --relay; errors out otherwise. Threads Maybe SMPServerWithAuth through APICreateMyAddress to the new agent createConnection parameter. --- src/Simplex/Chat/Controller.hs | 4 ++-- src/Simplex/Chat/Core.hs | 10 +++++----- src/Simplex/Chat/Library/Commands.hs | 18 +++++++++--------- src/Simplex/Chat/Mobile.hs | 1 + src/Simplex/Chat/Options.hs | 12 ++++++++++++ 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index cfb60c360a..2873a131a4 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -87,7 +87,7 @@ import Simplex.Messaging.Crypto.Ratchet (PQEncryption) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON) -import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), QueueId, SMPMsgMeta (..), SubscriptionMode (..), XFTPServer) +import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), QueueId, SMPMsgMeta (..), SMPServerWithAuth, SubscriptionMode (..), XFTPServer) import Simplex.Messaging.TMap (TMap) import Simplex.Messaging.Transport (TLS, TransportPeer (..), simplexMQVersion) import Simplex.Messaging.Transport.Client (SocksProxyWithAuth, TransportHost) @@ -488,7 +488,7 @@ data ChatCommand | ClearContact ContactName | APIListContacts {userId :: UserId} | ListContacts - | APICreateMyAddress {userId :: UserId} + | APICreateMyAddress {userId :: UserId, srv_ :: Maybe SMPServerWithAuth} | CreateMyAddress | APIDeleteMyAddress {userId :: UserId} | DeleteMyAddress diff --git a/src/Simplex/Chat/Core.hs b/src/Simplex/Chat/Core.hs index 3c1ce9bc26..6b48e824aa 100644 --- a/src/Simplex/Chat/Core.hs +++ b/src/Simplex/Chat/Core.hs @@ -69,11 +69,11 @@ simplexChatCore cfg@ChatConfig {confirmMigrations, testView, chatHooks} opts@Cha exitFailure runSimplexChat :: ChatConfig -> ChatOpts -> User -> ChatController -> (User -> ChatController -> IO ()) -> IO () -runSimplexChat ChatConfig {testView} ChatOpts {coreOptions = CoreChatOpts {chatRelay, maintenance}} u cc@ChatController {config = ChatConfig {chatHooks}} chat +runSimplexChat ChatConfig {testView} ChatOpts {coreOptions = CoreChatOpts {chatRelay, chatRelayServer, maintenance}} u cc@ChatController {config = ChatConfig {chatHooks}} chat | maintenance = wait =<< async (chat u cc) | otherwise = do a1 <- runReaderT (startChatController True True) cc - when (chatRelay && not testView) $ askCreateRelayAddress cc u + when (chatRelay && not testView) $ askCreateRelayAddress cc u chatRelayServer forM_ (postStartHook chatHooks) ($ cc) a2 <- async $ chat u cc waitEither_ a1 a2 @@ -144,8 +144,8 @@ createActiveUser cc CoreChatOpts {chatRelay} = \case Right (CRActiveUser user) -> pure user r -> printResponseEvent (Nothing, Nothing) (config cc) r >> onError -askCreateRelayAddress :: ChatController -> User -> IO () -askCreateRelayAddress cc@ChatController {chatStore} user = +askCreateRelayAddress :: ChatController -> User -> Maybe SMPServerWithAuth -> IO () +askCreateRelayAddress cc@ChatController {chatStore} user@User {userId} srv_ = withTransaction chatStore (\db -> runExceptT $ getUserAddress db user) >>= \case Right _ -> pure () Left SEUserContactLinkNotFound -> promptCreate @@ -155,7 +155,7 @@ askCreateRelayAddress cc@ChatController {chatStore} user = promptCreate = do ok <- onOffPrompt "Create relay address" True when ok $ - execChatCommand' CreateMyAddress 0 `runReaderT` cc >>= \case + execChatCommand' (APICreateMyAddress userId srv_) 0 `runReaderT` cc >>= \case Right (CRUserContactLinkCreated _ address) -> do putStrLn "Chat relay address is created:" putStrLn $ addressStr address diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 31e6533ad3..98ebbb3960 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -1929,7 +1929,7 @@ processChatCommand vr nm = \case let userData = contactShortLinkData (userProfileDirect user incognitoProfile Nothing True) Nothing userLinkData = UserInvLinkData userData -- TODO [certs rcv] - (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation (Just userLinkData) Nothing IKPQOn subMode + (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation (Just userLinkData) Nothing Nothing IKPQOn subMode ccLink' <- shortenCreatedLink ccLink -- TODO PQ pass minVersion from the current range conn <- withFastStore' $ \db -> createDirectConnection db user connId ccLink' Nothing ConnNew incognitoProfile subMode initialChatVersion PQSupportOn @@ -1971,7 +1971,7 @@ processChatCommand vr nm = \case | short = Just $ UserInvLinkData $ contactShortLinkData (userProfileDirect newUser Nothing Nothing True) Nothing | otherwise = Nothing -- TODO [certs rcv] - (agConnId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId newUser) True False SCMInvitation userLinkData_ Nothing IKPQOn subMode + (agConnId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId newUser) True False SCMInvitation userLinkData_ Nothing Nothing IKPQOn subMode ccLink' <- shortenCreatedLink ccLink conn' <- withFastStore' $ \db -> do deleteConnectionRecord db user connId @@ -2253,7 +2253,7 @@ processChatCommand vr nm = \case CRContactsList user <$> withFastStore' (\db -> getUserContacts db vr user) ListContacts -> withUser $ \User {userId} -> processChatCommand vr nm $ APIListContacts userId - APICreateMyAddress userId -> withUserId userId $ \user@User {userChatRelay} -> do + APICreateMyAddress userId srv_ -> withUserId userId $ \user@User {userChatRelay} -> do withFastStore' (\db -> runExceptT $ getUserAddress db user) >>= \case Left SEUserContactLinkNotFound -> pure () Left e -> throwError $ ChatErrorStore e @@ -2265,13 +2265,13 @@ processChatCommand vr nm = \case | otherwise = contactShortLinkData (userProfileDirect user Nothing Nothing True) Nothing userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData} -- TODO [certs rcv] - (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) Nothing IKPQOn subMode + (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) Nothing srv_ IKPQOn subMode ccLink' <- shortenCreatedLink ccLink let ccLink'' = if isTrue userChatRelay then setShortLinkType CCTRelay ccLink' else ccLink' withFastStore $ \db -> createUserContactLink db user connId ccLink'' subMode pure $ CRUserContactLinkCreated user ccLink'' CreateMyAddress -> withUser $ \User {userId} -> - processChatCommand vr nm $ APICreateMyAddress userId + processChatCommand vr nm $ APICreateMyAddress userId Nothing APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do conn <- withFastStore $ \db -> getUserAddressConnection db vr user withChatLock "deleteMyAddress" $ do @@ -2565,7 +2565,7 @@ processChatCommand vr nm = \case gVar <- asks random subMode <- chatReadVar subscriptionMode -- TODO [certs rcv] - (agentConnId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff subMode + (agentConnId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing Nothing IKPQOff subMode member <- withFastStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode sendInvitation member cReq pure $ CRSentGroupInvitation user gInfo contact member @@ -3006,7 +3006,7 @@ processChatCommand vr nm = \case userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData} crClientData = encodeJSON $ CRDataGroup groupLinkId -- TODO [certs rcv] - (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) (Just crClientData) IKPQOff subMode + (connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) (Just crClientData) Nothing IKPQOff subMode ccLink' <- setShortLinkType CCTGroup <$> shortenCreatedLink ccLink gVar <- asks random gLink <- withFastStore $ \db -> createGroupLink db gVar user gInfo connId ccLink' groupLinkId mRole subMode @@ -3047,7 +3047,7 @@ processChatCommand vr nm = \case subMode <- chatReadVar subscriptionMode -- TODO PQ should negotitate contact connection with PQSupportOn? -- TODO [certs rcv] - (connId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff subMode + (connId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing Nothing IKPQOff subMode -- [incognito] reuse membership incognito profile ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing (Just epochStart) @@ -5113,7 +5113,7 @@ chatCommandP = ("/fstatus " <|> "/fs ") *> (FileStatus <$> A.decimal), "/_connect contact " *> (APIConnectContactViaAddress <$> A.decimal <*> incognitoOnOffP <* A.space <*> A.decimal), "/simplex" *> (ConnectSimplex <$> incognitoP), - "/_address " *> (APICreateMyAddress <$> A.decimal), + "/_address " *> (APICreateMyAddress <$> A.decimal <*> optional (A.space *> strP)), ("/address" <|> "/ad") $> CreateMyAddress, "/_delete_address " *> (APIDeleteMyAddress <$> A.decimal), ("/delete_address" <|> "/da") $> DeleteMyAddress, diff --git a/src/Simplex/Chat/Mobile.hs b/src/Simplex/Chat/Mobile.hs index 281fc6b03b..f061061327 100644 --- a/src/Simplex/Chat/Mobile.hs +++ b/src/Simplex/Chat/Mobile.hs @@ -254,6 +254,7 @@ mobileChatOpts dbOptions = tbqSize = 4096, deviceName = Nothing, chatRelay = False, + chatRelayServer = Nothing, highlyAvailable = False, yesToUpMigrations = False, migrationBackupPath = Just "", diff --git a/src/Simplex/Chat/Options.hs b/src/Simplex/Chat/Options.hs index dde223f6b9..7f7d7edb22 100644 --- a/src/Simplex/Chat/Options.hs +++ b/src/Simplex/Chat/Options.hs @@ -66,6 +66,7 @@ data CoreChatOpts = CoreChatOpts tbqSize :: Natural, deviceName :: Maybe Text, chatRelay :: Bool, + chatRelayServer :: Maybe SMPServerWithAuth, highlyAvailable :: Bool, yesToUpMigrations :: Bool, migrationBackupPath :: Maybe FilePath, @@ -239,6 +240,14 @@ coreChatOptsP appDir defaultDbName = do ( long "relay" <> help "Run as a chat relay client" ) + chatRelayServer <- + optional $ + option + strParse + ( long "relay-address-server" + <> metavar "SERVER" + <> help "SMP server to use for chat relay address link (requires --relay)" + ) highlyAvailable <- switch ( long "ha" @@ -282,6 +291,9 @@ coreChatOptsP appDir defaultDbName = do tbqSize, deviceName, chatRelay, + chatRelayServer = case chatRelayServer of + Just _ | not chatRelay -> error "--relay-address-server option requires --relay option" + _ -> chatRelayServer, highlyAvailable, yesToUpMigrations, migrationBackupPath,