@@ -40,9 +40,9 @@ maxForwardRetriesPerServer = 100
41-- ============================================
41-- ============================================
43−type PollEvent = {id: Text, eventType: Text, payload: {encryptedBlob: Text, fromKey: Text, timestamp: Int, toKey: Text}}
43+type PollEvent = {id: Text, eventType: Text, payload: {encryptedBlob: Text, fromKey: Text, timestamp: Timestamp, toKey: Text}}
45−type GossipEvent = {eventType: Text, pubkey: Text, server: Text, timestamp: Int, ttl: Int}
45+type GossipEvent = {eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}
47type PubkeyHex = Text where \s -> isValidHex s && not (tooLong s maxPubkeyLen)
47type PubkeyHex = Text where \s -> isValidHex s && not (tooLong s maxPubkeyLen)
48type BlobHex = Text where \s -> isValidHex s && not (tooLong s maxBlobLen)
48type BlobHex = Text where \s -> isValidHex s && not (tooLong s maxBlobLen)
@@ -50,6 +50,8 @@ type SignatureHex = Text where \s -> isValidHex s && not (tooLong s maxSignature
50type MessageIdHex = Text where \s -> isValidHex s && not (tooLong s maxMessageIdLen)
50type MessageIdHex = Text where \s -> isValidHex s && not (tooLong s maxMessageIdLen)
51type ServerName = Text where \s -> not (tooLong s maxServerNameLen) && not (isBadServerName s)
51type ServerName = Text where \s -> not (tooLong s maxServerNameLen) && not (isBadServerName s)
53+type Timestamp = Int<Ms>
53-- ============================================
55-- ============================================
54-- Persisted State
56-- Persisted State
55-- ============================================
57-- ============================================
@@ -58,13 +60,13 @@ type ServerName = Text where \s -> not (tooLong s maxServerNameLen) && not (isBa
59*sessions : [{token: Text, pubkey: Text, expiresAt: Int}]
61*sessions : [{token: Text, pubkey: Text, expiresAt: Int}]
61−*messages : [{messageId: Text, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Int, originServer: Text, receivedAt: Int}]
63+*messages : [{messageId: Text, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Timestamp, originServer: Text, receivedAt: Int}]
63−*presence : [{pubkey: Text, server: Text, expiresAt: Int, timestamp: Int}]
65+*presence : [{pubkey: Text, server: Text, expiresAt: Int, timestamp: Timestamp}]
65*seqCounter : Int
67*seqCounter : Int
67−*forwards : [{messageId: Text, toServer: Text, createdAt: Int, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Int}]
69+*forwards : [{messageId: Text, toServer: Text, createdAt: Int, toKey: Text, fromKey: Text, encryptedBlob: Text, timestamp: Timestamp}]
69*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int, originServer: Text}]
71*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int, originServer: Text}]
@@ -88,7 +90,7 @@ route ClientApi where
88 -> {events: [PollEvent], cursor: Int, authorized: Bool}
90 -> {events: [PollEvent], cursor: Int, authorized: Bool}
91− POST {to: PubkeyHex, encryptedBlob: BlobHex, timestamp: Int} / headers {authorization: Text}
93+ POST {to: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp} / headers {authorization: Text}
92 -> {status: Text, messageId: Text}
94 -> {status: Text, messageId: Text}
93 = SendMessage
95 = SendMessage
94 POST {messageIds: [MessageIdHex]} /ack headers {authorization: Text}
96 POST {messageIds: [MessageIdHex]} /ack headers {authorization: Text}
@@ -103,7 +105,7 @@ route FederationApi where
103 POST {events: [GossipEvent], hopsRemaining: Int, fromServer: ServerName} /gossip headers {authorization: Text}
105 POST {events: [GossipEvent], hopsRemaining: Int, fromServer: ServerName} /gossip headers {authorization: Text}
104 -> {ok: Bool}
106 -> {ok: Bool}
105 = RecvGossip
107 = RecvGossip
106− POST {toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Int, originServer: ServerName, messageId: MessageIdHex} /forward headers {authorization: Text}
108+ POST {toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp, originServer: ServerName, messageId: MessageIdHex} /forward headers {authorization: Text}
107 -> {ok: Bool}
109 -> {ok: Bool}
108 = ForwardMessage
110 = ForwardMessage
109 POST {messageIds: [Text], originServer: Text, fromServer: ServerName} /ack headers {authorization: Text}
111 POST {messageIds: [Text], originServer: Text, fromServer: ServerName} /ack headers {authorization: Text}
@@ -121,6 +123,9 @@ route Api = AuthApi | ClientApi | FederationApi | MaintenanceApi
121-- Helpers
123-- Helpers
122-- ============================================
124-- ============================================
126+msToS : Int<Ms> -> Int<S>
127+msToS = \x -> x / 1000
124hexDigit = \n ->
129hexDigit = \n ->
125 let n = if n < 0 then 0 else if n > 15 then 15 else n in
130 let n = if n < 0 then 0 else if n > 15 then 15 else n in
126 if n == 0 then "0" else if n == 1 then "1" else if n == 2 then "2" else if n == 3 then "3"
131 if n == 0 then "0" else if n == 1 then "1" else if n == 2 then "2" else if n == 3 then "3"
@@ -414,14 +419,12 @@ sendToEach = \servers action ->
414 sendToEach rest action
419 sendToEach rest action
416pruneExpired = \t -> do
421pruneExpired = \t -> do
417− challenges <- *challenges
418 set *challenges = do
422 set *challenges = do
419− c <- challenges
423+ c <- *challenges
420 where c.expiresAt > t
424 where c.expiresAt > t
422− sessions <- *sessions
423 set *sessions = do
426 set *sessions = do
424− s <- sessions
427+ s <- *sessions
425 where s.expiresAt > t
428 where s.expiresAt > t
427 presenceBefore <- *presence
430 presenceBefore <- *presence
@@ -434,19 +437,16 @@ pruneExpired = \t -> do
434 where p.expiresAt > t
437 where p.expiresAt > t
436 set *presence = presenceAfter
439 set *presence = presenceAfter
437− messages <- *messages
438 set *messages = do
440 set *messages = do
439− m <- messages
441+ m <- *messages
440 where m.receivedAt >= t - messageMaxAge
442 where m.receivedAt >= t - messageMaxAge
442− forwards <- *forwards
443 set *forwards = do
444 set *forwards = do
444− f <- forwards
445+ f <- *forwards
445 where f.createdAt >= t - messageMaxAge
446 where f.createdAt >= t - messageMaxAge
447− ackedIds <- *ackedIds
448 set *ackedIds = do
448 set *ackedIds = do
449− a <- ackedIds
449+ a <- *ackedIds
450 where a.ackedAt >= t - messageMaxAge
450 where a.ackedAt >= t - messageMaxAge
452 yield {expiredLocal: (do
452 yield {expiredLocal: (do
@@ -468,7 +468,7 @@ maybePrune = \t -> do
468 set *lastPrunedAt = t
468 set *lastPrunedAt = t
469 pruneExpired t
469 pruneExpired t
471−sendBatchedGossipToServer : Text -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Int, ttl: Int}] -> IO {network} {}
471+sendBatchedGossipToServer : Text -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int}] -> IO {network} {}
472sendBatchedGossipToServer = \server allEvents ->
472sendBatchedGossipToServer = \server allEvents ->
473 if count allEvents == 0
473 if count allEvents == 0
474 then yield {}
474 then yield {}
@@ -485,7 +485,7 @@ sendBatchedGossipToServer = \server allEvents ->
485 Err _ -> yield {}
485 Err _ -> yield {}
486 sendBatchedGossipToServer server rest
486 sendBatchedGossipToServer server rest
488−sendBatchedGossip : [Text] -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Int, ttl: Int}] -> IO {network} {}
488+sendBatchedGossip : [Text] -> [{eventType: Text, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int}] -> IO {network} {}
489sendBatchedGossip = \peerServers allEvents ->
489sendBatchedGossip = \peerServers allEvents ->
490 if count allEvents == 0
490 if count allEvents == 0
491 then yield {}
491 then yield {}
@@ -506,7 +506,7 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
506 sendBatchedGossip peerServers gossipEvents
506 sendBatchedGossip peerServers gossipEvents
507 else yield {}
507 else yield {}
509−retryForwardsForServer : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Int, toKey: Text, toServer: Text}] -> Int -> IO {network} {}
509+retryForwardsForServer : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Timestamp, toKey: Text, toServer: Text}] -> Int -> IO {network} {}
510retryForwardsForServer = \entries remaining ->
510retryForwardsForServer = \entries remaining ->
511 if count entries == 0 || remaining <= 0
511 if count entries == 0 || remaining <= 0
512 then yield {}
512 then yield {}
@@ -532,7 +532,7 @@ retryForwardsForServer = \entries remaining ->
532 Err _ -> yield {}
532 Err _ -> yield {}
533 retryForwardsForServer rest (remaining - 1)
533 retryForwardsForServer rest (remaining - 1)
535−retryPendingForwards : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Int, toKey: Text, toServer: Text}] -> IO {network} {}
535+retryPendingForwards : [{encryptedBlob: Text, fromKey: Text, messageId: Text, timestamp: Timestamp, toKey: Text, toServer: Text}] -> IO {network} {}
536retryPendingForwards = \pending -> do
536retryPendingForwards = \pending -> do
537 let servers = fold (\acc x ->
537 let servers = fold (\acc x ->
538 if count (do s <- acc; where s == x.toServer; yield s) > 0 then acc
538 if count (do s <- acc; where s == x.toServer; yield s) > 0 then acc
@@ -636,7 +636,7 @@ pollLoop = \token cursor deadline -> do
636 if result.authorized && count result.events == 0 && t < deadline
636 if result.authorized && count result.events == 0 && t < deadline
638 let remaining = deadline - t
638 let remaining = deadline - t
639− let sleepMs = if remaining > 500 then 500 else remaining
639+ let sleepMs = if remaining > 500 then 500<Ms> else remaining
640 sleep sleepMs
640 sleep sleepMs
641 pollLoop token cursor deadline
641 pollLoop token cursor deadline
642 else yield result
642 else yield result