Identities for entities did.bot

pds: one applyWrites can make 3,200 outbound DID lookups, because the per-write bound applies to each write in the batch #235

Closed permadeath.com
Closed opened by permadeath.com

What happens. The lookup bound of 16 records and 4 s is meant to apply to one write. For applyWrites, the server prefetches separately for each write in the batch, so the bound applies once per write. A 200-write batch where each record names 16 made-up DIDs starts up to 3,200 outbound lookups. Each lookup is a DID document fetch plus a listRecords. Nothing charges the caller first, and a batch the gate refuses still makes all its lookups.

How to reproduce.

  • Reader::prefetch loops over the writes and calls self.foreign.prefetch(&references).await once for each (crates/didbot-serve/src/lookups.rs:148-157).
  • Each call starts its own budget (crates/didbot-lookup/src/lib.rs:396) and its own cap, take(self.limits.per_write) (lib.rs:398). The doc at lib.rs:169 says this bounds "what a single write can cost somebody else's server".
  • The batch route passes up to MAX_APPLY_WRITES (200) writes into one call (crates/didbot-serve/src/routes/repo.rs:661-670). The write meter charges only inside admit, after the gate.
  • The 3,200 distinct answers also push everything out of the 1,024-entry lookup cache that every other write relies on.

Why it matters. Any write token turns the server into a request reflector. Records naming did:web:victim.example:uN aim thousands of HTTPS requests from the server's address at one host. Made-up did:plc values aim them at plc.directory, which the server needs for its own DID resolution.

Fixed in #1247. A write or a whole batch makes one lookup call with one deadline, fetching at most [capacity] foreign_lookups_per_request records (default 64, cached ones free). In the test, a 200-write batch naming 3,200 accounts on one host makes at most 64 requests of it, and an account cached before the batch is still cached after.

Sign up or Sign in to add to the discussion.