on master help sign in
Extract helpers and refine type safety in server handlers
6f5e820iko
client/App.fs+48−49
@@ -20,6 +20,12 @@ module App =
20 Timestamp: DateTimeOffset
20 Timestamp: DateTimeOffset
21 IsOutgoing: bool }
21 IsOutgoing: bool }
22
22
23+ type IncomingMessage =
24+ { FromPubkey: string
25+ Body: string
26+ Timestamp: int64
27+ MessageId: string }
28+
23 type Page =
29 type Page =
24 | Setup
30 | Setup
25 | Conversations
31 | Conversations
@@ -68,7 +74,7 @@ module App =
68 | SetNewNickname of string
74 | SetNewNickname of string
69 | DoSaveContact
75 | DoSaveContact
70 | DoDeleteContact of string
76 | DoDeleteContact of string
71− | PollResult of (string * string * int64 * string) list * string * uint64
77+ | PollResult of IncomingMessage list * string * uint64
72 | DoPoll
78 | DoPoll
73 | CopyPubKey
79 | CopyPubKey
74 | DismissError
80 | DismissError
@@ -78,14 +84,7 @@ module App =
78 type CmdMsg =
84 type CmdMsg =
79 | CmdGenIdentity
85 | CmdGenIdentity
80 | CmdConnect of url: string * pubHex: string * privKey: byte[]
86 | CmdConnect of url: string * pubHex: string * privKey: byte[]
81− | CmdSend of
87+ | CmdSend of url: string * token: string * recipientHex: string * privKey: byte[] * text: string
82− url: string *
83− token: string *
84− toHex: string *
85− privKey: byte[] *
86− pubKeyHex: string *
87− recipPubHex: string *
88− text: string
89 | CmdPoll of url: string * token: string * privKey: byte[] * cursor: uint64
88 | CmdPoll of url: string * token: string * privKey: byte[] * cursor: uint64
90 | CmdCopyToClipboard of string
89 | CmdCopyToClipboard of string
91
90
@@ -100,6 +99,19 @@ module App =
100 |> Option.map _.Nickname
99 |> Option.map _.Nickname
101 |> Option.defaultValue (truncKey pk)
100 |> Option.defaultValue (truncKey pk)
102
101
102+ let private messagesFor (pk: string) (messages: Map<string, ChatMessage list>) =
103+ messages |> Map.tryFind pk |> Option.defaultValue []
104+
105+ let private addContactIfNew (contact: Contact) (contacts: Contact list) =
106+ if contacts |> List.exists (fun c -> c.Pubkey = contact.Pubkey) then contacts
107+ else contacts @ [ contact ]
108+
109+ let private upsertContact (contact: Contact) (contacts: Contact list) =
110+ if contacts |> List.exists (fun c -> c.Pubkey = contact.Pubkey) then
111+ contacts |> List.map (fun c -> if c.Pubkey = contact.Pubkey then contact else c)
112+ else
113+ contacts @ [ contact ]
114+
103 // ── Init ──
115 // ── Init ──
104
116
105 let init () =
117 let init () =
@@ -143,11 +155,12 @@ module App =
143 | None -> { model with Error = Some "No identity generated" }, []
155 | None -> { model with Error = Some "No identity generated" }, []
144
156
145 | AuthOk token ->
157 | AuthOk token ->
146− { model with
158+ match model.PrivKey with
147− Conn = Online token
159+ | Some priv ->
148− Page = Conversations
160+ { model with Conn = Online token; Page = Conversations; Error = None },
149− Error = None },
150− [ CmdPoll(model.ServerUrl, token, model.PrivKey.Value, model.PollCursor) ]
161+ [ CmdPoll(model.ServerUrl, token, priv, model.PollCursor) ]
162+ | None ->
163+ { model with Error = Some "No identity generated" }, []
151
164
152 | AuthErr err -> { model with Conn = Offline; Error = Some err }, []
165 | AuthErr err -> { model with Conn = Offline; Error = Some err }, []
153
166
@@ -164,12 +177,12 @@ module App =
164 Body = text
177 Body = text
165 Timestamp = DateTimeOffset.UtcNow
178 Timestamp = DateTimeOffset.UtcNow
166 IsOutgoing = true }
179 IsOutgoing = true }
167− let msgs = model.Messages |> Map.tryFind pk |> Option.defaultValue []
180+ let msgs = messagesFor pk model.Messages
168
181
169 { model with
182 { model with
170 ComposeText = ""
183 ComposeText = ""
171 Messages = model.Messages |> Map.add pk (msgs @ [ outMsg ]) },
184 Messages = model.Messages |> Map.add pk (msgs @ [ outMsg ]) },
172− [ CmdSend(model.ServerUrl, token, pk, priv, model.PubKeyHex, pk, text) ]
185+ [ CmdSend(model.ServerUrl, token, pk, priv, text) ]
173 | _ -> model, []
186 | _ -> model, []
174
187
175 | Sent(_, status) -> { model with LastSendStatus = status }, []
188 | Sent(_, status) -> { model with LastSendStatus = status }, []
@@ -178,31 +191,25 @@ module App =
178 let model' =
191 let model' =
179 incoming
192 incoming
180 |> List.fold
193 |> List.fold
181− (fun m (fromPk, body, ts, id) ->
194+ (fun m msg ->
182− let msgs = m.Messages |> Map.tryFind fromPk |> Option.defaultValue []
195+ let msgs = messagesFor msg.FromPubkey m.Messages
183
196
184− if msgs |> List.exists (fun msg -> msg.Id = id) then
197+ if msgs |> List.exists (fun x -> x.Id = msg.MessageId) then
185 m
198 m
186 else
199 else
187− let newMsg =
200+ let chatMsg =
188− { Id = id
201+ { Id = msg.MessageId
189− Body = body
202+ Body = msg.Body
190− Timestamp = DateTimeOffset.FromUnixTimeSeconds(ts)
203+ Timestamp = DateTimeOffset.FromUnixTimeSeconds(msg.Timestamp)
191 IsOutgoing = false }
204 IsOutgoing = false }
192
205
193− let contacts =
194− if m.Contacts |> List.exists (fun c -> c.Pubkey = fromPk) then
195− m.Contacts
196− else
197− m.Contacts @ [ { Pubkey = fromPk; Nickname = truncKey fromPk } ]
198−
199 { m with
206 { m with
200− Contacts = contacts
207+ Contacts = m.Contacts |> addContactIfNew { Pubkey = msg.FromPubkey; Nickname = truncKey msg.FromPubkey }
201− Messages = m.Messages |> Map.add fromPk (msgs @ [ newMsg ]) })
208+ Messages = m.Messages |> Map.add msg.FromPubkey (msgs @ [ chatMsg ]) })
202 { model with PollStatus = status; PollCursor = newCursor }
209 { model with PollStatus = status; PollCursor = newCursor }
203
210
204− match model'.Conn with
211+ match model'.Conn, model'.PrivKey with
205− | Online token -> model', [ CmdPoll(model'.ServerUrl, token, model'.PrivKey.Value, model'.PollCursor) ]
212+ | Online token, Some priv -> model', [ CmdPoll(model'.ServerUrl, token, priv, model'.PollCursor) ]
206 | _ -> model', []
213 | _ -> model', []
207
214
208 | DoPoll ->
215 | DoPoll ->
@@ -219,16 +226,8 @@ module App =
219 { Pubkey = model.NewPubkey
226 { Pubkey = model.NewPubkey
220 Nickname = model.NewNickname }
227 Nickname = model.NewNickname }
221
228
222− let contacts =
223− if model.Contacts |> List.exists (fun c -> c.Pubkey = model.NewPubkey) then
224− model.Contacts
225− |> List.map (fun c ->
226− if c.Pubkey = model.NewPubkey then contact else c)
227− else
228− model.Contacts @ [ contact ]
229−
230 { model with
229 { model with
231− Contacts = contacts
230+ Contacts = model.Contacts |> upsertContact contact
232 NewPubkey = ""
231 NewPubkey = ""
233 NewNickname = ""
232 NewNickname = ""
234 Page = Conversations },
233 Page = Conversations },
@@ -279,11 +278,11 @@ module App =
279 return AuthErr ex.Message
278 return AuthErr ex.Message
280 })
279 })
281
280
282− | CmdSend(url, token, toHex, privKey, pubKeyHex, recipPubHex, text) ->
281+ | CmdSend(url, token, recipientHex, privKey, text) ->
283 asyncCmd (async {
282 asyncCmd (async {
284 try
283 try
285− let pubKey = Crypto.fromHex pubKeyHex
284+ let pubKey = privKey.[32..63]
286− let recipPub = Crypto.fromHex recipPubHex
285+ let recipPub = Crypto.fromHex recipientHex
287 let id = Guid.NewGuid().ToString()
286 let id = Guid.NewGuid().ToString()
288 let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
287 let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
289
288
@@ -292,8 +291,8 @@ module App =
292
291
293 let blob = Crypto.encrypt privKey pubKey recipPub payload
292 let blob = Crypto.encrypt privKey pubKey recipPub payload
294 let blobHex = Crypto.toHex blob
293 let blobHex = Crypto.toHex blob
295− let! (status, msgId) = ApiClient.sendMessage url token toHex blobHex ts
294+ let! (status, msgId) = ApiClient.sendMessage url token recipientHex blobHex ts
296− return Sent(toHex, $"{status}:{msgId}")
295+ return Sent(recipientHex, $"{status}:{msgId}")
297 with ex ->
296 with ex ->
298 return DismissError // TODO: show send errors properly
297 return DismissError // TODO: show send errors properly
299 })
298 })
@@ -326,7 +325,7 @@ module App =
326 if ptRoot.GetProperty("type").GetString() = "text" then
325 if ptRoot.GetProperty("type").GetString() = "text" then
327 let msgId = ptRoot.GetProperty("id").GetString()
326 let msgId = ptRoot.GetProperty("id").GetString()
328 let body = ptRoot.GetProperty("body").GetString()
327 let body = ptRoot.GetProperty("body").GetString()
329− messages.Add((senderHex, body, ts, msgId))
328+ messages.Add({ FromPubkey = senderHex; Body = body; Timestamp = ts; MessageId = msgId })
330
329
331 ackIds.Add(evtId)
330 ackIds.Add(evtId)
332 | None ->
331 | None ->
@@ -478,7 +477,7 @@ module App =
478
477
479 let private viewChat model pk =
478 let private viewChat model pk =
480 let name = contactName model.Contacts pk
479 let name = contactName model.Contacts pk
481− let msgs = model.Messages |> Map.tryFind pk |> Option.defaultValue []
480+ let msgs = messagesFor pk model.Messages
482
481
483 ContentPage(
482 ContentPage(
484 (VStack(spacing = 8.) {
483 (VStack(spacing = 8.) {
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+ac8c87f242c436f4a1bad89ff035eecf82a2bb15")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+82fcdc7935484a61c2a22433c36ce8637ad179c8")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−fb7049c87bbc40133ce9fcdb916d1edeb0d2c72163aa8e3476623d121e5e3788
1+0a759b94d305484f3281f9f60b3791a1832d7dde46f8f7a622e4deaf1e156292
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/ac8c87f242c436f4a1bad89ff035eecf82a2bb15/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/82fcdc7935484a61c2a22433c36ce8637ad179c8/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.

server.knot+22−27
@@ -41,7 +41,7 @@ federationEnabled = not (federationSecret == "")
41-- Types
41-- Types
42-- ============================================
42-- ============================================
43
43
44−type PollEvent = {id: Text, eventType: Text, payload: {encryptedBlob: Text, fromKey: Text, timestamp: Timestamp, toKey: Text}}
44+type PollEvent = {id: MessageIdHex, eventType: Text, payload: {encryptedBlob: BlobHex, fromKey: PubkeyHex, timestamp: Timestamp, toKey: PubkeyHex}}
45
45
46type GossipEventType = Text where \s -> s == "online" || s == "offline"
46type GossipEventType = Text where \s -> s == "online" || s == "offline"
47
47
@@ -379,6 +379,12 @@ clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresen
379
379
380clampTimestamp = \timestamp t -> if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
380clampTimestamp = \timestamp t -> if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
381
381
382+nextSeqTs = \t seqCounter -> if t > seqCounter then t else seqCounter + 1
383+
384+isFederatedOrigin = \originServer -> not (originServer == "") && not (originServer == serverHostN)
385+
386+takeN = \items n -> fold (\acc e -> if count acc >= n then acc else union acc [e]) [] items
387+
382dedupMessageIds = \messageIds ->
388dedupMessageIds = \messageIds ->
383 fold (\acc mid ->
389 fold (\acc mid ->
384 let nmid = normalizeHex mid in
390 let nmid = normalizeHex mid in
@@ -544,14 +550,12 @@ maybePrune = \t -> do
544 set *lastPrunedAt = t
550 set *lastPrunedAt = t
545 pruneExpired t
551 pruneExpired t
546
552
547−sendBatchedGossipToServer : Text -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int}] -> IO {network} {}
553+sendBatchedGossipToServer : Text -> [{eventType: GossipEventType, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}] -> IO {network} {}
548sendBatchedGossipToServer = \server allEvents ->
554sendBatchedGossipToServer = \server allEvents ->
549 if count allEvents == 0
555 if count allEvents == 0
550 then yield {}
556 then yield {}
551 else do
557 else do
552− let batch = fold (\acc e ->
553− if count acc >= maxGossipEvents then acc
558+ let batch = takeN allEvents maxGossipEvents
554− else union acc [e]) [] allEvents
555 let rest = diff allEvents batch
559 let rest = diff allEvents batch
556 batchResult <- fetch (federationScheme ++ server) (RecvGossip {
560 batchResult <- fetch (federationScheme ++ server) (RecvGossip {
557 authorization: "Bearer " ++ federationSecret,
561 authorization: "Bearer " ++ federationSecret,
@@ -561,7 +565,7 @@ sendBatchedGossipToServer = \server allEvents ->
561 Err _ -> yield {}
565 Err _ -> yield {}
562 sendBatchedGossipToServer server rest
566 sendBatchedGossipToServer server rest
563
567
564−sendBatchedGossip : [Text] -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int}] -> IO {network} {}
568+sendBatchedGossip : [Text] -> [{eventType: GossipEventType, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}] -> IO {network} {}
565sendBatchedGossip = \peerServers allEvents ->
569sendBatchedGossip = \peerServers allEvents ->
566 if count allEvents == 0
570 if count allEvents == 0
567 then yield {}
571 then yield {}
@@ -582,7 +586,7 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
582 sendBatchedGossip peerServers gossipEvents
586 sendBatchedGossip peerServers gossipEvents
583 else yield {}
587 else yield {}
584
588
585−retryForwardsForServer : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Timestamp, toKey: Text, toServer: Text}] -> Int -> IO {network} {}
589+retryForwardsForServer : [{encryptedBlob: BlobHex, fromKey: PubkeyHex, messageId: MessageIdHex, timestamp: Timestamp, toKey: PubkeyHex, toServer: ServerName}] -> Int -> IO {network} {}
586retryForwardsForServer = \entries remaining ->
590retryForwardsForServer = \entries remaining ->
587 if count entries == 0 || remaining <= 0
591 if count entries == 0 || remaining <= 0
588 then yield {}
592 then yield {}
@@ -608,21 +612,19 @@ retryForwardsForServer = \entries remaining ->
608 Err _ -> yield {}
612 Err _ -> yield {}
609 retryForwardsForServer rest (remaining - 1)
613 retryForwardsForServer rest (remaining - 1)
610
614
611−retryPendingForwards : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Timestamp, toKey: Text, toServer: Text}] -> IO {network} {}
615+retryPendingForwards : [{encryptedBlob: BlobHex, fromKey: PubkeyHex, messageId: MessageIdHex, timestamp: Timestamp, toKey: PubkeyHex, toServer: ServerName}] -> IO {network} {}
612retryPendingForwards = \pending -> do
616retryPendingForwards = \pending -> do
613 let servers = collectPeerServers pending (\x -> x.toServer)
617 let servers = collectPeerServers pending (\x -> x.toServer)
614 sendToEach servers (\server ->
618 sendToEach servers (\server ->
615 retryForwardsForServer (do x <- pending; where x.toServer == server; yield x) maxForwardRetriesPerServer)
619 retryForwardsForServer (do x <- pending; where x.toServer == server; yield x) maxForwardRetriesPerServer)
616 yield {}
620 yield {}
617
621
618−sendBatchedAcksToServer : [{messageId: Text, toKey: Text, originServer: Text, | a}] -> Text -> IO {network} {}
622+sendBatchedAcksToServer : [{messageId: MessageIdHex, toKey: PubkeyHex, originServer: Text, | a}] -> Text -> IO {network} {}
619sendBatchedAcksToServer = \serverAcks server ->
623sendBatchedAcksToServer = \serverAcks server ->
620 if count serverAcks == 0
624 if count serverAcks == 0
621 then yield {}
625 then yield {}
622 else do
626 else do
623− let batch = fold (\acc a ->
627+ let batch = takeN serverAcks maxMessageIds
624− if count acc >= maxMessageIds then acc
625− else union acc [a]) [] serverAcks
626 let rest = diff serverAcks batch
628 let rest = diff serverAcks batch
627 result <- fetch (federationScheme ++ server) (FederationAck {
629 result <- fetch (federationScheme ++ server) (FederationAck {
628 authorization: "Bearer " ++ federationSecret,
630 authorization: "Bearer " ++ federationSecret,
@@ -639,7 +641,7 @@ sendBatchedAcksToServer = \serverAcks server ->
639 Err _ -> yield {}
641 Err _ -> yield {}
640 sendBatchedAcksToServer rest server
642 sendBatchedAcksToServer rest server
641
643
642−sendAcksAndClean : [{messageId: Text, toKey: Text, originServer: Text, | a}] -> [Text] -> IO {network} {}
644+sendAcksAndClean : [{messageId: MessageIdHex, toKey: PubkeyHex, originServer: Text, | a}] -> [Text] -> IO {network} {}
643sendAcksAndClean = \pendingAcks servers ->
645sendAcksAndClean = \pendingAcks servers ->
644 sendToEach servers (\server ->
646 sendToEach servers (\server ->
645 sendBatchedAcksToServer (do a <- pendingAcks; where a.originServer == server; yield a) server)
647 sendBatchedAcksToServer (do a <- pendingAcks; where a.originServer == server; yield a) server)
@@ -655,8 +657,7 @@ pruneAndRetry = \t -> do
655 ackedIds <- *ackedIds
657 ackedIds <- *ackedIds
656 let pendingAcks = do
658 let pendingAcks = do
657 a <- ackedIds
659 a <- ackedIds
658− where not (a.originServer == "")
660+ where isFederatedOrigin a.originServer
659− where not (a.originServer == serverHostN)
660 yield a
661 yield a
661 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: pendingForwards, pendingAcks: pendingAcks}
662 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: pendingForwards, pendingAcks: pendingAcks}
662 broadcastOfflineGossip t result.peerServers result.expiredLocal
663 broadcastOfflineGossip t result.peerServers result.expiredLocal
@@ -832,7 +833,7 @@ serve = \req -> case req of
832 Just {value: s} -> s.pubkey
833 Just {value: s} -> s.pubkey
833 let authorized = isJust session
834 let authorized = isJust session
834 seqCounter <- *seqCounter
835 seqCounter <- *seqCounter
835− let seqTs = if t > seqCounter then t else seqCounter + 1
836+ let seqTs = nextSeqTs t seqCounter
836 let recipientMsgCount = countMessagesFor messages to t
837 let recipientMsgCount = countMessagesFor messages to t
837 let shouldInsert = authorized && recipientMsgCount < maxMessagesPerRecipient
838 let shouldInsert = authorized && recipientMsgCount < maxMessagesPerRecipient
838 set *seqCounter = if shouldInsert then seqTs else seqCounter
839 set *seqCounter = if shouldInsert then seqTs else seqCounter
@@ -858,9 +859,7 @@ serve = \req -> case req of
858 forwards <- *forwards
859 forwards <- *forwards
859 let forwardsAtCapacity = count forwards >= maxForwardsEntries
860 let forwardsAtCapacity = count forwards >= maxForwardsEntries
860 let forwardSlots = maxForwardsEntries - count forwards
861 let forwardSlots = maxForwardsEntries - count forwards
861− let forwardServers = fold (\acc s ->
862+ let forwardServers = takeN remoteServers forwardSlots
862− if count acc >= forwardSlots then acc
863− else union acc [s]) [] remoteServers
864 set *forwards = if shouldInsert && not forwardsAtCapacity && count forwardServers > 0 && federationEnabled
863 set *forwards = if shouldInsert && not forwardsAtCapacity && count forwardServers > 0 && federationEnabled
865 then union forwards (do s <- forwardServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
864 then union forwards (do s <- forwardServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
866 else forwards
865 else forwards
@@ -910,8 +909,7 @@ serve = \req -> case req of
910 Just {value: _} -> diff messages ackedMessages
909 Just {value: _} -> diff messages ackedMessages
911 let ackedFederated = do
910 let ackedFederated = do
912 m <- ackedMessages
911 m <- ackedMessages
913− where not (m.originServer == "")
912+ where isFederatedOrigin m.originServer
914− where not (m.originServer == serverHostN)
915 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t, originServer: m.originServer}
913 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t, originServer: m.originServer}
916 ackedIds <- *ackedIds
914 ackedIds <- *ackedIds
917 let combinedAckedIds = union ackedIds ackedFederated
915 let combinedAckedIds = union ackedIds ackedFederated
@@ -925,8 +923,7 @@ serve = \req -> case req of
925 yield {ok: ok, ackedMessages: ackedMessages, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, evictedEntries: evictedEntries}
923 yield {ok: ok, ackedMessages: ackedMessages, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, evictedEntries: evictedEntries}
926 let fedMessages = do
924 let fedMessages = do
927 m <- result.ackedMessages
925 m <- result.ackedMessages
928− where not (m.originServer == "")
926+ where isFederatedOrigin m.originServer
929− where not (m.originServer == serverHostN)
930 yield m
927 yield m
931 let originServers = collectPeerServers fedMessages (\m -> m.originServer)
928 let originServers = collectPeerServers fedMessages (\m -> m.originServer)
932 fork (do
929 fork (do
@@ -1063,9 +1060,7 @@ serve = \req -> case req of
1063 where none updatesOnlyAfterOffline (\p -> samePubkeyServer p e)
1060 where none updatesOnlyAfterOffline (\p -> samePubkeyServer p e)
1064 yield e
1061 yield e
1065 let reclaimedEntries = if withinCapacity then []
1062 let reclaimedEntries = if withinCapacity then []
1066− else fold (\acc e ->
1067− if count acc >= reclaimCapacity then acc
1063+ else takeN newEntries reclaimCapacity
1068− else union acc [e]) [] newEntries
1069 let afterReclaim = if withinCapacity then afterOffline
1064 let afterReclaim = if withinCapacity then afterOffline
1070 else union updatesOnlyAfterOffline reclaimedEntries
1065 else union updatesOnlyAfterOffline reclaimedEntries
1071 let appliedOnlineUpdates = if withinCapacity then appliedOnline
1066 let appliedOnlineUpdates = if withinCapacity then appliedOnline
@@ -1130,7 +1125,7 @@ serve = \req -> case req of
1130 pr <- maybePrune t
1125 pr <- maybePrune t
1131 messages <- *messages
1126 messages <- *messages
1132 seqCounter <- *seqCounter
1127 seqCounter <- *seqCounter
1133− let seqTs = if t > seqCounter then t else seqCounter + 1
1128+ let seqTs = nextSeqTs t seqCounter
1134 let recipientMsgCount = if not (authorized && validOrigin) then 0
1129 let recipientMsgCount = if not (authorized && validOrigin) then 0
1135 else countMessagesFor messages toKey t
1130 else countMessagesFor messages toKey t
1136 let existsInMessages = if not (authorized && validOrigin) then False {}
1131 let existsInMessages = if not (authorized && validOrigin) then False {}