@@ -10,7 +10,7 @@ serverHost = "localhost:8080"
10federationSecret = "" -- must be set to enable federation
10federationSecret = "" -- must be set to enable federation
11maintenanceSecret = "" -- set to enable /maintenance/prune independently of federation
11maintenanceSecret = "" -- set to enable /maintenance/prune independently of federation
12federationScheme = "https://" -- scheme for federation HTTP calls
12federationScheme = "https://" -- scheme for federation HTTP calls
13−messageMaxAge = 86400000 -- 24 hours in milliseconds
13+messageMaxAge = 2592000000 -- 30 days in milliseconds
14maxPresenceTtl = 3600 -- max presence TTL from gossip in seconds
14maxPresenceTtl = 3600 -- max presence TTL from gossip in seconds
15maxPubkeyLen = 256
15maxPubkeyLen = 256
16maxBlobLen = 65536
16maxBlobLen = 65536
@@ -60,6 +60,8 @@ type GossipEvent = {eventType: Text, pubkey: Text, server: Text, timestamp: Int,
61*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int}]
61*ackedIds : [{messageId: Text, toKey: Text, ackedAt: Int}]
63-- ============================================
65-- ============================================
65-- ============================================
67-- ============================================
@@ -112,6 +114,7 @@ route Api = AuthApi | ClientApi | FederationApi | MaintenanceApi
112-- ============================================
114-- ============================================
114hexDigit = \n ->
116hexDigit = \n ->
117+ let n = if n < 0 then 0 else if n > 15 then 15 else n
115 if n == 0 then "0" else if n == 1 then "1" else if n == 2 then "2" else if n == 3 then "3"
118 if n == 0 then "0" else if n == 1 then "1" else if n == 2 then "2" else if n == 3 then "3"
116 else if n == 4 then "4" else if n == 5 then "5" else if n == 6 then "6" else if n == 7 then "7"
119 else if n == 4 then "4" else if n == 5 then "5" else if n == 6 then "6" else if n == 7 then "7"
117 else if n == 8 then "8" else if n == 9 then "9" else if n == 10 then "a" else if n == 11 then "b"
120 else if n == 8 then "8" else if n == 9 then "9" else if n == 10 then "a" else if n == 11 then "b"
@@ -327,8 +330,10 @@ verifyFederationAuth = \authorization ->
327 not (federationSecret == "") && safeCompare (extractToken authorization) federationSecret
330 not (federationSecret == "") && safeCompare (extractToken authorization) federationSecret
329verifyMaintenanceAuth = \authorization ->
332verifyMaintenanceAuth = \authorization ->
330− verifyFederationAuth authorization
331− || (not (maintenanceSecret == "") && safeCompare (extractToken authorization) maintenanceSecret)
333+ let token = extractToken authorization
334+ let fedOk = not (federationSecret == "") && safeCompare token federationSecret
335+ let maintOk = not (maintenanceSecret == "") && safeCompare token maintenanceSecret
333clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
338clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
@@ -400,6 +405,14 @@ pruneExpired = \t -> do
400 yield q) == 0
405 yield q) == 0
401 yield p), peerServers: peerServersBefore}
406 yield p), peerServers: peerServersBefore}
408+maybePrune = \t -> do
409+ lastPruned <- *lastPrunedAt
410+ if t - lastPruned < 1000
411+ then yield {expiredLocal: [], peerServers: []}
413+ set *lastPrunedAt = t
403sendBatchedGossip = \peerServers allEvents ->
416sendBatchedGossip = \peerServers allEvents ->
404 if count allEvents == 0
417 if count allEvents == 0
405 then yield {}
418 then yield {}
@@ -461,7 +474,7 @@ serve = \req -> case req of
463 pruneResult <- atomic do
476 pruneResult <- atomic do
464− pr <- pruneExpired t
477+ pr <- maybePrune t
465 challenges <- *challenges
478 challenges <- *challenges
466 let existingCount = count (do c <- challenges; where c.pubkey == pubkey; yield c)
479 let existingCount = count (do c <- challenges; where c.pubkey == pubkey; yield c)
467 let oldestChallengeExpiry = fold (\acc c2 -> if c2.pubkey == pubkey && (acc == -1 || c2.expiresAt < acc) then c2.expiresAt else acc) -1 challenges
480 let oldestChallengeExpiry = fold (\acc c2 -> if c2.pubkey == pubkey && (acc == -1 || c2.expiresAt < acc) then c2.expiresAt else acc) -1 challenges
@@ -494,7 +507,7 @@ serve = \req -> case req of
494 token <- genToken
507 token <- genToken
495 let expiry = t + sessionExpiry
508 let expiry = t + sessionExpiry
496 result <- atomic do
509 result <- atomic do
497− pr <- pruneExpired t
510+ pr <- maybePrune t
498 challenges <- *challenges
511 challenges <- *challenges
499 sessions <- *sessions
512 sessions <- *sessions
500 presence <- *presence
513 presence <- *presence
@@ -582,7 +595,7 @@ serve = \req -> case req of
583 let token = extractToken authorization
596 let token = extractToken authorization
584 result <- atomic do
597 result <- atomic do
585− pr <- pruneExpired t
598+ pr <- maybePrune t
586 sessions <- *sessions
599 sessions <- *sessions
587 messages <- *messages
600 messages <- *messages
588 seqCounter <- *seqCounter
601 seqCounter <- *seqCounter
@@ -626,7 +639,7 @@ serve = \req -> case req of
626 msgId <- genToken
639 msgId <- genToken
627 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
640 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
628 result <- atomic do
641 result <- atomic do
629− pr <- pruneExpired t
642+ pr <- maybePrune t
630 sessions <- *sessions
643 sessions <- *sessions
631 messages <- *messages
644 messages <- *messages
632 presence <- *presence
645 presence <- *presence
@@ -696,15 +709,16 @@ serve = \req -> case req of
697 AckMessages {messageIds, authorization, respond} -> do
710 AckMessages {messageIds, authorization, respond} -> do
712+ let dedupedIds = if count messageIds > maxMessageIds then []
699− let dedupedIds = fold (\acc mid ->
713+ else fold (\acc mid ->
700− if mid == "" then acc
714+ if mid == "" then acc
701− else if count acc >= maxMessageIds then acc
715+ else if count acc >= maxMessageIds then acc
702− else if tooLong mid maxMessageIdLen then acc
716+ else if tooLong mid maxMessageIdLen then acc
703− else if not (isValidHex mid) then acc
717+ else if not (isValidHex mid) then acc
704− else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
718+ else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
705− else union acc [normalizeHex mid]) [] messageIds
719+ else union acc [normalizeHex mid]) [] messageIds
706 result <- atomic do
720 result <- atomic do
707− pr <- pruneExpired t
721+ pr <- maybePrune t
708 sessions <- *sessions
722 sessions <- *sessions
709 messages <- *messages
723 messages <- *messages
710 let session = lookupSession sessions (extractToken authorization) t
724 let session = lookupSession sessions (extractToken authorization) t
@@ -762,7 +776,7 @@ serve = \req -> case req of
762 let pubkey = if isValidHex pubkey then normalizeHex pubkey else pubkey
776 let pubkey = if isValidHex pubkey then normalizeHex pubkey else pubkey
764 result <- atomic do
778 result <- atomic do
765− pr <- pruneExpired t
779+ pr <- maybePrune t
766 sessions <- *sessions
780 sessions <- *sessions
767 presence <- *presence
781 presence <- *presence
768 let session = lookupSession sessions (extractToken authorization) t
782 let session = lookupSession sessions (extractToken authorization) t
@@ -854,7 +868,7 @@ serve = \req -> case req of
855 [e]) [] filteredOnline
869 [e]) [] filteredOnline
856 gossipResult <- atomic do
870 gossipResult <- atomic do
857− pr <- pruneExpired t
871+ pr <- maybePrune t
858 presence <- *presence
872 presence <- *presence
859 let fromServerTrusted = count (do p <- presence; where p.server == fromServer; yield p) > 0
873 let fromServerTrusted = count (do p <- presence; where p.server == fromServer; yield p) > 0
860 let trustedDeduped = do
874 let trustedDeduped = do
@@ -1042,7 +1056,7 @@ serve = \req -> case req of
1042 let validOrigin = not (originServer == "") && not (originServer == serverHostN) && not (tooLong originServer maxServerNameLen) && not (isBadServerName originServer)
1056 let validOrigin = not (originServer == "") && not (originServer == serverHostN) && not (tooLong originServer maxServerNameLen) && not (isBadServerName originServer)
1043 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
1057 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
1044 result <- atomic do
1058 result <- atomic do
1045− pr <- pruneExpired t
1059+ pr <- maybePrune t
1046 messages <- *messages
1060 messages <- *messages
1047 seqCounter <- *seqCounter
1061 seqCounter <- *seqCounter
1048 let seqTs = if t > seqCounter then t else seqCounter + 1
1062 let seqTs = if t > seqCounter then t else seqCounter + 1
@@ -1080,16 +1094,17 @@ serve = \req -> case req of
1080 let originServer = normalizeServerName originServer
1094 let originServer = normalizeServerName originServer
1081 t <- now
1095 t <- now
1082 let authorized = verifyFederationAuth authorization
1096 let authorized = verifyFederationAuth authorization
1097+ let dedupedIds = if count messageIds > maxMessageIds then []
1083− let dedupedIds = fold (\acc mid ->
1098+ else fold (\acc mid ->
1084− if mid == "" then acc
1099+ if mid == "" then acc
1085− else if count acc >= maxMessageIds then acc
1100+ else if count acc >= maxMessageIds then acc
1086− else if tooLong mid maxMessageIdLen then acc
1101+ else if tooLong mid maxMessageIdLen then acc
1087− else if not (isValidHex mid) then acc
1102+ else if not (isValidHex mid) then acc
1088− else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
1103+ else if count (do a <- acc; where a == normalizeHex mid; yield a) > 0 then acc
1089− else union acc [normalizeHex mid]) [] messageIds
1104+ else union acc [normalizeHex mid]) [] messageIds
1090 let validAckFields = authorized && originServer == serverHostN && not (fromServer == "") && not (fromServer == serverHostN) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer)
1105 let validAckFields = authorized && originServer == serverHostN && not (fromServer == "") && not (fromServer == serverHostN) && not (tooLong fromServer maxServerNameLen) && not (isBadServerName fromServer)
1091 pruneResult <- atomic do
1106 pruneResult <- atomic do
1092− pr <- pruneExpired t
1107+ pr <- maybePrune t
1093 forwards <- *forwards
1108 forwards <- *forwards
1094 let validAck = validAckFields
1109 let validAck = validAckFields
1095 let forwardsToRemove = do
1110 let forwardsToRemove = do