@@ -742,12 +742,16 @@ checkIpRateGate = \ip t ->
742742 if ip == "" then yield False
743743 else checkPubkeyRateGate ipRates ip maxIpRatePerIp ipRateWindowMs t
744744
745−checkPubkeyRateGate = \rateRef pubkey maxRate windowMs t -> atomic do
745+-- Non-atomic version: caller must wrap in `atomic do`.
746+chargeRate = \rateRef key maxRate windowMs t -> do
746747 rates <- *rateRef
747− let r = checkRate rates pubkey maxRate windowMs 5000 t
748+ let r = checkRate rates key maxRate windowMs 5000 t
748749 set *rateRef = r.entries
749750 yield r.limited
750751
752+checkPubkeyRateGate = \rateRef pubkey maxRate windowMs t -> atomic
753+ (chargeRate rateRef pubkey maxRate windowMs t)
754+
751755checkServerRateGate = \server t ->
752756 checkPubkeyRateGate federationRates server maxFederationRatePerServer federationRateWindowMs t
753757
@@ -1069,11 +1073,9 @@ handleRevoke = \targetToken authorization clientIp respond -> do
10691073 (\caller -> do
10701074 result <- atomic do
10711075 ses <- *sessions
1072− let found = any ses (\s -> s.pubkey == caller && s.token == targetToken)
1073− if not found then yield False
1074− else do
1075− set *sessions = removeWhere ses (\s -> s.pubkey == caller && s.token == targetToken)
1076− yield True
1076+ let pred = \s -> s.pubkey == caller && s.token == targetToken
1077+ set *sessions = removeWhere ses pred
1078+ yield (any ses pred)
10771079 yield (respond {ok: result, authorized: True {}}))
10781080
10791081handleLogout = \authorization clientIp respond -> do
@@ -1240,21 +1242,14 @@ sendMessageCore = \senderKey to encryptedBlob clampedTs effMsgId expiresInMs msg
12401242 let isDup = (effMsgId != "" && any msgs (\m -> m.messageId == effMsgId && m.toKey == to))
12411243 || hasMessage effMsgId to aids
12421244 || hasMessage effMsgId to dids
1243− sendRates <- *sendRates
1244− let sr = checkRate sendRates senderKey maxSendRatePerKey sendRateWindowMs 5000 t
1245− set *sendRates = sr.entries
1246− pairRates <- *pairRates
1247− let pr = checkRate pairRates (senderKey ++ "|" ++ to) maxPairRatePerPair pairRateWindowMs 5000 t
1248− set *pairRates = pr.entries
1249− recvRates <- *recvRates
1250− let rr = checkRate recvRates to maxRecvRatePerKey recvRateWindowMs 5000 t
1251− set *recvRates = rr.entries
1252− let recipientCount = countWhere msgs (\m -> m.toKey == to)
1245+ srLim <- chargeRate sendRates senderKey maxSendRatePerKey sendRateWindowMs t
1246+ prLim <- chargeRate pairRates (senderKey ++ "|" ++ to) maxPairRatePerPair pairRateWindowMs t
1247+ rrLim <- chargeRate recvRates to maxRecvRatePerKey recvRateWindowMs t
12531248 if senderKey == to then yield {status: MsRejected {}, remoteServers: []}
12541249 else if isDup then yield {status: MsDuplicate {}, remoteServers: []}
1255− else if sr.limited || pr.limited || rr.limited then yield {status: MsRateLimited {}, remoteServers: []}
1250+ else if srLim || prLim || rrLim then yield {status: MsRateLimited {}, remoteServers: []}
12561251 else if count msgs >= maxMessages then yield {status: MsRejected {}, remoteServers: []}
1257− else if recipientCount >= maxMessagesPerRecipient then yield {status: MsQueued {}, remoteServers: []}
1252+ else if countWhere msgs (\m -> m.toKey == to) >= maxMessagesPerRecipient then yield {status: MsQueued {}, remoteServers: []}
12581253 else do
12591254 pres <- *presence
12601255 seq <- *seqCounter
@@ -1366,12 +1361,10 @@ handleAckMessages = \messageIds authorization clientIp respond -> do
13661361 result <- atomic do
13671362 msgs <- *messages
13681363 let toAck = filter msgs matches
1369− if count toAck == 0 then yield {ackedCount: 0, pendingAcks: []}
1370− else do
1371− set *messages = removeWhere msgs matches
1372− pruneRef forwards (\f -> not (matches f))
1373− ackedFed <- recordAcksAndDeliveries toAck t
1374− yield {ackedCount: count toAck, pendingAcks: ackedFed}
1364+ set *messages = removeWhere msgs matches
1365+ pruneRef forwards (\f -> not (matches f))
1366+ ackedFed <- recordAcksAndDeliveries toAck t
1367+ yield {ackedCount: count toAck, pendingAcks: ackedFed}
13751368 fork (when federationEnabled (sendAcksAndClean result.pendingAcks))
13761369 yield (respond {ok: True {}, authorized: True {}, ackedCount: result.ackedCount, error: ""}))
13771370
@@ -1402,13 +1395,11 @@ handleDeleteMessages = \messageIds authorization clientIp respond -> do
14021395 result <- atomic do
14031396 msgs <- *messages
14041397 let toDel = filter msgs matches
1405− if count toDel == 0 then yield {deletedCount: 0, pendingAcks: []}
1406− else do
1407− set *messages = removeWhere msgs matches
1408− pruneRef forwards (\f -> not (matches f))
1409− let ackedFed = mapWhere toDel (\m -> m.originServer != "") (\m -> mkAckedEntry m t)
1410− appendCapped ackedIds ackedFed maxAckedIds (\a -> a.ackedAt)
1411− yield {deletedCount: count toDel, pendingAcks: ackedFed}
1398+ set *messages = removeWhere msgs matches
1399+ pruneRef forwards (\f -> not (matches f))
1400+ let ackedFed = mapWhere toDel (\m -> m.originServer != "") (\m -> mkAckedEntry m t)
1401+ appendCapped ackedIds ackedFed maxAckedIds (\a -> a.ackedAt)
1402+ yield {deletedCount: count toDel, pendingAcks: ackedFed}
14121403 fork (when federationEnabled (sendAcksAndClean result.pendingAcks))
14131404 yield (respond {ok: True {}, authorized: True {}, deletedCount: result.deletedCount, error: ""}))
14141405
@@ -1508,8 +1499,7 @@ handleVerifyToken = \token scope clientIp respond -> do
15081499 else do
15091500 found <- atomic do
15101501 pending <- *pendingVerifications
1511− let candidates = filter pending (\p -> p.scope == scope && t - p.createdAt < verificationTokenExpiry)
1512− let match = any candidates (\p -> p.token == token)
1502+ let match = any pending (\p -> p.scope == scope && p.token == token && t - p.createdAt < verificationTokenExpiry)
15131503 when match (set *pendingVerifications = filter pending (\p -> p.scope != scope || p.token != token))
15141504 yield match
15151505 yield (respond {ok: found})