@@ -86,9 +86,6 @@ maxChallenges = 10000
8686-- Ceiling on concurrent active sessions.
8787maxSessions : Int
8888maxSessions = 10000
89−-- Ceiling on remembered delivered message IDs.
90−maxDeliveredIds : Int
91−maxDeliveredIds = 10000
9289-- Per-pubkey ceiling on outstanding challenges (prevents one key from exhausting the pool).
9390maxChallengesPerKey : Int
9491maxChallengesPerKey = 5
@@ -238,7 +235,7 @@ impl FromJSON GossipEventType where
238235type GossipEvent = {eventType: GossipEventType, pubkey: PubkeyHex}
239236
240237-- /messages signals failure via the per-entry `error` field on each result:
241−-- empty = success (delivered, federated, queued, or silent duplicate),
238+-- empty = success (delivered, federated, or queued),
242239-- non-empty = "rate_limited" | "self_send". A non-empty top-level `error`
243240-- ("rate_limited" | "unauthorized") means the request was rejected before
244241-- any entry was processed and `results` is empty.
@@ -291,8 +288,6 @@ type PresenceEntry = {pubkey: PubkeyHex, server: ServerName, expiresAt: Timestam
291288-- incremented by `retryPendingForwards` until `maxForwardRetries` is hit.
292289-- Identity is derived from `encryptedBlob` (see `Message`).
293290type ForwardEntry = {toServer: ServerName, toKey: PubkeyHex, encryptedBlob: BlobHex, retries: Int}
294−-- Replay-protection record: a message id we already delivered locally.
295−type DeliveredEntry = {messageId: MessageIdHex, toKey: PubkeyHex, deliveredAt: Timestamp}
296291-- Running failure counter for a peer; keyed by `server`. Drives `retryBackoff`.
297292type ForwardFailure = {server: Text, failedAt: Timestamp, failures: Int, firstFailedAt: Timestamp}
298293-- One fixed-window rate counter. `key` identifies the subject (pubkey, ip,
@@ -326,8 +321,6 @@ type ActivePoll = {pubkey: PubkeyHex, startedAt: Timestamp}
326321*presence : [PresenceEntry]
327322-- Cross-server outbox: messages queued for delivery to a peer, with retry counts.
328323*forwards : [ForwardEntry]
329−-- Tombstones of already-delivered message ids; combined with `*messages` for dedup.
330−*deliveredIds : [DeliveredEntry]
331324-- Per-peer failure counters; drives `retryBackoff` and the federation health view.
332325*forwardFailures : [ForwardFailure]
333326-- Currently-blocking long-poll slots, used to cap concurrent /poll holds per pubkey.
@@ -468,7 +461,7 @@ type KeyPresenceEntry = {server: Text, expiresAt: Timestamp}
468461type RecipientCount = {pubkey: PubkeyHex, count: Int}
469462
470463route StatsApi where
471− GET /stats headers {authorization: Text} -> {ok: Bool, version: Text, capacity: {sessions: Int, sessionsMax: Int, sessionsPct: Int, messages: Int, totalMessageCount: Int, presence: Int, forwards: Int, challenges: Int, challengesMax: Int, deliveredIds: Int, deliveredIdsMax: Int, forwardNonces: Int, forwardNoncesMax: Int, nonceEvictions: Int, activePolls: Int, recipientCounts: Int, messageBucketMax: Int, messageBucketMin: Int}, federation: {enabled: Bool, inFlight: Int, hungSlots: Int, avgLatencyMs: Int, maxLatencyMs: Int, callCount: Int, slowVerifies: Int, verifyFailures: Int, forwardFailures: Int, oldestForwardAge: Int, oldestAckAge: Int, recentGossip: Int, blockedPeers: Int, forwardServers: Int, maxForwardQueueDepth: Int, topFailingPeers: [{server: Text, failures: Int}], gossipSuccess: Int, gossipFail: Int, latencyBuckets: {lt100: Int, lt500: Int, lt2000: Int, gte2000: Int}}, rates: {send: Int, challenge: Int, federation: Int, federationPair: Int, global: Int, ip: Int, lookup: Int, recv: Int, pair: Int, gossipEvent: Int, verifyIp: Int, uniqueKeyIp: Int, pendingAckCleanups: Int, rejectedGlobal: Int, rejectedIp: Int, rejectedAuth: Int, rejectedPerKey: Int, rejectedFederation: Int}, timing: {pruneCount: Int, pruneRunning: Timestamp, heartbeatCount: Int, uptime: Int, lastPruneAt: Timestamp, lastHeartbeatAt: Timestamp, totalEntries: Int, activeRequests: Int, peakActiveRequests: Int, pollAvgMs: Int, pollMaxMs: Int, pollCount: Int, pollLatencyBuckets: {lt50: Int, lt200: Int, lt1000: Int, gte1000: Int}, sendAvgMs: Int, sendMaxMs: Int, sendCount: Int, sendLatencyBuckets: {lt50: Int, lt200: Int, lt1000: Int, gte1000: Int}, authAvgMs: Int, authMaxMs: Int, authCount: Int}} = Stats
464+ GET /stats headers {authorization: Text} -> {ok: Bool, version: Text, capacity: {sessions: Int, sessionsMax: Int, sessionsPct: Int, messages: Int, totalMessageCount: Int, presence: Int, forwards: Int, challenges: Int, challengesMax: Int, forwardNonces: Int, forwardNoncesMax: Int, nonceEvictions: Int, activePolls: Int, recipientCounts: Int, messageBucketMax: Int, messageBucketMin: Int}, federation: {enabled: Bool, inFlight: Int, hungSlots: Int, avgLatencyMs: Int, maxLatencyMs: Int, callCount: Int, slowVerifies: Int, verifyFailures: Int, forwardFailures: Int, oldestForwardAge: Int, oldestAckAge: Int, recentGossip: Int, blockedPeers: Int, forwardServers: Int, maxForwardQueueDepth: Int, topFailingPeers: [{server: Text, failures: Int}], gossipSuccess: Int, gossipFail: Int, latencyBuckets: {lt100: Int, lt500: Int, lt2000: Int, gte2000: Int}}, rates: {send: Int, challenge: Int, federation: Int, federationPair: Int, global: Int, ip: Int, lookup: Int, recv: Int, pair: Int, gossipEvent: Int, verifyIp: Int, uniqueKeyIp: Int, pendingAckCleanups: Int, rejectedGlobal: Int, rejectedIp: Int, rejectedAuth: Int, rejectedPerKey: Int, rejectedFederation: Int}, timing: {pruneCount: Int, pruneRunning: Timestamp, heartbeatCount: Int, uptime: Int, lastPruneAt: Timestamp, lastHeartbeatAt: Timestamp, totalEntries: Int, activeRequests: Int, peakActiveRequests: Int, pollAvgMs: Int, pollMaxMs: Int, pollCount: Int, pollLatencyBuckets: {lt50: Int, lt200: Int, lt1000: Int, gte1000: Int}, sendAvgMs: Int, sendMaxMs: Int, sendCount: Int, sendLatencyBuckets: {lt50: Int, lt200: Int, lt1000: Int, gte1000: Int}, authAvgMs: Int, authMaxMs: Int, authCount: Int}} = Stats
472465 GET /stats/peers headers {authorization: Text} -> {peers: [PeerStatus], ok: Bool} = StatsPeers
473466 GET /stats/key/{pubkey: PubkeyHex} headers {authorization: Text} -> {messages: Int, presence: [KeyPresenceEntry], sessions: Int, forwards: Int, forwardDetails: [{server: Text, retries: Int}], activePolls: Int, ok: Bool} = StatsKey
474467 POST {limit: Int} /stats/top-recipients headers {authorization: Text} -> {recipients: [RecipientCount], ok: Bool, total: Int} = TopRecipients
@@ -942,8 +935,7 @@ clearFailure = \server -> atomic (do
942935-- Common utility queries
943936-- ============================================
944937-- Pure helpers shared by send/poll/federation paths: monotonic sequence
945−-- generation, presence lookup, and duplicate detection across
946−-- (messages, deliveredIds).
938+-- generation and presence lookup.
947939
948940-- Strictly-monotonic next sequence timestamp: max of wall clock and prior+1.
949941-- Guarantees /poll cursors order events even when many arrive in the same ms.
@@ -959,15 +951,6 @@ presenceForKey = \presence pubkey t -> filter presence (\p -> p.pubkey == pubkey
959951maskedServer : [PresenceEntry] -> PubkeyHex -> Timestamp -> Text
960952maskedServer = \pres pubkey t -> (\here -> if any here (\p -> isLocalServer p.server) then serverHostN else if count here > 0 then "remote" else "") (presenceForKey pres pubkey t)
961953
962−-- Replay-protection check. A message is a duplicate if either:
963−-- * a live mailbox row exists with the same (encryptedBlob, toKey) -- since
964−-- id := messageIdOf encryptedBlob, same blob ⇒ same id; or
965−-- * a delivered-id tombstone matches (messageId, toKey).
966−-- The blob comparison avoids hashing every live row on each check; the
967−-- tombstone log only stores the id, so we compare it directly.
968−isDuplicateMessage : BlobHex -> PubkeyHex -> [Message] -> [DeliveredEntry] -> Bool
969−isDuplicateMessage = \blob toKey msgs dids -> any msgs (\m -> m.toKey == toKey && m.encryptedBlob == blob) || (\mid -> any dids (\d -> d.toKey == toKey && d.messageId == mid)) (messageIdOf blob)
970−
971954-- Project items through `getServer`, drop our own hostname, and dedupe.
972955-- Used to derive peer recipient lists from forwards/presence/etc.
973956collectPeers : [a] -> (a -> Text) -> [Text]
@@ -988,7 +971,7 @@ knownPeerServersFromPresence = \presence -> collectPeers presence (\p -> p.serve
988971-- One sweep over every state ref: drops expired rows in a single atomic block.
989972-- Returns the locally-expired presence rows whose pubkey has no other live
990973-- local session, so the caller can broadcast offline gossip for them.
991−pruneExpired : Int<Ms> -> IO {rw *activePolls, rw *challenges, rw *deliveredIds, rw *forwardFailures, rw *messages, rw *presence, rw *sessions} [{expiresAt: Timestamp, pubkey: PubkeyHex, server: ServerName}]
974+pruneExpired : Int<Ms> -> IO {rw *activePolls, rw *challenges, rw *forwardFailures, rw *messages, rw *presence, rw *sessions} [{expiresAt: Timestamp, pubkey: PubkeyHex, server: ServerName}]
992975pruneExpired = \t -> atomic do
993976 ch <- *challenges
994977 *challenges = filter ch (\x -> x.expiresAt > t)
@@ -996,8 +979,6 @@ pruneExpired = \t -> atomic do
996979 *sessions = filter ses (\x -> x.expiresAt > t)
997980 msgs <- *messages
998981 *messages = filter msgs (\m -> t - m.receivedAt < messageMaxAge)
999− dids <- *deliveredIds
1000− *deliveredIds = filter dids (\d -> t - d.deliveredAt < messageMaxAge)
1001982 ff <- *forwardFailures
1002983 *forwardFailures = filter ff (\f -> t - f.failedAt < 86400000)
1003984 aps <- *activePolls
@@ -1373,19 +1354,17 @@ handleSendMessages = \messages authorization clientIp -> do
13731354
13741355-- Atomic core of /messages. Charges send/pair/recv rate
13751356-- counters together (so any limit denies the message), enforces caps and
1376−-- self-send/duplicate rejections, allocates a monotonic receivedAt via
1377−-- `seqCounter`, appends the message, and pre-stages forwards for any remote
1378−-- presence rows that already exist for `to`. Returns the error code (empty
1379−-- on success or silent duplicate) and the list of remote servers the caller
1380−-- should kick forwards to.
1357+-- self-send rejections, allocates a monotonic receivedAt via `seqCounter`,
1358+-- appends the message, and pre-stages forwards for any remote presence rows
1359+-- that already exist for `to`. Returns the error code (empty on success) and
1360+-- the list of remote servers the caller should kick forwards to.
13811361-- All three rate counters are charged unconditionally (even on a failure)
1382−-- so that a sender can't probe limits cheaply by sending to themselves or
1383−-- replaying duplicates. Order matters: send-rate is the sender's identity,
1384−-- pair-rate covers conversation spam, and recv-rate is per-recipient.
1385−sendMessageCore : PubkeyHex -> PubkeyHex -> BlobHex -> Int<Ms> -> IO {r *deliveredIds, rw *forwards, rw *messages, rw *pairRates, rw *pollWakeup, r *presence, rw *recvRates, rw *sendRates, rw *seqCounter} {remoteServers: [Text], error: Text}
1362+-- so that a sender can't probe limits cheaply by sending to themselves.
1363+-- Order matters: send-rate is the sender's identity, pair-rate covers
1364+-- conversation spam, and recv-rate is per-recipient.
1365+sendMessageCore : PubkeyHex -> PubkeyHex -> BlobHex -> Int<Ms> -> IO {rw *forwards, rw *messages, rw *pairRates, rw *pollWakeup, r *presence, rw *recvRates, rw *sendRates, rw *seqCounter} {remoteServers: [Text], error: Text}
13861366sendMessageCore = \senderKey to encryptedBlob t -> do
13871367 msgs <- *messages
1388− dids <- *deliveredIds
13891368 sRates <- *sendRates
13901369 let sR = checkRate sRates senderKey maxSendRatePerKey sendRateWindowMs t
13911370 *sendRates = sR.entries
@@ -1401,8 +1380,6 @@ sendMessageCore = \senderKey to encryptedBlob t -> do
14011380 let reject = \e -> yield {error: e, remoteServers: []}
14021381 if senderKey == to
14031382 then reject "self_send"
1404− else if isDuplicateMessage encryptedBlob to msgs dids
1405− then reject ""
14061383 else if srLim || prLim || rrLim
14071384 then reject "rate_limited"
14081385 else do
@@ -1503,8 +1480,9 @@ handleBatchLookupPresence = \pubkeys authorization clientIp -> do
15031480 yield {results, authorized: True {}, error: ""})
15041481
15051482-- POST /messages/status: classify a message as
1506−-- pending (in mailbox), forwarding (en-route to a peer),
1507−-- delivered (acked or terminal), or unknown.
1483+-- pending (in mailbox), forwarding (en-route to a peer), or unknown.
1484+-- A message that has been polled and removed returns "unknown"; the server
1485+-- keeps no post-delivery record, so the client must track delivery itself.
15081486handleMessageStatus : PubkeyHex -> MessageIdHex -> Text -> Text -> IO {rw *globalRateCount, rw *globalRateWindowStart, r *sessions, clock} {authorized: Bool, status: Text}
15091487handleMessageStatus = \to messageId authorization clientIp -> do
15101488 t <- now
@@ -1512,14 +1490,10 @@ handleMessageStatus = \to messageId authorization clientIp -> do
15121490 gateAuth authorization clientIp t mkErr (\_ -> do
15131491 msgs <- *messages
15141492 fwds <- *forwards
1515− dids <- *deliveredIds
15161493 let blobMatches = \x -> messageIdOf x.encryptedBlob == messageId && x.toKey == to
1517− let didMatches = \d -> d.messageId == messageId && d.toKey == to
15181494 let s = if any msgs blobMatches
15191495 then "pending"
1520− else if any fwds blobMatches
1521− then "forwarding"
1522− else if any dids didMatches then "delivered" else "unknown"
1496+ else if any fwds blobMatches then "forwarding" else "unknown"
15231497 yield {status: s, authorized: True {}})
15241498
15251499-- ============================================
@@ -1596,7 +1570,7 @@ handleRecvGossip = \events fromServer requestTimestamp authorization -> do
15961570 yield {ok: True {}, error: "", retryAfterMs: 0})
15971571
15981572-- Store a federated/forwarded message into the local mailbox. Caller wraps in `atomic`.
1599−-- Returns "" on success (including duplicates), otherwise an error code:
1573+-- Returns "" on success, otherwise an error code:
16001574-- "no_presence" | "rate_limited" (only when checkRecvRate).
16011575--
16021576-- The presence gate requires the recipient to be **currently locally online**
@@ -1604,15 +1578,12 @@ handleRecvGossip = \events fromServer requestTimestamp authorization -> do
16041578-- on stale gossip about R could land on B with no actively-polling client and
16051579-- get stuck. If B refuses, A retries via `retryPendingForwards` against the
16061580-- next presence target.
1607−storeFederated : PubkeyHex -> BlobHex -> Bool -> Int<Ms> -> IO {r *deliveredIds, rw *messages, rw *pollWakeup, r *presence, rw *recvRates, rw *seqCounter} Text
1581+storeFederated : PubkeyHex -> BlobHex -> Bool -> Int<Ms> -> IO {rw *messages, rw *pollWakeup, r *presence, rw *recvRates, rw *seqCounter} Text
16081582storeFederated = \toKey encryptedBlob checkRecvRate t -> do
16091583 pres <- *presence
16101584 msgs <- *messages
1611− dids <- *deliveredIds
16121585 if not any pres (\p -> p.pubkey == toKey && isLocalServer p.server && p.expiresAt > t)
16131586 then yield "no_presence"
1614− else if isDuplicateMessage encryptedBlob toKey msgs dids
1615− then yield ""
16161587 else do
16171588 limited <- if checkRecvRate
16181589 then do
@@ -1633,12 +1604,12 @@ storeFederated = \toKey encryptedBlob checkRecvRate t -> do
16331604
16341605-- POST /federation/forward: receive a single federated message destined for
16351606-- a local key. Delegates the actual store decision to `storeFederated`,
1636−-- which returns "" on success (including the duplicate case). The peer-
1637−-- supplied messageId in the request body is ignored: we derive the id from
1638−-- the blob ourselves so a hostile peer cannot bypass dedup by varying the
1639−-- id. Honest senders derive the same value, so the "forward:" ++ messageId
1640−-- auth scope still verifies.
1641−handleForwardMessage : PubkeyHex -> BlobHex -> Text -> Text -> Int -> Text -> IO {r *allowedPeers, r *blockedPeers, rw *federationRates, r *deliveredIds, rw *messages, rw *pollWakeup, r *presence, rw *recvRates, rw *seqCounter, clock, network} {error: Text, ok: Bool, retryAfterMs: Int}
1607+-- which returns "" on success. The peer-supplied messageId in the request
1608+-- body is ignored: we derive the id from the blob ourselves so a hostile
1609+-- peer cannot influence the auth scope by varying the id. Honest senders
1610+-- derive the same value, so the "forward:" ++ messageId auth scope still
1611+-- verifies.
1612+handleForwardMessage : PubkeyHex -> BlobHex -> Text -> Text -> Int -> Text -> IO {r *allowedPeers, r *blockedPeers, rw *federationRates, rw *messages, rw *pollWakeup, r *presence, rw *recvRates, rw *seqCounter, clock, network} {error: Text, ok: Bool, retryAfterMs: Int}
16421613handleForwardMessage = \toKey encryptedBlob _ fromServer requestTimestamp authorization -> do
16431614 let messageId = messageIdOf encryptedBlob
16441615 t <- now
@@ -1671,7 +1642,7 @@ handleFederationBatchForward = \forwards fromServer requestTimestamp authorizati
16711642-- POST /maintenance/prune: trigger an out-of-band `pruneAndRetry` tick.
16721643-- Useful when the operator wants to force expiry/forward retries without
16731644-- waiting for the next `backgroundPrune` interval.
1674−handlePrune : Text -> IO {r *runtimeMaintenanceSecret, rw *activePolls, r *blockedPeers, rw *challenges, rw *deliveredIds, rw *forwardFailures, r *forwards, rw *lastPresenceRefresh, rw *lastPrunedAt, rw *messages, rw *presence, rw *sessions, clock} {ok: Bool}
1645+handlePrune : Text -> IO {r *runtimeMaintenanceSecret, rw *activePolls, r *blockedPeers, rw *challenges, rw *forwardFailures, r *forwards, rw *lastPresenceRefresh, rw *lastPrunedAt, rw *messages, rw *presence, rw *sessions, clock} {ok: Bool}
16751646handlePrune = \authorization -> do
16761647 t <- now
16771648 authorized <- verifyMaintenanceAuth authorization
@@ -1947,7 +1918,7 @@ handleGetConfig = \authorization -> do
19471918-- GET /stats: capacity gauges + minimal federation/timing snapshot. Most of
19481919-- the historical metric fields are zero-filled (see `zeroStats`) — only the
19491920-- counts that come straight from the state refs are populated.
1950−handleStats : Text -> IO {r *activePolls, r *blockedPeers, r *challenges, r *deliveredIds, r *forwards, r *heartbeatCount, r *lastHeartbeatAt, r *lastPrunedAt, r *messages, r *presence, r *pruneCount, r *runtimeMaintenanceSecret, r *sessions, r *startedAt, clock} {capacity: {activePolls: Int, challenges: Int, challengesMax: Int, deliveredIds: Int, deliveredIdsMax: Int, forwardNonces: Int, forwardNoncesMax: Int, forwards: Int, messageBucketMax: Int, messageBucketMin: Int, messages: Int, nonceEvictions: Int, presence: Int, recipientCounts: Int, sessions: Int, sessionsMax: Int, sessionsPct: Int, totalMessageCount: Int}, federation: {avgLatencyMs: Int, blockedPeers: Int, callCount: Int, enabled: Bool, forwardFailures: Int, forwardServers: Int, gossipFail: Int, gossipSuccess: Int, hungSlots: Int, inFlight: Int, latencyBuckets: {gte2000: Int, lt100: Int, lt2000: Int, lt500: Int}, maxForwardQueueDepth: Int, maxLatencyMs: Int, oldestAckAge: Int, oldestForwardAge: Int, recentGossip: Int, slowVerifies: Int, topFailingPeers: [{failures: Int, server: Text}], verifyFailures: Int}, ok: Bool, rates: {challenge: Int, federation: Int, federationPair: Int, global: Int, gossipEvent: Int, ip: Int, lookup: Int, pair: Int, pendingAckCleanups: Int, recv: Int, rejectedAuth: Int, rejectedFederation: Int, rejectedGlobal: Int, rejectedIp: Int, rejectedPerKey: Int, send: Int, uniqueKeyIp: Int, verifyIp: Int}, timing: {activeRequests: Int, authAvgMs: Int, authCount: Int, authMaxMs: Int, heartbeatCount: Int, lastHeartbeatAt: Timestamp, lastPruneAt: Timestamp, peakActiveRequests: Int, pollAvgMs: Int, pollCount: Int, pollLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, pollMaxMs: Int, pruneCount: Int, pruneRunning: Timestamp, sendAvgMs: Int, sendCount: Int, sendLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, sendMaxMs: Int, totalEntries: Int, uptime: Int}, version: Text}
1921+handleStats : Text -> IO {r *activePolls, r *blockedPeers, r *challenges, r *forwards, r *heartbeatCount, r *lastHeartbeatAt, r *lastPrunedAt, r *messages, r *presence, r *pruneCount, r *runtimeMaintenanceSecret, r *sessions, r *startedAt, clock} {capacity: {activePolls: Int, challenges: Int, challengesMax: Int, forwardNonces: Int, forwardNoncesMax: Int, forwards: Int, messageBucketMax: Int, messageBucketMin: Int, messages: Int, nonceEvictions: Int, presence: Int, recipientCounts: Int, sessions: Int, sessionsMax: Int, sessionsPct: Int, totalMessageCount: Int}, federation: {avgLatencyMs: Int, blockedPeers: Int, callCount: Int, enabled: Bool, forwardFailures: Int, forwardServers: Int, gossipFail: Int, gossipSuccess: Int, hungSlots: Int, inFlight: Int, latencyBuckets: {gte2000: Int, lt100: Int, lt2000: Int, lt500: Int}, maxForwardQueueDepth: Int, maxLatencyMs: Int, oldestAckAge: Int, oldestForwardAge: Int, recentGossip: Int, slowVerifies: Int, topFailingPeers: [{failures: Int, server: Text}], verifyFailures: Int}, ok: Bool, rates: {challenge: Int, federation: Int, federationPair: Int, global: Int, gossipEvent: Int, ip: Int, lookup: Int, pair: Int, pendingAckCleanups: Int, recv: Int, rejectedAuth: Int, rejectedFederation: Int, rejectedGlobal: Int, rejectedIp: Int, rejectedPerKey: Int, send: Int, uniqueKeyIp: Int, verifyIp: Int}, timing: {activeRequests: Int, authAvgMs: Int, authCount: Int, authMaxMs: Int, heartbeatCount: Int, lastHeartbeatAt: Timestamp, lastPruneAt: Timestamp, peakActiveRequests: Int, pollAvgMs: Int, pollCount: Int, pollLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, pollMaxMs: Int, pruneCount: Int, pruneRunning: Timestamp, sendAvgMs: Int, sendCount: Int, sendLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, sendMaxMs: Int, totalEntries: Int, uptime: Int}, version: Text}
19511922handleStats = \authorization -> withMaintAuth authorization (yield zeroStats) (do
19521923 t <- now
19531924 sa <- *startedAt
@@ -1955,7 +1926,6 @@ handleStats = \authorization -> withMaintAuth authorization (yield zeroStats) (d
19551926 msgs <- *messages
19561927 pres <- *presence
19571928 fwds <- *forwards
1958− dids <- *deliveredIds
19591929 aps <- *activePolls
19601930 chs <- *challenges
19611931 bp <- *blockedPeers
@@ -1980,8 +1950,6 @@ handleStats = \authorization -> withMaintAuth authorization (yield zeroStats) (d
19801950 forwards: fCount,
19811951 challenges: count chs,
19821952 challengesMax: maxChallenges,
1983− deliveredIds: count dids,
1984− deliveredIdsMax: maxDeliveredIds,
19851953 forwardNonces: 0,
19861954 forwardNoncesMax: 0,
19871955 nonceEvictions: 0,
@@ -2058,7 +2026,7 @@ handleStats = \authorization -> withMaintAuth authorization (yield zeroStats) (d
20582026-- All-zero /stats payload returned to unauthorized callers. The shape is kept
20592027-- in lockstep with the response in `handleStats` so older operator tooling
20602028-- never sees a missing field.
2061−zeroStats : {capacity: {activePolls: Int, challenges: Int, challengesMax: Int, deliveredIds: Int, deliveredIdsMax: Int, forwardNonces: Int, forwardNoncesMax: Int, forwards: Int, messageBucketMax: Int, messageBucketMin: Int, messages: Int, nonceEvictions: Int, presence: Int, recipientCounts: Int, sessions: Int, sessionsMax: Int, sessionsPct: Int, totalMessageCount: Int}, federation: {avgLatencyMs: Int, blockedPeers: Int, callCount: Int, enabled: Bool, forwardFailures: Int, forwardServers: Int, gossipFail: Int, gossipSuccess: Int, hungSlots: Int, inFlight: Int, latencyBuckets: {gte2000: Int, lt100: Int, lt2000: Int, lt500: Int}, maxForwardQueueDepth: Int, maxLatencyMs: Int, oldestAckAge: Int, oldestForwardAge: Int, recentGossip: Int, slowVerifies: Int, topFailingPeers: [{failures: Int, server: Text}], verifyFailures: Int}, ok: Bool, rates: {challenge: Int, federation: Int, federationPair: Int, global: Int, gossipEvent: Int, ip: Int, lookup: Int, pair: Int, pendingAckCleanups: Int, recv: Int, rejectedAuth: Int, rejectedFederation: Int, rejectedGlobal: Int, rejectedIp: Int, rejectedPerKey: Int, send: Int, uniqueKeyIp: Int, verifyIp: Int}, timing: {activeRequests: Int, authAvgMs: Int, authCount: Int, authMaxMs: Int, heartbeatCount: Int, lastHeartbeatAt: Timestamp, lastPruneAt: Timestamp, peakActiveRequests: Int, pollAvgMs: Int, pollCount: Int, pollLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, pollMaxMs: Int, pruneCount: Int, pruneRunning: Timestamp, sendAvgMs: Int, sendCount: Int, sendLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, sendMaxMs: Int, totalEntries: Int, uptime: Int}, version: Text}
2029+zeroStats : {capacity: {activePolls: Int, challenges: Int, challengesMax: Int, forwardNonces: Int, forwardNoncesMax: Int, forwards: Int, messageBucketMax: Int, messageBucketMin: Int, messages: Int, nonceEvictions: Int, presence: Int, recipientCounts: Int, sessions: Int, sessionsMax: Int, sessionsPct: Int, totalMessageCount: Int}, federation: {avgLatencyMs: Int, blockedPeers: Int, callCount: Int, enabled: Bool, forwardFailures: Int, forwardServers: Int, gossipFail: Int, gossipSuccess: Int, hungSlots: Int, inFlight: Int, latencyBuckets: {gte2000: Int, lt100: Int, lt2000: Int, lt500: Int}, maxForwardQueueDepth: Int, maxLatencyMs: Int, oldestAckAge: Int, oldestForwardAge: Int, recentGossip: Int, slowVerifies: Int, topFailingPeers: [{failures: Int, server: Text}], verifyFailures: Int}, ok: Bool, rates: {challenge: Int, federation: Int, federationPair: Int, global: Int, gossipEvent: Int, ip: Int, lookup: Int, pair: Int, pendingAckCleanups: Int, recv: Int, rejectedAuth: Int, rejectedFederation: Int, rejectedGlobal: Int, rejectedIp: Int, rejectedPerKey: Int, send: Int, uniqueKeyIp: Int, verifyIp: Int}, timing: {activeRequests: Int, authAvgMs: Int, authCount: Int, authMaxMs: Int, heartbeatCount: Int, lastHeartbeatAt: Timestamp, lastPruneAt: Timestamp, peakActiveRequests: Int, pollAvgMs: Int, pollCount: Int, pollLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, pollMaxMs: Int, pruneCount: Int, pruneRunning: Timestamp, sendAvgMs: Int, sendCount: Int, sendLatencyBuckets: {gte1000: Int, lt1000: Int, lt200: Int, lt50: Int}, sendMaxMs: Int, totalEntries: Int, uptime: Int}, version: Text}
20622030zeroStats = {
20632031 ok: False {},
20642032 version: serverBuildVersion,
@@ -2072,8 +2040,6 @@ zeroStats = {
20722040 forwards: 0,
20732041 challenges: 0,
20742042 challengesMax: 0,
2075− deliveredIds: 0,
2076− deliveredIdsMax: 0,
20772043 forwardNonces: 0,
20782044 forwardNoncesMax: 0,
20792045 nonceEvictions: 0,
@@ -2342,7 +2308,7 @@ api = serve Api where
23422308
23432309-- Endless tick: every `backgroundPruneInterval` run `pruneAndRetry` and bump
23442310-- `pruneCount` for /stats and /health.
2345−backgroundPrune : IO {rw *activePolls, r *blockedPeers, rw *challenges, rw *deliveredIds, rw *forwardFailures, r *forwards, rw *lastPresenceRefresh, rw *lastPrunedAt, rw *messages, rw *presence, rw *pruneCount, rw *sessions, clock} a
2311+backgroundPrune : IO {rw *activePolls, r *blockedPeers, rw *challenges, rw *forwardFailures, r *forwards, rw *lastPresenceRefresh, rw *lastPrunedAt, rw *messages, rw *presence, rw *pruneCount, rw *sessions, clock} a
23462312backgroundPrune = do
23472313 sleep backgroundPruneInterval
23482314 t <- now