on master help sign in
Remove GET /lookup/{pubkey} and POST /lookup/batch endpoints
commitaa3c3eb89a0245ba65ac4e44035c73795f9070fa authoriko <mail@iko.soy> dateFri 8 May 2026, 11:13 (5 months ago) parent4520419
1 file changed+3−70
server.knot+3−70
server.knot+3−70
@@ -111,12 +111,6 @@ maxPairRatePerPair = 30
111111-- Window for the per-pair rate limit (1 min).
112112pairRateWindowMs : Int<Ms>
113113pairRateWindowMs = 60000
114−-- Presence/key lookups permitted per key per window.
115−maxLookupRatePerKey : Int
116−maxLookupRatePerKey = 5
117−-- Window for the per-key lookup rate limit (1 s).
118−lookupRateWindowMs : Int<Ms>
119−lookupRateWindowMs = 1000
120114-- Requests permitted per source IP per window (covers unauthenticated traffic).
121115maxIpRatePerIp : Int
122116maxIpRatePerIp = 10
@@ -290,8 +284,6 @@ type RateEntry = {key: Text, windowStart: Timestamp, count: Int}
290284*pairRates : [RateEntry]
291285-- Per-key /auth/challenge counters (separate from `*challenges` capacity gate).
292286*challengeRates : [RateEntry]
293−-- Per-key counters for read-only lookups (presence).
294−*lookupRates : [RateEntry]
295287-- Per-source-IP counters; gate for unauthenticated traffic.
296288*ipRates : [RateEntry]
297289
@@ -321,7 +313,7 @@ type RateEntry = {key: Text, windowStart: Timestamp, count: Int}
321313-- ============================================
322314-- Public HTTP surface, grouped by audience:
323315-- AuthApi -- challenge/verify (client identity)
324−-- ClientApi -- /poll, /messages (send), presence lookup
316+-- ClientApi -- /poll, /messages (send)
325317-- FederationApi -- inter-server gossip, forward (single + batch)
326318-- ReadyApi -- observability
327319-- Response field shapes are kept stable for backwards compatibility even when
@@ -334,8 +326,6 @@ route AuthApi where
334326route ClientApi where
335327 POST {cursor: Timestamp, generation: Int} /poll headers {authorization: Text, clientIp: Text} -> {events: [PollEvent], cursor: Timestamp, authorized: Bool, rateLimited: Bool, generation: Int, heartbeat: Bool, retryAfterMs: Int, cursorReset: Bool, hasMore: Bool, cursorStale: Bool, loadPct: Int} = Poll
336328 POST {messages: [{to: PubkeyHex, encryptedBlob: BlobHex}]} /messages headers {authorization: Text, clientIp: Text} -> {results: [{error: Text}], error: Text, retryAfterMs: Int} = SendMessages
337− GET /lookup/{pubkey: PubkeyHex} headers {authorization: Text, clientIp: Text} -> {server: ServerName, authorized: Bool, error: Text} = LookupPresence
338− POST {pubkeys: [PubkeyHex]} /lookup/batch headers {authorization: Text, clientIp: Text} -> {results: [{pubkey: PubkeyHex, server: ServerName}], authorized: Bool, error: Text} = BatchLookupPresence
339329
340330route FederationApi where
341331 POST {events: [GossipEvent], fromServer: ServerName} /federation/gossip -> {ok: Bool, error: Text, retryAfterMs: Int} = RecvGossip
@@ -518,9 +508,8 @@ isBadServerName = \sn -> sn == "" || sn == "localhost" || not all (chars sn) (\c
518508-- ============================================
519509-- Auth helpers
520510-- ============================================
521−-- Bearer token extraction, session lookup, stats secret check, and the
522−-- `gateAuth`/`gateLookup` combinators that every client handler funnels
523−-- through (global rate → per-key rate → session).
511+-- Bearer token extraction, session lookup, and the `gateAuth` combinator that
512+-- every client handler funnels through (global rate → session).
524513-- Federation requests are unauthenticated: anyone can hit /federation/*.
525514-- Per-recipient `recvRate` and `no_presence` checks bound the spam ceiling.
526515
@@ -543,19 +532,6 @@ gateAuth = \auth ip t mkErr onAuth -> do
543532 let err = \e -> yield (mkErr e)
544533 if g then err "rate_limited" else withSessionAuth auth t ip (err "unauthorized") onAuth
545534
546−-- gateAuth + per-key lookup-rate gate. `rateResp` is the response value when
547−-- the per-key gate trips.
548−gateLookup : Text -> Text -> Int<Ms> -> (Text -> a) -> a -> (Text -> IO {} a) -> IO {rw *globalRateCount, rw *globalRateWindowStart, r *sessions} a
549−gateLookup = \auth ip t mkErr rateResp onAuth ->
550− gateAuth auth ip t mkErr (\pk -> do
551− lim <- atomic do
552− rates <- *lookupRates
553− let r = checkRate rates pk maxLookupRatePerKey lookupRateWindowMs t
554− -- TODO: make this no be a `replace`
555− *lookupRates = r.entries
556− yield r.limited
557− if lim then yield rateResp else onAuth pk)
558−
559535-- Resolve a Bearer token to its session pubkey if the session is unexpired and
560536-- the client IP matches. Runs `onAuth pubkey` on success, `onUnauth` otherwise.
561537withSessionAuth : Text -> Int<Ms> -> Text -> IO e a -> (PubkeyHex -> IO e a) -> IO {r *sessions | e} a
@@ -696,10 +672,6 @@ nextSeqTs = \t seqCounter -> max t (seqCounter + 1)
696672presenceForKey : [PresenceEntry] -> PubkeyHex -> Timestamp -> [PresenceEntry]
697673presenceForKey = \presence pubkey t -> filter presence (\p -> p.pubkey == pubkey && p.expiresAt > t)
698674
699−-- Resolve a key's server for client lookup, masking remote identities behind "remote".
700−maskedServer : [PresenceEntry] -> PubkeyHex -> Timestamp -> Text
701−maskedServer = \pres pubkey t -> (\here -> if any here (\p -> isLocalServer p.server) then serverHost else if count here > 0 then "remote" else "") (presenceForKey pres pubkey t)
702−
703675-- Project items through `getServer`, drop our own hostname, and dedupe.
704676-- Used to derive peer recipient lists from forwards/presence/etc.
705677collectPeers : [a] -> (a -> Text) -> [Text]
@@ -1155,43 +1127,6 @@ forkForwardsTo = \to encryptedBlob servers -> forEach servers (\server -> do
11551127 msgs <- *messages
11561128 *messages = filter msgs (\m -> m.encryptedBlob != encryptedBlob)))))
11571129
1158−-- ============================================
1159−-- Handler: lookup
1160−-- ============================================
1161−-- Presence resolution for clients deciding where to send. Remote presence is
1162−-- masked to the literal string "remote" rather than leaking the peer hostname
1163−-- (the routing decision is the server's, not the sender's).
1164−
1165−-- GET /lookup/{pubkey}: return our local server name if the key is here,
1166−-- "remote" if known on a peer, "" otherwise. Peer hostnames are deliberately
1167−-- masked (see section header).
1168−handleLookupPresence : PubkeyHex -> Text -> Text -> IO {rw *globalRateCount, rw *globalRateWindowStart, r *sessions, clock} {authorized: Bool, error: Text, server: Text}
1169−handleLookupPresence = \pubkey authorization clientIp -> do
1170− t <- now
1171− let mkErr = \e -> {server: "", authorized: False {}, error: if e == "rate_limited" then e else ""}
1172− gateLookup authorization clientIp t mkErr {server: "", authorized: True {}, error: "rate_limited"} (\_ -> do
1173− pres <- *presence
1174− yield {server: maskedServer pres pubkey t, authorized: True {}, error: ""})
1175−
1176−-- POST /lookup/batch: same masking as `handleLookupPresence`, but for up to
1177−-- 50 pubkeys in one round-trip.
1178−handleBatchLookupPresence : [PubkeyHex] -> Text -> Text -> IO {rw *globalRateCount, rw *globalRateWindowStart, r *sessions, clock} {authorized: Bool, error: Text, results: [{pubkey: PubkeyHex, server: Text}]}
1179−handleBatchLookupPresence = \pubkeys authorization clientIp -> do
1180− t <- now
1181− let mkErr = \e -> {
1182− results: [],
1183− authorized: False {},
1184− error: if e == "rate_limited" then e else ""
1185− }
1186− gateLookup authorization clientIp t mkErr {
1187− results: [],
1188− authorized: True {},
1189− error: "rate_limited"
1190− } (\_ -> do
1191− pres <- *presence
1192− let results = pluck (takeRelation 50 pubkeys) (\k -> {pubkey: k, server: maskedServer pres k t})
1193− yield {results, authorized: True {}, error: ""})
1194−
11951130-- ============================================
11961131-- Handler: federation receive
11971132-- ============================================
@@ -1306,8 +1241,6 @@ api = serve Api where
13061241 VerifyAuth = \{pubkey, challenge, signature, revokeOthers, clientIp} -> handleVerifyAuth pubkey challenge signature revokeOthers clientIp
13071242 Poll = \{cursor, generation, authorization, clientIp} -> handlePoll cursor generation authorization clientIp
13081243 SendMessages = \{messages, authorization, clientIp} -> handleSendMessages messages authorization clientIp
1309− LookupPresence = \{pubkey, authorization, clientIp} -> handleLookupPresence pubkey authorization clientIp
1310− BatchLookupPresence = \{pubkeys, authorization, clientIp} -> handleBatchLookupPresence pubkeys authorization clientIp
13111244 RecvGossip = \{events, fromServer} -> handleRecvGossip events fromServer
13121245 ForwardMessage = \{toKey, encryptedBlob, fromServer} -> handleForwardMessage toKey encryptedBlob fromServer
13131246 FederationBatchForward = \{forwards, fromServer} -> handleFederationBatchForward forwards fromServer