@@ -49,7 +49,7 @@ type GossipEventType = Text where \s -> s == "online" || s == "offline"
50type GossipEvent = {eventType: GossipEventType, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}
50type GossipEvent = {eventType: GossipEventType, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}
52−type ValidGossipEntry = {eventType: GossipEventType, pubkey: PubkeyHex, server: Text, expiresAt: Timestamp, timestamp: Timestamp, ttl: Int<S>}
52+type ValidGossipEntry = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, expiresAt: Timestamp, timestamp: Timestamp, ttl: Int<S>}
54type MessageStatus = Text where \s -> s == "unauthorized" || s == "rejected" || s == "delivered" || s == "federated" || s == "queued"
54type MessageStatus = Text where \s -> s == "unauthorized" || s == "rejected" || s == "delivered" || s == "federated" || s == "queued"
@@ -145,6 +145,7 @@ route Api = AuthApi | ClientApi | FederationApi | MaintenanceApi
146any = \xs pred -> fold (\acc x -> if acc then acc else pred x) (False {}) xs
146any = \xs pred -> fold (\acc x -> if acc then acc else pred x) (False {}) xs
147none = \xs pred -> not (any xs pred)
147none = \xs pred -> not (any xs pred)
148+when = \cond action -> if cond then action else yield {}
148headOf = \items -> fold (\acc x -> case acc of
149headOf = \items -> fold (\acc x -> case acc of
149 Nothing {} -> Just {value: x}
150 Nothing {} -> Just {value: x}
150 _ -> acc) Nothing {} items
151 _ -> acc) Nothing {} items
@@ -583,13 +584,11 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
583 let expiredEntries = mergeByKey (\a b -> a.pubkey == b.pubkey) [] (do
584 let expiredEntries = mergeByKey (\a b -> a.pubkey == b.pubkey) [] (do
584 p <- expiredLocal
585 p <- expiredLocal
585 yield {pubkey: p.pubkey, ttl: (let rawTtl = (p.expiresAt - p.timestamp) / 1000 in if rawTtl < offlineGossipMinTtl then offlineGossipMinTtl else if rawTtl > maxPresenceTtl then maxPresenceTtl else rawTtl), expiresAt: p.expiresAt})
586 yield {pubkey: p.pubkey, ttl: (let rawTtl = (p.expiresAt - p.timestamp) / 1000 in if rawTtl < offlineGossipMinTtl then offlineGossipMinTtl else if rawTtl > maxPresenceTtl then maxPresenceTtl else rawTtl), expiresAt: p.expiresAt})
586− if federationEnabled && count expiredEntries > 0 && count peerServers > 0
587+ when (federationEnabled && count expiredEntries > 0 && count peerServers > 0) (do
588− let gossipEvents = do
588+ let gossipEvents = do
589− e <- expiredEntries
589+ e <- expiredEntries
590− yield {eventType: "offline", pubkey: e.pubkey, server: serverHostN, timestamp: e.expiresAt, ttl: e.ttl}
590+ yield {eventType: "offline", pubkey: e.pubkey, server: serverHostN, timestamp: e.expiresAt, ttl: e.ttl}
591− sendBatchedGossip peerServers gossipEvents
591+ sendBatchedGossip peerServers gossipEvents)
594forkBroadcastOffline = \t result ->
593forkBroadcastOffline = \t result ->
595 fork (broadcastOfflineGossip t result.peerServers result.expiredLocal)
594 fork (broadcastOfflineGossip t result.peerServers result.expiredLocal)
@@ -658,14 +657,11 @@ pruneAndRetry = \t -> do
658 let pendingAcks = do a <- ackedIds; where isFederatedOrigin a.originServer; yield a
657 let pendingAcks = do a <- ackedIds; where isFederatedOrigin a.originServer; yield a
659 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: forwards, pendingAcks: pendingAcks}
658 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: forwards, pendingAcks: pendingAcks}
660 broadcastOfflineGossip t result.peerServers result.expiredLocal
659 broadcastOfflineGossip t result.peerServers result.expiredLocal
661− if federationEnabled && count result.pendingForwards > 0
660+ when (federationEnabled && count result.pendingForwards > 0)
662− then retryPendingForwards result.pendingForwards
661+ (retryPendingForwards result.pendingForwards)
664− if federationEnabled && count result.pendingAcks > 0
662+ when (federationEnabled && count result.pendingAcks > 0) (do
666− let ackOriginServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
663+ let ackOriginServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
667− sendAcksAndClean result.pendingAcks ackOriginServers
664+ sendAcksAndClean result.pendingAcks ackOriginServers)
670pollOnce : Text -> Int -> IO {clock} {authorized: Bool, cursor: Int, events: [PollEvent]}
666pollOnce : Text -> Int -> IO {clock} {authorized: Bool, cursor: Int, events: [PollEvent]}
671pollOnce = \token cursor -> do
667pollOnce = \token cursor -> do
@@ -751,40 +747,35 @@ serve = \req -> case req of
751 let sigValid = case matched of
747 let sigValid = case matched of
752 Nothing {} -> False {}
748 Nothing {} -> False {}
753 _ -> verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature)
749 _ -> verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature)
754− set *challenges = if sigValid
750+ if not sigValid
755− then do
751+ then yield {sigValid: False {}, peerServers: [], expiredLocal: pr.expiredLocal, offlinePeers: pr.peerServers}
753+ set *challenges = do
756− c <- challenges
754+ c <- challenges
757− where not (c.pubkey == pubkey && c.challenge == challenge)
755+ where not (c.pubkey == pubkey && c.challenge == challenge)
760− let sessionsCapped = capPerKey sessions (\s -> s.pubkey == pubkey) maxSessionsPerKey (\s -> s.expiresAt) t
757+ let sessionsCapped = capPerKey sessions (\s -> s.pubkey == pubkey) maxSessionsPerKey (\s -> s.expiresAt) t
761− let activeSessionsCapped = do s <- sessionsCapped; where s.expiresAt > t; yield s
758+ let activeSessionsCapped = do s <- sessionsCapped; where s.expiresAt > t; yield s
762− let globalSessionsCapped = capGlobal activeSessionsCapped maxSessions (\s -> s.expiresAt)
759+ let globalSessionsCapped = capGlobal activeSessionsCapped maxSessions (\s -> s.expiresAt)
763− set *sessions = if sigValid
764− then union globalSessionsCapped [{token: token, pubkey: pubkey, expiresAt: expiry}]
760+ set *sessions = union globalSessionsCapped [{token: token, pubkey: pubkey, expiresAt: expiry}]
766− let presenceWithout = do
761+ let presenceWithout = do
767− p <- presence
762+ p <- presence
768− where not (p.pubkey == pubkey && p.server == serverHostN)
763+ where not (p.pubkey == pubkey && p.server == serverHostN)
770− let presenceEvicted = capGlobal presenceWithout maxPresenceEntries (\p -> p.expiresAt)
765+ let presenceEvicted = capGlobal presenceWithout maxPresenceEntries (\p -> p.expiresAt)
771− let updatedPresence = if sigValid
772− then union presenceEvicted [{pubkey: pubkey, server: serverHostN, expiresAt: expiry, timestamp: t}]
766+ let updatedPresence = union presenceEvicted [{pubkey: pubkey, server: serverHostN, expiresAt: expiry, timestamp: t}]
774− set *presence = updatedPresence
767+ set *presence = updatedPresence
775− let peerServers = if sigValid && federationEnabled
768+ let peerServers = if federationEnabled
776− then collectPeerServers updatedPresence (\p -> p.server)
769+ then collectPeerServers updatedPresence (\p -> p.server)
778− yield {sigValid: sigValid, peerServers: peerServers, expiredLocal: pr.expiredLocal, offlinePeers: pr.peerServers}
771+ yield {sigValid: True {}, peerServers: peerServers, expiredLocal: pr.expiredLocal, offlinePeers: pr.peerServers}
780− if result.sigValid && federationEnabled
773+ when (result.sigValid && federationEnabled)
781− then sendToEach result.peerServers (\server -> do
774+ (sendToEach result.peerServers (\server -> do
782 _ <- fetch (federationScheme ++ server) (RecvGossip {
775 _ <- fetch (federationScheme ++ server) (RecvGossip {
783 authorization: "Bearer " ++ federationSecret,
776 authorization: "Bearer " ++ federationSecret,
784 events: [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}], hopsRemaining: 2, fromServer: serverHostN})
777 events: [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}], hopsRemaining: 2, fromServer: serverHostN})
785− yield {}
778+ yield {}))
788 broadcastOfflineGossip t result.offlinePeers (do
779 broadcastOfflineGossip t result.offlinePeers (do
789 p <- result.expiredLocal
780 p <- result.expiredLocal
790 where not (result.sigValid && p.pubkey == pubkey)
781 where not (result.sigValid && p.pubkey == pubkey)
@@ -846,20 +837,17 @@ serve = \req -> case req of
846 else forwards
837 else forwards
847 yield {status: status, shouldInsert: shouldInsert, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
838 yield {status: status, shouldInsert: shouldInsert, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
849− if count result.remoteServers > 0 && result.shouldInsert && federationEnabled
840+ when (count result.remoteServers > 0 && result.shouldInsert && federationEnabled)
850− then sendToEach result.remoteServers (\server -> do
841+ (sendToEach result.remoteServers (\server -> do
851 fwdResult <- fetch (federationScheme ++ server) (ForwardMessage {
842 fwdResult <- fetch (federationScheme ++ server) (ForwardMessage {
852 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
843 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
853 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
844 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
854 authorization: "Bearer " ++ federationSecret})
845 authorization: "Bearer " ++ federationSecret})
855 case fwdResult of
846 case fwdResult of
856 Ok _ -> removeForward msgId server
847 Ok _ -> removeForward msgId server
857− Err _ -> yield {}
848+ Err _ -> yield {}))
860− if result.forwardsAtCapacity && count result.remoteServers > 0 && result.shouldInsert
849+ when (result.forwardsAtCapacity && count result.remoteServers > 0 && result.shouldInsert)
861− then println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")
850+ (println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking"))
863 broadcastOfflineGossip t result.peerServers result.expiredLocal)
851 broadcastOfflineGossip t result.peerServers result.expiredLocal)
864 yield (respond {status: result.status, messageId: if result.shouldInsert then msgId else ""})
852 yield (respond {status: result.status, messageId: if result.shouldInsert then msgId else ""})
@@ -897,41 +885,34 @@ serve = \req -> case req of
897 yield {ok: True {}, pendingAcks: ackedFederated, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, evictedEntries: evictedEntries}
885 yield {ok: True {}, pendingAcks: ackedFederated, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, evictedEntries: evictedEntries}
898 let originServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
886 let originServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
900− if result.ok
888+ when result.ok (do
902− if count originServers > 0 && federationEnabled
889+ when (count originServers > 0 && federationEnabled)
903− then sendAcksAndClean result.pendingAcks originServers
890+ (sendAcksAndClean result.pendingAcks originServers)
905− let evictedToRetry = do
891+ let evictedToRetry = do
906− a <- result.evictedEntries
892+ a <- result.evictedEntries
907− where none result.pendingAcks (\a2 -> a2.messageId == a.messageId && a2.toKey == a.toKey)
893+ where none result.pendingAcks (\a2 -> a2.messageId == a.messageId && a2.toKey == a.toKey)
909− if count evictedToRetry > 0 && federationEnabled
895+ when (count evictedToRetry > 0 && federationEnabled) (do
911− let evictedOriginServers = collectPeerServers evictedToRetry (\a -> a.originServer)
896+ let evictedOriginServers = collectPeerServers evictedToRetry (\a -> a.originServer)
912− sendToEach evictedOriginServers (\server -> do
897+ sendToEach evictedOriginServers (\server -> do
913− fetchResult <- fetch (federationScheme ++ server) (FederationAck {
898+ fetchResult <- fetch (federationScheme ++ server) (FederationAck {
914− authorization: "Bearer " ++ federationSecret,
899+ authorization: "Bearer " ++ federationSecret,
915− messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
900+ messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
916− originServer: server, fromServer: serverHostN})
901+ originServer: server, fromServer: serverHostN})
917− case fetchResult of
902+ case fetchResult of
918− Err _ -> do
903+ Err _ -> do
919− dropped <- atomic do
904+ dropped <- atomic do
920− ackedIds <- *ackedIds
905+ ackedIds <- *ackedIds
921− let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
906+ let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
922− let combined = union ackedIds reinsert
907+ let combined = union ackedIds reinsert
923− let excess = count combined - maxAckedIds
908+ let excess = count combined - maxAckedIds
924− let afterEviction = if excess <= 0 then combined
909+ let afterEviction = if excess <= 0 then combined
925− else evictOldestAckedIds combined excess
910+ else evictOldestAckedIds combined excess
926− set *ackedIds = afterEviction
911+ set *ackedIds = afterEviction
927− yield excess
912+ yield excess
928− if dropped > 0
913+ when (dropped > 0)
929− then println ("ackMessages: ackedIds at capacity (" ++ show maxAckedIds ++ "), " ++ show dropped ++ " ack entries permanently lost for " ++ server)
914+ (println ("ackMessages: ackedIds at capacity (" ++ show maxAckedIds ++ "), " ++ show dropped ++ " ack entries permanently lost for " ++ server))
931− _ -> yield {}
915+ _ -> yield {})))
935 broadcastOfflineGossip t result.peerServers result.expiredLocal)
916 broadcastOfflineGossip t result.peerServers result.expiredLocal)
936 yield (respond {ok: result.ok})
917 yield (respond {ok: result.ok})
@@ -973,7 +954,7 @@ serve = \req -> case req of
973 where none offlineEntries (\o -> o.server == fromServer && samePubkeyServer o e && o.timestamp > e.timestamp)
954 where none offlineEntries (\o -> o.server == fromServer && samePubkeyServer o e && o.timestamp > e.timestamp)
975 let affectedKeys = do
956 let affectedKeys = do
976− e <- union onlineEntries offlineEntries
957+ e <- allValidEntries
977 yield {pubkey: e.pubkey, server: e.server}
958 yield {pubkey: e.pubkey, server: e.server}
978 let deduped = mergeByPubkeyServer [] filteredOnline
959 let deduped = mergeByPubkeyServer [] filteredOnline
979 gossipResult <- atomic do
960 gossipResult <- atomic do
@@ -1027,23 +1008,19 @@ serve = \req -> case req of
1027 e <- union filteredOnline filteredOffline
1008 e <- union filteredOnline filteredOffline
1028 where any gossipResult.appliedKeys (\a -> samePubkeyServer a e)
1009 where any gossipResult.appliedKeys (\a -> samePubkeyServer a e)
1029 yield {eventType: e.eventType, pubkey: e.pubkey, server: e.server, timestamp: e.timestamp, ttl: e.ttl}
1010 yield {eventType: e.eventType, pubkey: e.pubkey, server: e.server, timestamp: e.timestamp, ttl: e.ttl}
1030− if not gossipResult.withinCapacity
1011+ when (not gossipResult.withinCapacity)
1031− then println ("gossip: presence at capacity (" ++ show maxPresenceEntries ++ "), limiting new entries from " ++ fromServer)
1012+ (println ("gossip: presence at capacity (" ++ show maxPresenceEntries ++ "), limiting new entries from " ++ fromServer))
1033 fork (do
1013 fork (do
1034− if validRequest && cappedHops > 1 && federationEnabled && count effectiveEvents > 0
1014+ when (validRequest && cappedHops > 1 && federationEnabled && count effectiveEvents > 0) (do
1036− let targets = do
1015+ let targets = do
1037− s <- gossipResult.gossipPeerServers
1016+ s <- gossipResult.gossipPeerServers
1038− where not (s == fromServer)
1017+ where not (s == fromServer)
1039− yield s
1018+ yield s
1040− sendToEach targets (\server -> do
1019+ sendToEach targets (\server -> do
1041− _ <- fetch (federationScheme ++ server) (RecvGossip {
1020+ _ <- fetch (federationScheme ++ server) (RecvGossip {
1042− authorization: "Bearer " ++ federationSecret,
1021+ authorization: "Bearer " ++ federationSecret,
1043− events: effectiveEvents, hopsRemaining: cappedHops - 1, fromServer: serverHostN})
1022+ events: effectiveEvents, hopsRemaining: cappedHops - 1, fromServer: serverHostN})
1044− yield {}
1023+ yield {}))
1047 broadcastOfflineGossip t gossipResult.peerServers gossipResult.expiredLocal)
1024 broadcastOfflineGossip t gossipResult.peerServers gossipResult.expiredLocal)
1048 yield (respond {ok: validRequest})
1025 yield (respond {ok: validRequest})
@@ -1063,10 +1040,11 @@ serve = \req -> case req of
1063 seqCounter <- *seqCounter
1040 seqCounter <- *seqCounter
1064 let seqTs = nextSeqTs t seqCounter
1041 let seqTs = nextSeqTs t seqCounter
1065 ackedIds <- *ackedIds
1042 ackedIds <- *ackedIds
1066− let exists = authorized && validOrigin
1043+ let valid = authorized && validOrigin
1044+ let alreadyExists = valid
1067 && (any messages (\m -> m.messageId == messageId && m.toKey == toKey)
1045 && (any messages (\m -> m.messageId == messageId && m.toKey == toKey)
1068 || any ackedIds (\a -> a.messageId == messageId && a.toKey == toKey))
1046 || any ackedIds (\a -> a.messageId == messageId && a.toKey == toKey))
1069− let shouldInsert = authorized && validOrigin && not exists
1047+ let shouldInsert = valid && not alreadyExists
1070 && countMessagesFor messages toKey t < maxMessagesPerRecipient
1048 && countMessagesFor messages toKey t < maxMessagesPerRecipient
1071 set *seqCounter = if shouldInsert then seqTs else seqCounter
1049 set *seqCounter = if shouldInsert then seqTs else seqCounter
1072 set *messages = if shouldInsert
1050 set *messages = if shouldInsert
@@ -1074,7 +1052,7 @@ serve = \req -> case req of
1074 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
1052 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
1075 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer, receivedAt: seqTs}]
1053 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer, receivedAt: seqTs}]
1076 else messages
1054 else messages
1077− yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, ok: shouldInsert || exists}
1055+ yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, ok: shouldInsert || alreadyExists}
1078 forkBroadcastOffline t result
1056 forkBroadcastOffline t result
1079 yield (respond {ok: result.ok})
1057 yield (respond {ok: result.ok})
@@ -1104,7 +1082,7 @@ serve = \req -> case req of
1104 Prune {authorization, respond} -> do
1082 Prune {authorization, respond} -> do
1105 t <- now
1083 t <- now
1106 let authorized = verifyMaintenanceAuth authorization
1084 let authorized = verifyMaintenanceAuth authorization
1107− if authorized then fork (pruneAndRetry t) else yield {}
1085+ when authorized (fork (pruneAndRetry t))
1108 yield (respond {ok: authorized})
1086 yield (respond {ok: authorized})
1110-- ============================================
1088-- ============================================