@@ -312,25 +312,11 @@ route Api = AuthApi | ClientApi | FederationApi
312-- Everything is intentionally O(n) over flat lists; performance is sacrificed
312-- Everything is intentionally O(n) over flat lists; performance is sacrificed
313-- for clarity per the file header.
313-- for clarity per the file header.
315−-- Keep elements satisfying `pred`. Uses Knot's relation comprehension (`where`).
316−filter : a b -> (b -> Bool) -> a b
317−filter = \xs pred -> do
321−-- Inverse of `filter`: drop elements satisfying `pred`.
322−removeWhere : a b -> (b -> Bool) -> a b
323−removeWhere = \xs pred -> filter xs (\x -> not pred x)
324−-- Map without filtering (a.k.a. classic `map`).
325−pluck : a b -> (b -> c) -> a c
329-- Filter then map in one pass.
315-- Filter then map in one pass.
330mapWhere : a b -> (b -> Bool) -> (b -> c) -> a c
316mapWhere : a b -> (b -> Bool) -> (b -> c) -> a c
331−mapWhere = \xs pred -> pluck (filter xs pred)
317+mapWhere = \xs pred fn -> map fn (filter pred xs)
332countWhere : a b -> (b -> Bool) -> Int
318countWhere : a b -> (b -> Bool) -> Int
333−countWhere = \xs pred -> count (filter xs pred)
319+countWhere = \xs pred -> count (filter pred xs)
334-- True when at least one element matches.
320-- True when at least one element matches.
335any : [a] -> (a -> Bool) -> Bool
321any : [a] -> (a -> Bool) -> Bool
336any = \xs pred -> countWhere xs pred > 0
322any = \xs pred -> countWhere xs pred > 0
@@ -341,7 +327,7 @@ none = \xs pred -> not any xs pred
341all : [a] -> (a -> Bool) -> Bool
327all : [a] -> (a -> Bool) -> Bool
342all = \xs pred -> none xs (\x -> not pred x)
328all = \xs pred -> none xs (\x -> not pred x)
343findFirst : [a] -> (a -> Bool) -> Maybe a
329findFirst : [a] -> (a -> Bool) -> Maybe a
344−findFirst = \xs pred -> headOf (filter xs pred)
330+findFirst = \xs pred -> headOf (filter pred xs)
345-- Run `action` only when `cond` is True; otherwise no-op.
331-- Run `action` only when `cond` is True; otherwise no-op.
346when : Bool -> IO {} {} -> IO {} {}
332when : Bool -> IO {} {} -> IO {} {}
347when = \cond action -> if cond then action else yield {}
333when = \cond action -> if cond then action else yield {}
@@ -368,7 +354,7 @@ capList = \items maxN getKey -> dropRelation (max 0 (count items - maxN)) (sortB
368-- single key can't exhaust a global pool — see `maxChallengesPerKey` and
354-- single key can't exhaust a global pool — see `maxChallengesPerKey` and
369-- `maxSessionsPerKey`.
355-- `maxSessionsPerKey`.
370capPerKey : [a] -> (a -> Bool) -> Int -> (a -> b) -> [a]
356capPerKey : [a] -> (a -> Bool) -> Int -> (a -> b) -> [a]
371−capPerKey = \items pred maxN getKey -> union (removeWhere items pred) (capList (filter items pred) (maxN - 1) getKey)
357+capPerKey = \items pred maxN getKey -> union (filter (\x -> not pred x) items) (capList (filter pred items) (maxN - 1) getKey)
373-- Sequence an IO action over each item, in order. Recurses through the list
359-- Sequence an IO action over each item, in order. Recurses through the list
374-- one element at a time; this is the closest equivalent of `mapM_` here.
360-- one element at a time; this is the closest equivalent of `mapM_` here.
@@ -520,7 +506,7 @@ withSessionAuth = \authorization t clientIp onUnauth onAuth -> do
520-- `entries` list is what the caller must write back to the corresponding ref;
506-- `entries` list is what the caller must write back to the corresponding ref;
521-- it has been capped at 5000 rows (LRU by `windowStart`) to bound memory.
507-- it has been capped at 5000 rows (LRU by `windowStart`) to bound memory.
522checkRate : [RateEntry] -> Text -> Int -> Int<Ms> -> Timestamp -> {limited: Bool, entries: [RateEntry]}
508checkRate : [RateEntry] -> Text -> Int -> Int<Ms> -> Timestamp -> {limited: Bool, entries: [RateEntry]}
523−checkRate = \entries key maxRate windowMs t -> (\cur -> if cur.count > maxRate then {limited: True {}, entries} else {limited: False {}, entries: capList (union (removeWhere entries (\x -> x.key == key)) [{key, cur.windowStart, cur.count}]) 5000 (\r -> r.windowStart)}) (case findFirst entries (\r -> r.key == key && t - r.windowStart < windowMs) of
509+checkRate = \entries key maxRate windowMs t -> (\cur -> if cur.count > maxRate then {limited: True {}, entries} else {limited: False {}, entries: capList (union (filter (\x -> not (x.key == key)) entries) [{key, cur.windowStart, cur.count}]) 5000 (\r -> r.windowStart)}) (case findFirst entries (\r -> r.key == key && t - r.windowStart < windowMs) of
524 Just {value: r} -> {r.windowStart, count: r.count + 1}
510 Just {value: r} -> {r.windowStart, count: r.count + 1}
525 _ -> {windowStart: t, count: 1})
511 _ -> {windowStart: t, count: 1})
@@ -608,7 +594,7 @@ recordFailure = \server -> do
608 let prev = case findFirst failures same of
594 let prev = case findFirst failures same of
609 Just {value: f} -> {failures: f.failures + 1, f.firstFailedAt}
595 Just {value: f} -> {failures: f.failures + 1, f.firstFailedAt}
610 _ -> {failures: 1, firstFailedAt: t}
596 _ -> {failures: 1, firstFailedAt: t}
611− *forwardFailures = union (removeWhere failures same) [
597+ *forwardFailures = union (filter (\f -> not same f) failures) [
612 {server, failedAt: t, prev.failures, prev.firstFailedAt}
598 {server, failedAt: t, prev.failures, prev.firstFailedAt}
@@ -617,7 +603,7 @@ recordFailure = \server -> do
617clearFailure : Text -> IO {rw *forwardFailures} {}
603clearFailure : Text -> IO {rw *forwardFailures} {}
618clearFailure = \server -> atomic (do
604clearFailure = \server -> atomic (do
619 forwardFailures <- *forwardFailures
605 forwardFailures <- *forwardFailures
620− *forwardFailures = filter forwardFailures (\f -> f.server != server))
606+ *forwardFailures = filter (\f -> f.server != server) forwardFailures)
622-- ============================================
608-- ============================================
623-- Common utility queries
609-- Common utility queries
@@ -633,12 +619,12 @@ nextSeqTs = \t seqCounter -> max t (seqCounter + 1)
633-- All unexpired presence rows for a given pubkey (may include both local and
619-- All unexpired presence rows for a given pubkey (may include both local and
634-- remote entries — the recipient may live on multiple servers simultaneously).
620-- remote entries — the recipient may live on multiple servers simultaneously).
635presenceForKey : [PresenceEntry] -> PubkeyHex -> Timestamp -> [PresenceEntry]
621presenceForKey : [PresenceEntry] -> PubkeyHex -> Timestamp -> [PresenceEntry]
636−presenceForKey = \presence pubkey t -> filter presence (\p -> p.pubkey == pubkey && p.expiresAt > t)
622+presenceForKey = \presence pubkey t -> filter (\p -> p.pubkey == pubkey && p.expiresAt > t) presence
638-- Project items through `getServer`, drop our own hostname, and dedupe.
624-- Project items through `getServer`, drop our own hostname, and dedupe.
639-- Used to derive peer recipient lists from forwards/presence/etc.
625-- Used to derive peer recipient lists from forwards/presence/etc.
640collectPeers : [a] -> (a -> Text) -> [Text]
626collectPeers : [a] -> (a -> Text) -> [Text]
641−collectPeers = \items getServer -> filter (pluck items getServer) isRemote
627+collectPeers = \items getServer -> filter isRemote (map getServer items)
643-- All distinct remote hostnames currently appearing in `presence` (the gossip
629-- All distinct remote hostnames currently appearing in `presence` (the gossip
644-- broadcast set we know about, less ourselves).
630-- broadcast set we know about, less ourselves).
@@ -658,18 +644,18 @@ knownPeerServersFromPresence = \presence -> collectPeers presence (\p -> p.serve
658pruneExpired : Int<Ms> -> IO {rw *challenges, rw *forwardFailures, rw *messages, rw *presence, rw *sessions} [{expiresAt: Timestamp, pubkey: PubkeyHex, server: ServerName}]
644pruneExpired : Int<Ms> -> IO {rw *challenges, rw *forwardFailures, rw *messages, rw *presence, rw *sessions} [{expiresAt: Timestamp, pubkey: PubkeyHex, server: ServerName}]
659pruneExpired = \t -> atomic do
645pruneExpired = \t -> atomic do
660 ch <- *challenges
646 ch <- *challenges
661− *challenges = filter ch (\x -> x.expiresAt > t)
647+ *challenges = filter (\x -> x.expiresAt > t) ch
662 ses <- *sessions
648 ses <- *sessions
663− *sessions = filter ses (\x -> x.expiresAt > t)
649+ *sessions = filter (\x -> x.expiresAt > t) ses
664 msgs <- *messages
650 msgs <- *messages
665− *messages = filter msgs (\m -> t - m.receivedAt < messageMaxAge)
651+ *messages = filter (\m -> t - m.receivedAt < messageMaxAge) msgs
666 ff <- *forwardFailures
652 ff <- *forwardFailures
667− *forwardFailures = filter ff (\f -> t - f.failedAt < 86400000)
653+ *forwardFailures = filter (\f -> t - f.failedAt < 86400000) ff
668 -- presence: remember locally-expired keys for offline gossip
654 -- presence: remember locally-expired keys for offline gossip
669 pres <- *presence
655 pres <- *presence
670− *presence = filter pres (\p -> p.expiresAt > t)
656+ *presence = filter (\p -> p.expiresAt > t) pres
671 let stillLive = \pk -> any pres (\k -> isLocalServer k.server && k.pubkey == pk && k.expiresAt > t)
657 let stillLive = \pk -> any pres (\k -> isLocalServer k.server && k.pubkey == pk && k.expiresAt > t)
672− yield (filter pres (\p -> p.expiresAt <= t && isLocalServer p.server && not (stillLive p.pubkey)))
658+ yield (filter (\p -> p.expiresAt <= t && isLocalServer p.server && not (stillLive p.pubkey)) pres)
674-- ============================================
660-- ============================================
675-- Federation gossip
661-- Federation gossip
@@ -695,9 +681,9 @@ sendBatchedGossip = \peers events -> forEach peers (\server -> do
695broadcastOfflineGossip : [{pubkey: PubkeyHex | a}] -> IO {r *presence} {}
681broadcastOfflineGossip : [{pubkey: PubkeyHex | a}] -> IO {r *presence} {}
696broadcastOfflineGossip = \expiredLocal -> do
682broadcastOfflineGossip = \expiredLocal -> do
697 presence <- *presence
683 presence <- *presence
698− when (federationEnabled && count expiredLocal > 0) (sendBatchedGossip (knownPeerServersFromPresence presence) (pluck expiredLocal (\p -> GossipOffline {
684+ when (federationEnabled && count expiredLocal > 0) (sendBatchedGossip (knownPeerServersFromPresence presence) (map (\p -> GossipOffline {
700− })))
686+ }) expiredLocal))
702-- ============================================
688-- ============================================
703-- Forward retries
689-- Forward retries
@@ -716,11 +702,8 @@ retryPendingForwards = do
717 fwds <- *forwards
703 fwds <- *forwards
718 failures <- *forwardFailures
704 failures <- *forwardFailures
719− forEach (filter (collectPeers fwds (\f -> f.toServer)) (\s -> shouldRetryServer s t failures)) (\server -> forEach (takeRelation maxBatchSize (filter fwds (\f -> f.toServer == server))) (\f -> do
705+ forEach (filter (\s -> shouldRetryServer s t failures) (collectPeers fwds (\f -> f.toServer))) (\server -> forEach (takeRelation maxBatchSize (filter (\f -> f.toServer == server) fwds)) (\f -> do
720− result <- fetch (federationUrl server) (ForwardMessage {
706+ result <- fetch (federationUrl server) (ForwardMessage {f.toKey, f.encryptedBlob})
724 success <- case result of
707 success <- case result of
725 Err _ -> do
708 Err _ -> do
726 recordFailure server
709 recordFailure server
@@ -732,22 +715,22 @@ retryPendingForwards = do
732 let isDone = \x -> isMatch x && (success || x.retries + 1 >= maxForwardRetries)
715 let isDone = \x -> isMatch x && (success || x.retries + 1 >= maxForwardRetries)
733 atomic (do
716 atomic (do
734 cur <- *forwards
717 cur <- *forwards
735− *forwards = pluck (removeWhere cur isDone) (\x -> if isMatch x
718+ *forwards = map (\x -> if isMatch x
736 then {x.toServer, x.toKey, x.encryptedBlob, retries: x.retries + 1}
719 then {x.toServer, x.toKey, x.encryptedBlob, retries: x.retries + 1}
737− else x))))
720+ else x) (filter (\x -> not isDone x) cur))))
739-- Forward queued messages to a server when a remote key comes online.
722-- Forward queued messages to a server when a remote key comes online.
740forwardQueuedToServer : PubkeyHex -> ServerName -> IO {rw *forwards, r *messages} {}
723forwardQueuedToServer : PubkeyHex -> ServerName -> IO {rw *forwards, r *messages} {}
741forwardQueuedToServer = \pubkey server -> atomic (do
724forwardQueuedToServer = \pubkey server -> atomic (do
742 msgs <- *messages
725 msgs <- *messages
743 fwds <- *forwards
726 fwds <- *forwards
744− let candidates = filter msgs (\m -> m.toKey == pubkey && none fwds (\f -> f.encryptedBlob == m.encryptedBlob && f.toServer == server))
727+ let candidates = filter (\m -> m.toKey == pubkey && none fwds (\f -> f.encryptedBlob == m.encryptedBlob && f.toServer == server)) msgs
745− let newFwds = pluck (takeRelation maxForwardsPerServer candidates) (\m -> {
728+ let newFwds = map (\m -> {
746 toServer: server,
729 toServer: server,
748 m.encryptedBlob,
731 m.encryptedBlob,
749 retries: 0
732 retries: 0
750− })
733+ }) (takeRelation maxForwardsPerServer candidates)
751 *forwards = union fwds newFwds)
734 *forwards = union fwds newFwds)
753-- ============================================
736-- ============================================
@@ -789,7 +772,7 @@ handleChallenge = \pubkey clientIp -> do
789 challenge <- genToken
772 challenge <- genToken
790 result <- atomic (do
773 result <- atomic (do
791 ch <- *challenges
774 ch <- *challenges
792− let cappedActive = capPerKey (filter ch (\c -> c.expiresAt > t)) (\c -> c.pubkey == pubkey) maxChallengesPerKey (\c -> c.expiresAt)
775+ let cappedActive = capPerKey (filter (\c -> c.expiresAt > t) ch) (\c -> c.pubkey == pubkey) maxChallengesPerKey (\c -> c.expiresAt)
793 if count cappedActive >= maxChallenges
776 if count cappedActive >= maxChallenges
794 then yield "capacity"
777 then yield "capacity"
@@ -832,16 +815,16 @@ handleVerifyAuth = \pubkey challenge signature revokeOthers clientIp -> do
832 if not valid
815 if not valid
833 then yield {valid: False {}, peers: []}
816 then yield {valid: False {}, peers: []}
835− *challenges = removeWhere ch same
818+ *challenges = filter (\c -> not same c) ch
836 ses <- *sessions
819 ses <- *sessions
837 let mine = \s -> s.pubkey == pubkey
820 let mine = \s -> s.pubkey == pubkey
838 let pruned = if revokeOthers
821 let pruned = if revokeOthers
839− then removeWhere ses mine
822+ then filter (\s -> not mine s) ses
840 else capPerKey ses mine maxSessionsPerKey (\s -> s.expiresAt)
823 else capPerKey ses mine maxSessionsPerKey (\s -> s.expiresAt)
841 let newSession = {token, pubkey, expiresAt: expiry, ip: clientIp}
824 let newSession = {token, pubkey, expiresAt: expiry, ip: clientIp}
842 *sessions = capList (union pruned [newSession]) maxSessions (\s -> s.expiresAt)
825 *sessions = capList (union pruned [newSession]) maxSessions (\s -> s.expiresAt)
843 pres <- *presence
826 pres <- *presence
844− let prunedPres = removeWhere pres (\p -> p.pubkey == pubkey && isLocalServer p.server)
827+ let prunedPres = filter (\p -> not (p.pubkey == pubkey && isLocalServer p.server)) pres
845 let newPres = union prunedPres [{pubkey, server: serverHost, expiresAt: expiry}]
828 let newPres = union prunedPres [{pubkey, server: serverHost, expiresAt: expiry}]
846 *presence = newPres
829 *presence = newPres
847 yield {valid: True {}, peers: knownPeerServersFromPresence newPres})
830 yield {valid: True {}, peers: knownPeerServersFromPresence newPres})
@@ -881,20 +864,22 @@ pollOnce = \authorization cursor clientIp t -> do
881 startWakeup <- *pollWakeup
864 startWakeup <- *pollWakeup
882 pres <- *presence
865 pres <- *presence
883 let mine = \p -> p.pubkey == pubkey && isLocalServer p.server
866 let mine = \p -> p.pubkey == pubkey && isLocalServer p.server
884− let stale = not (any pres (\p -> mine p && p.expiresAt - t >= sessionExpiry / 2))
867+ let stale = not any pres (\p -> mine p && p.expiresAt - t >= sessionExpiry / 2)
885 refreshPeers <- if stale
868 refreshPeers <- if stale
887− let kept = removeWhere pres mine
870+ let kept = filter (\p -> not mine p) pres
888− let newPres = union kept [{pubkey, server: serverHost, expiresAt: t + sessionExpiry}]
871+ let newPres = union kept [{pubkey, server: serverHost, expiresAt: t + sessionExpiry}]
889− *presence = newPres
872+ *presence = newPres
890− yield (Just {value: knownPeerServersFromPresence newPres})
873+ yield (Just {value: knownPeerServersFromPresence newPres})
891− else yield (Nothing {})
874+ else yield (Nothing {})
892 yield (Just {value: {pubkey, clampedCursor, startWakeup, refreshPeers}}))
875 yield (Just {value: {pubkey, clampedCursor, startWakeup, refreshPeers}}))
893 case setup of
876 case setup of
894 Nothing {} -> yield (Err {error: {status: 401, message: "unauthorized"}})
877 Nothing {} -> yield (Err {error: {status: 401, message: "unauthorized"}})
895 Just {value: s} -> do
878 Just {value: s} -> do
896 case s.refreshPeers of
879 case s.refreshPeers of
897− Just {value: peers} -> when federationEnabled (fork (sendBatchedGossip peers [GossipOnline {pubkey: s.pubkey}]))
880+ Just {value: peers} -> when federationEnabled (fork (sendBatchedGossip peers [
881+ GossipOnline {s.pubkey}
898 Nothing {} -> yield {}
883 Nothing {} -> yield {}
899 pollResult <- doPoll s t
884 pollResult <- doPoll s t
900 yield (Ok {value: {pollResult.events, pollResult.cursor}})
885 yield (Ok {value: {pollResult.events, pollResult.cursor}})
@@ -924,7 +909,7 @@ doPoll = \s t -> do
924-- which the client echoes on the next /poll to advance — and which the
909-- which the client echoes on the next /poll to advance — and which the
925-- server treats as an implicit ack.
910-- server treats as an implicit ack.
926collectPollEvents : [Message] -> PubkeyHex -> Timestamp -> Timestamp -> {events: [PollEvent], cursor: Timestamp}
911collectPollEvents : [Message] -> PubkeyHex -> Timestamp -> Timestamp -> {events: [PollEvent], cursor: Timestamp}
927−collectPollEvents = \messages pubkey cursor t -> (\limited -> {events: pluck limited (\m -> {m.encryptedBlob}), cursor: fold (\acc m -> max acc m.receivedAt) cursor limited}) (takeRelation maxPollEvents (sortBy (\m -> m.receivedAt) (filter messages (\m -> m.toKey == pubkey && t - m.receivedAt < messageMaxAge && m.receivedAt > cursor))))
912+collectPollEvents = \messages pubkey cursor t -> (\limited -> {events: map (\m -> {m.encryptedBlob}) limited, cursor: fold (\acc m -> max acc m.receivedAt) cursor limited}) (takeRelation maxPollEvents (sortBy (\m -> m.receivedAt) (filter (\m -> m.toKey == pubkey && t - m.receivedAt < messageMaxAge && m.receivedAt > cursor) messages)))
929-- Treat the supplied cursor as an ack: drop all messages with
914-- Treat the supplied cursor as an ack: drop all messages with
930-- receivedAt <= cursor, plus their pending forwards.
915-- receivedAt <= cursor, plus their pending forwards.
@@ -932,11 +917,11 @@ ackPolledMessages : PubkeyHex -> Int -> IO {rw *forwards, rw *messages} {}
932ackPolledMessages = \pubkey cursor -> do
917ackPolledMessages = \pubkey cursor -> do
933 msgs <- *messages
918 msgs <- *messages
934 let matches = \m -> m.toKey == pubkey && m.receivedAt <= cursor
919 let matches = \m -> m.toKey == pubkey && m.receivedAt <= cursor
935− let toAck = filter msgs matches
920+ let toAck = filter matches msgs
936− let blobs = pluck toAck (\m -> m.encryptedBlob)
921+ let blobs = map (\m -> m.encryptedBlob) toAck
937− *messages = removeWhere msgs matches
922+ *messages = filter (\m -> not matches m) msgs
938 fwds <- *forwards
923 fwds <- *forwards
939− *forwards = filter fwds (\f -> not (f.toKey == pubkey && elem f.encryptedBlob blobs))
924+ *forwards = filter (\f -> not (f.toKey == pubkey && elem f.encryptedBlob blobs)) fwds
941-- ============================================
926-- ============================================
942-- Handler: send
927-- Handler: send
@@ -1036,24 +1021,21 @@ forkForwardsTo = \to encryptedBlob servers -> forEach servers (\server -> do
1036 Nothing {} -> yield {}
1021 Nothing {} -> yield {}
1037 Just {value: f} -> do
1022 Just {value: f} -> do
1038 t <- now
1023 t <- now
1039− result <- fetch (federationUrl server) (ForwardMessage {
1024+ result <- fetch (federationUrl server) (ForwardMessage {toKey: to, encryptedBlob})
1043 case result of
1025 case result of
1044 Err _ -> recordFailure server
1026 Err _ -> recordFailure server
1045 Ok _ -> do
1027 Ok _ -> do
1046 clearFailure server
1028 clearFailure server
1047 atomic (do
1029 atomic (do
1048 fwds2 <- *forwards
1030 fwds2 <- *forwards
1049− let remaining = filter fwds2 (\x -> not same x)
1031+ let remaining = filter (\x -> not same x) fwds2
1050 *forwards = remaining
1032 *forwards = remaining
1051 pres <- *presence
1033 pres <- *presence
1052 let stillLocal = any pres (\p -> p.pubkey == to && isLocalServer p.server && p.expiresAt > t)
1034 let stillLocal = any pres (\p -> p.pubkey == to && isLocalServer p.server && p.expiresAt > t)
1053 let othersPending = any remaining (\x -> x.encryptedBlob == encryptedBlob)
1035 let othersPending = any remaining (\x -> x.encryptedBlob == encryptedBlob)
1054 when (not stillLocal && not othersPending) (do
1036 when (not stillLocal && not othersPending) (do
1055 msgs <- *messages
1037 msgs <- *messages
1056− *messages = filter msgs (\m -> m.encryptedBlob != encryptedBlob))))
1038+ *messages = filter (\m -> m.encryptedBlob != encryptedBlob) msgs)))
1058-- ============================================
1040-- ============================================
1059-- Handler: federation receive
1041-- Handler: federation receive
@@ -1086,12 +1068,12 @@ handleRecvGossip = \events fromServer -> do
1086 GossipOnline {pubkey} -> pubkey
1068 GossipOnline {pubkey} -> pubkey
1087 GossipOffline {pubkey} -> pubkey
1069 GossipOffline {pubkey} -> pubkey
1089− let onlinePubkeys = pluck (filter events isOnline) pubkeyOf
1071+ let onlinePubkeys = map pubkeyOf (filter isOnline events)
1090− let touchedPubkeys = pluck (filter events (\e -> isOnline e || isOffline e)) pubkeyOf
1072+ let touchedPubkeys = map pubkeyOf (filter (\e -> isOnline e || isOffline e) events)
1091− let kept = filter pres (\p -> p.server != fromServer || not elem p.pubkey touchedPubkeys)
1073+ let kept = filter (\p -> p.server != fromServer || not elem p.pubkey touchedPubkeys) pres
1092 let added = map (\pk -> {pubkey: pk, server: fromServer, expiresAt: t + onlineGossipTtl}) onlinePubkeys
1074 let added = map (\pk -> {pubkey: pk, server: fromServer, expiresAt: t + onlineGossipTtl}) onlinePubkeys
1093 *presence = union kept added
1075 *presence = union kept added
1094− yield (filter onlinePubkeys (\pk -> none pres (\p -> p.pubkey == pk && p.server == fromServer && p.expiresAt > t))))
1076+ yield (filter (\pk -> none pres (\p -> p.pubkey == pk && p.server == fromServer && p.expiresAt > t)) onlinePubkeys))
1095 fork (forEach newlyOnline (\pk -> forwardQueuedToServer pk fromServer))
1077 fork (forEach newlyOnline (\pk -> forwardQueuedToServer pk fromServer))
1096 yield (Ok {value: {}})
1078 yield (Ok {value: {}})