@@ -71,10 +71,9 @@ type AckedEntry = {messageId: MessageIdHex, toKey: PubkeyHex, ackedAt: Timestamp
71type AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
71type AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
72type PruneResult = {expiredLocal: [PresenceEntry], peerServers: [ServerName]}
72type PruneResult = {expiredLocal: [PresenceEntry], peerServers: [ServerName]}
73type PollResult = PollUnauthorized {} | PollAuthorized {events: [PollEvent], cursor: Timestamp}
73type PollResult = PollUnauthorized {} | PollAuthorized {events: [PollEvent], cursor: Timestamp}
74−type VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [ServerName]}
74+type DeliveryStatus = Text where \s -> s == "delivered" || s == "federated" || s == "queued"
75−type SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: MessageStatus, remoteServers: [ServerName], fromKey: PubkeyHex}
75+type SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: DeliveryStatus, remoteServers: [ServerName], fromKey: PubkeyHex}
76type AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
76type AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
77−type LookupResult = LookupUnauthorized {} | LookupDone {foundServer: OptionalServerName}
80-- ============================================
79-- ============================================
@@ -107,12 +106,12 @@ route AuthApi where
107 -> {challenge: Text}
106 -> {challenge: Text}
108 = ReqChallenge
107 = ReqChallenge
109 POST {pubkey: PubkeyHex, challenge: Text, signature: SignatureHex} /verify
108 POST {pubkey: PubkeyHex, challenge: Text, signature: SignatureHex} /verify
110− -> {token: Text, expiresAt: Int}
109+ -> {token: Text, expiresAt: Timestamp}
111 = VerifyAuth
110 = VerifyAuth
113route ClientApi where
112route ClientApi where
114− POST {cursor: Int, timeout: Int} /poll headers {authorization: Text}
113+ POST {cursor: Timestamp, timeout: Int} /poll headers {authorization: Text}
115− -> {events: [PollEvent], cursor: Int, authorized: Bool}
114+ -> {events: [PollEvent], cursor: Timestamp, authorized: Bool}
117 /messages
116 /messages
118 POST {to: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp} / headers {authorization: Text}
117 POST {to: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp} / headers {authorization: Text}
@@ -652,6 +651,33 @@ sendAcksAndClean = \pendingAcks servers ->
652 sendToEach servers (\server ->
651 sendToEach servers (\server ->
653 sendBatchedAcksToServer (do a <- pendingAcks; where a.originServer == server; yield a) server)
652 sendBatchedAcksToServer (do a <- pendingAcks; where a.originServer == server; yield a) server)
654+retryEvictedAcks = \evictedEntries pendingAcks ->
655+ let evictedToRetry = do
657+ where none pendingAcks (\a2 -> a2.messageId == a.messageId && a2.toKey == a.toKey)
659+ in when (count evictedToRetry > 0) (do
660+ let evictedOriginServers = collectPeerServers evictedToRetry (\a -> a.originServer)
661+ sendToEach evictedOriginServers (\server -> do
662+ fetchResult <- fetch (federationScheme ++ server) (FederationAck {
663+ authorization: "Bearer " ++ federationSecret,
664+ messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
665+ originServer: server, fromServer: serverHostN})
668+ dropped <- atomic do
669+ ackedIds <- *ackedIds
670+ let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
671+ let combined = union ackedIds reinsert
672+ let excess = count combined - maxAckedIds
673+ let afterEviction = if excess <= 0 then combined
674+ else evictOldestAckedIds combined excess
675+ set *ackedIds = afterEviction
678+ (println ("ackMessages: ackedIds at capacity (" ++ show maxAckedIds ++ "), " ++ show dropped ++ " ack entries permanently lost for " ++ server))
655pruneAndRetry = \t -> do
681pruneAndRetry = \t -> do
656 result <- atomic do
682 result <- atomic do
657 set *lastPrunedAt = t
683 set *lastPrunedAt = t
@@ -743,7 +769,7 @@ serve = \req -> case req of
743 let hasMatch = any challenges (\c -> c.pubkey == pubkey && c.challenge == challenge && c.expiresAt > t)
769 let hasMatch = any challenges (\c -> c.pubkey == pubkey && c.challenge == challenge && c.expiresAt > t)
744 let sigValid = if hasMatch then verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature) else False {}
770 let sigValid = if hasMatch then verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature) else False {}
745 if not sigValid
771 if not sigValid
746− then yield {pr: pr, verify: VerifyFailed {}}
772+ then yield {pr: pr, verified: False {}, peerServers: []}
748 set *challenges = do
774 set *challenges = do
749 c <- challenges
775 c <- challenges
@@ -762,20 +788,17 @@ serve = \req -> case req of
762 let peerServers = if federationEnabled
788 let peerServers = if federationEnabled
763 then collectPeerServers updatedPresence (\p -> p.server)
789 then collectPeerServers updatedPresence (\p -> p.server)
765− yield {pr: pr, verify: VerifyOk {peerServers: peerServers}}
791+ yield {pr: pr, verified: True {}, peerServers: peerServers}
766− let expiredLocal = case result.verify of
792+ let expiredLocal = if result.verified
767− VerifyOk _ -> do p <- result.pr.expiredLocal; where not (p.pubkey == pubkey); yield p
793+ then do p <- result.pr.expiredLocal; where not (p.pubkey == pubkey); yield p
768− _ -> result.pr.expiredLocal
794+ else result.pr.expiredLocal
770− case result.verify of
771− VerifyOk {peerServers} ->
772− when federationEnabled
796+ when (result.verified && federationEnabled)
773− (sendBatchedGossip peerServers [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}] 2)
797+ (sendBatchedGossip result.peerServers [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}] 2)
775 broadcastOfflineGossip t result.pr.peerServers expiredLocal)
798 broadcastOfflineGossip t result.pr.peerServers expiredLocal)
776− yield (case result.verify of
799+ yield (if result.verified
777− VerifyFailed _ -> respond {token: "", expiresAt: 0}
778− VerifyOk _ -> respond {token: token, expiresAt: expiry})
800+ then respond {token: token, expiresAt: expiry}
801+ else respond {token: "", expiresAt: 0})
780 Poll {cursor, timeout, authorization, respond} -> do
803 Poll {cursor, timeout, authorization, respond} -> do
@@ -881,31 +904,7 @@ serve = \req -> case req of
881 let originServers = collectPeerServers pendingAcks (\a -> a.originServer)
904 let originServers = collectPeerServers pendingAcks (\a -> a.originServer)
882 when (count originServers > 0)
905 when (count originServers > 0)
883 (sendAcksAndClean pendingAcks originServers)
906 (sendAcksAndClean pendingAcks originServers)
884− let evictedToRetry = do
885− a <- evictedEntries
907+ retryEvictedAcks evictedEntries pendingAcks)
886− where none pendingAcks (\a2 -> a2.messageId == a.messageId && a2.toKey == a.toKey)
888− when (count evictedToRetry > 0) (do
889− let evictedOriginServers = collectPeerServers evictedToRetry (\a -> a.originServer)
890− sendToEach evictedOriginServers (\server -> do
891− fetchResult <- fetch (federationScheme ++ server) (FederationAck {
892− authorization: "Bearer " ++ federationSecret,
893− messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
894− originServer: server, fromServer: serverHostN})
897− dropped <- atomic do
898− ackedIds <- *ackedIds
899− let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
900− let combined = union ackedIds reinsert
901− let excess = count combined - maxAckedIds
902− let afterEviction = if excess <= 0 then combined
903− else evictOldestAckedIds combined excess
904− set *ackedIds = afterEviction
907− (println ("ackMessages: ackedIds at capacity (" ++ show maxAckedIds ++ "), " ++ show dropped ++ " ack entries permanently lost for " ++ server))
909 _ -> yield {}
908 _ -> yield {}
910 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
909 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
911 yield (case result.ack of
910 yield (case result.ack of
@@ -921,7 +920,7 @@ serve = \req -> case req of
921 let auth = authenticateRequest sessions authorization t
920 let auth = authenticateRequest sessions authorization t
922 case auth of
921 case auth of
923 Unauthorized {} ->
922 Unauthorized {} ->
924− yield {pr: pr, lookup: LookupUnauthorized {}}
923+ yield {pr: pr, authorized: False {}, foundServer: ""}
925 Authorized _ -> do
924 Authorized _ -> do
926 presence <- *presence
925 presence <- *presence
927 let valid = do
926 let valid = do
@@ -933,11 +932,11 @@ serve = \req -> case req of
933 else if p.server == serverHostN then {server: serverHostN, best: p.expiresAt}
932 else if p.server == serverHostN then {server: serverHostN, best: p.expiresAt}
934 else if p.expiresAt > acc.best then {server: p.server, best: p.expiresAt}
933 else if p.expiresAt > acc.best then {server: p.server, best: p.expiresAt}
935 else acc) {server: "", best: 0} valid).server
934 else acc) {server: "", best: 0} valid).server
936− yield {pr: pr, lookup: LookupDone {foundServer: foundServer}}
935+ yield {pr: pr, authorized: True {}, foundServer: foundServer}
937 forkBroadcastOffline t result.pr
936 forkBroadcastOffline t result.pr
938− yield (case result.lookup of
937+ yield (if result.authorized
939− LookupUnauthorized _ -> respond {online: False {}, server: "", authorized: False {}}
940− LookupDone {foundServer} -> respond {online: not (foundServer == ""), server: foundServer, authorized: True {}})
938+ then respond {online: not (result.foundServer == ""), server: result.foundServer, authorized: True {}}
939+ else respond {online: False {}, server: "", authorized: False {}})
942 RecvGossip {events, hopsRemaining, fromServer, authorization, respond} -> do
941 RecvGossip {events, hopsRemaining, fromServer, authorization, respond} -> do