@@ -105,12 +105,6 @@ maxRecvRatePerKey = 10
105-- Window for the per-key receive rate limit (1 s).
105-- Window for the per-key receive rate limit (1 s).
106recvRateWindowMs : Int<Ms>
106recvRateWindowMs : Int<Ms>
107recvRateWindowMs = 1000
107recvRateWindowMs = 1000
108−-- Messages permitted per (sender, recipient) pair within the pair window.
109−maxPairRatePerPair : Int
110−maxPairRatePerPair = 30
111−-- Window for the per-pair rate limit (1 min).
112−pairRateWindowMs : Int<Ms>
113−pairRateWindowMs = 60000
114-- Requests permitted per source IP per window (covers unauthenticated traffic).
108-- Requests permitted per source IP per window (covers unauthenticated traffic).
115maxIpRatePerIp : Int
109maxIpRatePerIp : Int
116maxIpRatePerIp = 10
110maxIpRatePerIp = 10
@@ -282,8 +276,6 @@ type RateEntry = {key: Text, windowStart: Timestamp, count: Int}
282*sendRates : [RateEntry]
276*sendRates : [RateEntry]
283-- Per-recipient counters; protects a popular key from being flooded by many senders.
277-- Per-recipient counters; protects a popular key from being flooded by many senders.
284*recvRates : [RateEntry]
278*recvRates : [RateEntry]
285−-- Per-(sender, recipient) pair counters; second-axis brake on chat spam.
286−*pairRates : [RateEntry]
287-- Per-key /auth/challenge counters (separate from `*challenges` capacity gate).
279-- Per-key /auth/challenge counters (separate from `*challenges` capacity gate).
288*challengeRates : [RateEntry]
280*challengeRates : [RateEntry]
289-- Per-source-IP counters; gate for unauthenticated traffic.
281-- Per-source-IP counters; gate for unauthenticated traffic.
@@ -983,7 +975,7 @@ ackPolledMessages = \pubkey cursor -> do
983-- Federated forwards are kicked off in background forks after the atomic.
975-- Federated forwards are kicked off in background forks after the atomic.
985-- POST /messages: send a list of ciphertexts atomically. Every message is
977-- POST /messages: send a list of ciphertexts atomically. Every message is
986−-- validated (self-send + send/pair/recv rate windows) inside a single atomic
978+-- validated (self-send + send/recv rate windows) inside a single atomic
987-- block; if any one would be rejected, none are enqueued and the call returns
979-- block; if any one would be rejected, none are enqueued and the call returns
988-- a non-2xx HTTP status. Rate counters are still charged for every attempted
980-- a non-2xx HTTP status. Rate counters are still charged for every attempted
989-- message even when the batch is rejected, so a sender can't probe limits
981-- message even when the batch is rejected, so a sender can't probe limits
@@ -1011,28 +1003,23 @@ handleSendMessages = \messages authorization clientIp -> do
1011 when federationEnabled (forEach checked (\c -> fork (forkForwardsTo c.to c.encryptedBlob c.remoteServers)))
1003 when federationEnabled (forEach checked (\c -> fork (forkForwardsTo c.to c.encryptedBlob c.remoteServers)))
1012 yield (Ok {value: {}}))
1004 yield (Ok {value: {}}))
1014−-- Charge send/pair/recv rate counters for one (sender, recipient) pair and
1006+-- Charge send/recv rate counters for one (sender, recipient) pair and
1015-- decide whether the message would be accepted. Mutates only the rate
1007-- decide whether the message would be accepted. Mutates only the rate
1016-- counters, never the mailbox; the caller commits via `commitSendMessage`
1008-- counters, never the mailbox; the caller commits via `commitSendMessage`
1017-- only after every message in the batch has passed.
1009-- only after every message in the batch has passed.
1018−-- All three rate counters are charged unconditionally (even on a failure)
1010+-- Both rate counters are charged unconditionally (even on a failure)
1019-- so that a sender can't probe limits cheaply by sending to themselves.
1011-- so that a sender can't probe limits cheaply by sending to themselves.
1020−-- Order matters: send-rate is the sender's identity, pair-rate covers
1021−-- conversation spam, and recv-rate is per-recipient.
1022−checkSendRate : PubkeyHex -> PubkeyHex -> Int<Ms> -> IO {rw *pairRates, r *presence, rw *recvRates, rw *sendRates} {remoteServers: [Text], error: Text}
1012+checkSendRate : PubkeyHex -> PubkeyHex -> Int<Ms> -> IO {r *presence, rw *recvRates, rw *sendRates} {remoteServers: [Text], error: Text}
1023checkSendRate = \senderKey to t -> do
1013checkSendRate = \senderKey to t -> do
1024 sRates <- *sendRates
1014 sRates <- *sendRates
1025 let sR = checkRate sRates senderKey maxSendRatePerKey sendRateWindowMs t
1015 let sR = checkRate sRates senderKey maxSendRatePerKey sendRateWindowMs t
1026 *sendRates = sR.entries
1016 *sendRates = sR.entries
1027− pRates <- *pairRates
1028− let pR = checkRate pRates (senderKey ++ "|" ++ to) maxPairRatePerPair pairRateWindowMs t
1029− *pairRates = pR.entries
1030 rRates <- *recvRates
1017 rRates <- *recvRates
1031 let rR = checkRate rRates to maxRecvRatePerKey recvRateWindowMs t
1018 let rR = checkRate rRates to maxRecvRatePerKey recvRateWindowMs t
1032 *recvRates = rR.entries
1019 *recvRates = rR.entries
1033 if senderKey == to
1020 if senderKey == to
1034 then yield {error: "self_send", remoteServers: []}
1021 then yield {error: "self_send", remoteServers: []}
1035− else if sR.limited || pR.limited || rR.limited
1022+ else if sR.limited || rR.limited
1036 then yield {error: "rate_limited", remoteServers: []}
1023 then yield {error: "rate_limited", remoteServers: []}
1038 pres <- *presence
1025 pres <- *presence