@@ -354,9 +354,9 @@ setConnShortLinkAsync :: AgentClient -> ACorrId -> ConnId -> UserConnLinkData 'C
354354setConnShortLinkAsync c = withAgentEnv c .:: setConnShortLinkAsync' c
355355{-# INLINE setConnShortLinkAsync #-}
356356
357- -- | Get and verify data from short link (LGET/LKEY command) asynchronously, synchronous response is new connection id
358- getConnShortLinkAsync :: AgentClient -> UserId -> ACorrId -> ConnShortLink 'CMContact -> AE ConnId
359- getConnShortLinkAsync c = withAgentEnv c .:. getConnShortLinkAsync' c
357+ -- | Get and verify data from short link (LGET/LKEY command) asynchronously, synchronous response is new/passed connection id
358+ getConnShortLinkAsync :: AgentClient -> UserId -> ACorrId -> Maybe ConnId -> ConnShortLink 'CMContact -> AE ConnId
359+ getConnShortLinkAsync c = withAgentEnv c .:: getConnShortLinkAsync' c
360360{-# INLINE getConnShortLinkAsync #-}
361361
362362-- | Join SMP agent connection (JOIN command) asynchronously, synchronous response is new connection id.
@@ -396,8 +396,8 @@ deleteConnectionsAsync c waitDelivery = withAgentEnv c . deleteConnectionsAsync'
396396{-# INLINE deleteConnectionsAsync #-}
397397
398398-- | Create SMP agent connection (NEW command)
399- createConnection :: ConnectionModeI c => AgentClient -> NetworkRequestMode -> UserId -> Bool -> Bool -> SConnectionMode c -> Maybe (UserConnLinkData c ) -> Maybe CRClientData -> CR. InitialKeys -> SubscriptionMode -> AE (ConnId , (CreatedConnLink c , Maybe ClientServiceId ))
400- createConnection c nm userId enableNtfs checkNotices = withAgentEnv c .::. newConn c nm userId enableNtfs checkNotices
399+ createConnection :: ConnectionModeI c => AgentClient -> NetworkRequestMode -> UserId -> Maybe ByteString -> Bool -> Bool -> SConnectionMode c -> Maybe (UserConnLinkData c ) -> Maybe CRClientData -> CR. InitialKeys -> SubscriptionMode -> AE (ConnId , (Maybe C. KeyPairEd25519 , CreatedConnLink c , Maybe ClientServiceId ))
400+ createConnection c nm userId linkEntityId enableNtfs checkNotices = withAgentEnv c .::. newConn c nm userId linkEntityId enableNtfs checkNotices
401401{-# INLINE createConnection #-}
402402
403403-- | Prepare connection link for contact mode (no network call).
@@ -908,13 +908,13 @@ switchConnectionAsync' c corrId connId =
908908 connectionStats c $ DuplexConnection cData rqs' sqs
909909 _ -> throwE $ CMD PROHIBITED " switchConnectionAsync: not duplex"
910910
911- newConn :: ConnectionModeI c => AgentClient -> NetworkRequestMode -> UserId -> Bool -> Bool -> SConnectionMode c -> Maybe (UserConnLinkData c ) -> Maybe CRClientData -> CR. InitialKeys -> SubscriptionMode -> AM (ConnId , (CreatedConnLink c , Maybe ClientServiceId ))
912- newConn c nm userId enableNtfs checkNotices cMode linkData_ clientData pqInitKeys subMode = do
911+ newConn :: ConnectionModeI c => AgentClient -> NetworkRequestMode -> UserId -> Maybe ByteString -> Bool -> Bool -> SConnectionMode c -> Maybe (UserConnLinkData c ) -> Maybe CRClientData -> CR. InitialKeys -> SubscriptionMode -> AM (ConnId , (Maybe C. KeyPairEd25519 , CreatedConnLink c , Maybe ClientServiceId ))
912+ newConn c nm userId linkEntityId enableNtfs checkNotices cMode linkData_ clientData pqInitKeys subMode = do
913913 srv <- getSMPServer c userId
914914 when (checkNotices && connMode cMode == CMContact ) $ checkClientNotices c srv
915915 connId <- newConnNoQueues c userId enableNtfs cMode (CR. connPQEncryption pqInitKeys)
916916 (connId,)
917- <$> newRcvConnSrv c nm userId connId enableNtfs cMode linkData_ clientData pqInitKeys subMode srv
917+ <$> newRcvConnSrv c nm userId linkEntityId connId enableNtfs cMode linkData_ clientData pqInitKeys subMode srv
918918 `catchE` \ e -> withStore' c (`deleteConnRecord` connId) >> throwE e
919919
920920-- | Prepare connection link for contact mode (no network, no database).
@@ -1001,14 +1001,22 @@ setConnShortLinkAsync' c corrId connId userLinkData clientData =
10011001 _ -> throwE $ CMD PROHIBITED " setConnShortLinkAsync: invalid connection or mode"
10021002 enqueueCommand c corrId connId (Just srv) $ AClientCommand $ LSET userLinkData clientData
10031003
1004- getConnShortLinkAsync' :: AgentClient -> UserId -> ACorrId -> ConnShortLink 'CMContact -> AM ConnId
1005- getConnShortLinkAsync' c userId corrId shortLink@ (CSLContact _ _ srv _) = do
1006- g <- asks random
1007- connId <- withStore c $ \ db -> do
1008- -- server is created so the command is processed in server queue,
1009- -- not blocking other "no server" commands
1010- void $ createServer db srv
1011- prepareNewConn db g
1004+ getConnShortLinkAsync' :: AgentClient -> UserId -> ACorrId -> Maybe ConnId -> ConnShortLink 'CMContact -> AM ConnId
1005+ getConnShortLinkAsync' c userId corrId connId_ shortLink@ (CSLContact _ _ srv _) = do
1006+ connId <- case connId_ of
1007+ Just existingConnId -> do
1008+ -- connId and srv can be unrelated: connId is used as "mailbox" for LDATA delivery,
1009+ -- while srv is the short link's server for the LGET request.
1010+ -- E.g., owner's relay connection (connId, on server A) fetches relay's group link data (srv = server B).
1011+ -- This works because enqueueCommand stores (connId, srv) independently in the commands table,
1012+ -- the network request targets srv, and event delivery uses connId via corrId correlation.
1013+ withStore' c $ \ db -> void $ createServer db srv
1014+ pure existingConnId
1015+ Nothing -> do
1016+ g <- asks random
1017+ withStore c $ \ db -> do
1018+ void $ createServer db srv
1019+ prepareNewConn db g
10121020 enqueueCommand c corrId connId (Just srv) $ AClientCommand $ LGET shortLink
10131021 pure connId
10141022 where
@@ -1131,23 +1139,23 @@ changeConnectionUser' c oldUserId connId newUserId = do
11311139 where
11321140 updateConn = withStore' c $ \ db -> setConnUserId db oldUserId connId newUserId
11331141
1134- newRcvConnSrv :: forall c . ConnectionModeI c => AgentClient -> NetworkRequestMode -> UserId -> ConnId -> Bool -> SConnectionMode c -> Maybe (UserConnLinkData c ) -> Maybe CRClientData -> CR. InitialKeys -> SubscriptionMode -> SMPServerWithAuth -> AM (CreatedConnLink c , Maybe ClientServiceId )
1135- newRcvConnSrv c nm userId connId enableNtfs cMode userLinkData_ clientData pqInitKeys subMode srvWithAuth@ (ProtoServerWithAuth srv _) = do
1142+ newRcvConnSrv :: forall c . ConnectionModeI c => AgentClient -> NetworkRequestMode -> UserId -> Maybe ByteString -> ConnId -> Bool -> SConnectionMode c -> Maybe (UserConnLinkData c ) -> Maybe CRClientData -> CR. InitialKeys -> SubscriptionMode -> SMPServerWithAuth -> AM (Maybe C. KeyPairEd25519 , CreatedConnLink c , Maybe ClientServiceId )
1143+ newRcvConnSrv c nm userId linkEntityId connId enableNtfs cMode userLinkData_ clientData pqInitKeys subMode srvWithAuth@ (ProtoServerWithAuth srv _) = do
11361144 case (cMode, pqInitKeys) of
11371145 (SCMContact , CR. IKUsePQ ) -> throwE $ CMD PROHIBITED " newRcvConnSrv"
11381146 _ -> pure ()
11391147 e2eKeys <- atomically . C. generateKeyPair =<< asks random
11401148 case userLinkData_ of
11411149 Just d -> do
1142- (nonce, qUri, cReq, qd) <- prepareLinkData d $ fst e2eKeys
1150+ (sigKeys, nonce, qUri, cReq, qd) <- prepareLinkData linkEntityId d $ fst e2eKeys
11431151 (rq, qUri') <- createRcvQueue c nm userId connId srvWithAuth enableNtfs subMode (Just nonce) qd e2eKeys
11441152 ccLink <- connReqWithShortLink qUri cReq qUri' (shortLink rq)
1145- pure (ccLink, clientServiceId rq)
1153+ pure (Just sigKeys, ccLink, clientServiceId rq)
11461154 Nothing -> do
11471155 let qd = case cMode of SCMContact -> CQRContact Nothing ; SCMInvitation -> CQRMessaging Nothing
11481156 (rq, qUri) <- createRcvQueue c nm userId connId srvWithAuth enableNtfs subMode Nothing qd e2eKeys
11491157 cReq <- createConnReq qUri
1150- pure (CCLink cReq Nothing , clientServiceId rq)
1158+ pure (Nothing , CCLink cReq Nothing , clientServiceId rq)
11511159 where
11521160 createConnReq :: SMPQueueUri -> AM (ConnectionRequestUri c )
11531161 createConnReq qUri = do
@@ -1161,8 +1169,8 @@ newRcvConnSrv c nm userId connId enableNtfs cMode userLinkData_ clientData pqIni
11611169 (pk1, pk2, pKem, e2eRcvParams) <- liftIO $ CR. generateRcvE2EParams g (maxVersion e2eEncryptVRange) pqEnc
11621170 withStore' c $ \ db -> createRatchetX3dhKeys db connId pk1 pk2 pKem
11631171 pure $ CRInvitationUri crData $ toVersionRangeT e2eRcvParams e2eEncryptVRange
1164- prepareLinkData :: UserConnLinkData c -> C. PublicKeyX25519 -> AM (C. CbNonce , SMPQueueUri , ConnectionRequestUri c , ClntQueueReqData )
1165- prepareLinkData userLinkData e2eDhKey = do
1172+ prepareLinkData :: Maybe ByteString -> UserConnLinkData c -> C. PublicKeyX25519 -> AM (C. KeyPairEd25519 , C. CbNonce , SMPQueueUri , ConnectionRequestUri c , ClntQueueReqData )
1173+ prepareLinkData linkEntityId_ userLinkData e2eDhKey = do
11661174 g <- asks random
11671175 nonce@ (C. CbNonce corrId) <- atomically $ C. randomCbNonce g
11681176 sigKeys@ (_, privSigKey) <- atomically $ C. generateKeyPair @ 'C.Ed25519 g
@@ -1172,14 +1180,14 @@ newRcvConnSrv c nm userId connId enableNtfs cMode userLinkData_ clientData pqIni
11721180 qm = case cMode of SCMContact -> QMContact ; SCMInvitation -> QMMessaging
11731181 qUri = SMPQueueUri vr $ SMPQueueAddress srv sndId e2eDhKey (Just qm)
11741182 connReq <- createConnReq qUri
1175- let (linkKey, linkData) = SL. encodeSignLinkData sigKeys smpAgentVRange connReq Nothing userLinkData
1183+ let (linkKey, linkData) = SL. encodeSignLinkData sigKeys smpAgentVRange connReq linkEntityId_ userLinkData
11761184 qd <- case cMode of
11771185 SCMContact -> encryptContactLinkData g privSigKey linkKey sndId linkData
11781186 SCMInvitation -> do
11791187 let k = SL. invShortLinkKdf linkKey
11801188 srvData <- liftError id $ SL. encryptLinkData g k linkData
11811189 pure $ CQRMessaging $ Just CQRData {linkKey, privSigKey, srvReq = (sndId, srvData)}
1182- pure (nonce, qUri, connReq, qd)
1190+ pure (sigKeys, nonce, qUri, connReq, qd)
11831191 connReqWithShortLink :: SMPQueueUri -> ConnectionRequestUri c -> SMPQueueUri -> Maybe ShortLinkCreds -> AM (CreatedConnLink c )
11841192 connReqWithShortLink qUri cReq qUri' shortLink = case shortLink of
11851193 Just ShortLinkCreds {shortLinkId, shortLinkKey}
@@ -1341,7 +1349,9 @@ joinConnSrv c nm userId connId enableNtfs cReqUri@CRContactUri {} cInfo pqSup su
13411349 SomeConn cType conn <- withStore c (`getConn` connId)
13421350 let pqInitKeys = CR. joinContactInitialKeys (v >= pqdrSMPAgentVersion) pqSup
13431351 (CCLink cReq _, service) <- case conn of
1344- NewConnection _ -> newRcvConnSrv c NRMBackground userId connId enableNtfs SCMInvitation Nothing Nothing pqInitKeys subMode srv
1352+ NewConnection _ -> do
1353+ (_, ccLink, service) <- newRcvConnSrv c NRMBackground userId Nothing connId enableNtfs SCMInvitation Nothing Nothing pqInitKeys subMode srv
1354+ pure (ccLink, service)
13451355 RcvConnection _ rq -> mkJoinInvitation rq pqInitKeys
13461356 _ -> throwE $ CMD PROHIBITED $ " joinConnSrv: bad connection " <> show cType
13471357 void $ sendInvitation c nm userId connId qInfo vrsn cReq cInfo
@@ -1788,7 +1798,7 @@ runCommandProcessing c@AgentClient {subQ} connId server_ Worker {doWork} = do
17881798 NEW enableNtfs (ACM cMode) pqEnc subMode -> noServer $ do
17891799 triedHosts <- newTVarIO S. empty
17901800 tryCommand . withNextSrv c userId storageSrvs triedHosts [] $ \ srv -> do
1791- (CCLink cReq _, service) <- newRcvConnSrv c NRMBackground userId connId enableNtfs cMode Nothing Nothing pqEnc subMode srv
1801+ (_, CCLink cReq _, service) <- newRcvConnSrv c NRMBackground userId Nothing connId enableNtfs cMode Nothing Nothing pqEnc subMode srv
17921802 notify $ INV (ACR cMode cReq) service
17931803 LSET userLinkData clientData ->
17941804 withServer' . tryCommand $ do
0 commit comments