Skip to content

Third-party lookups take a client outbound proxy slot and keep per-appservice timeouts

  • Status: accepted
  • Date: 2026-10-21

Technical Story: security audit run-1, sp02.thirdparty-no-proxy-slot

Context and Problem Statement

ADR-0079 requires every client request that makes this server call another one to take a slot of the client outbound proxy budget first, so lookups that wait on slow peers cannot hold every request thread. The /thirdparty/* routes call the registered appservices and took no slot. The bare location?alias= and user?userid= routes call every appservice in turn.

Decision Drivers

  • The budget only works if every client-triggered outbound route takes part.
  • A server with no appservice to call must not answer 429.
  • Aggregated results should not be cut short.

Considered Options

  • Take a slot on every route that will call an appservice; keep each appservice's own timeout
  • Take a slot and also run the whole route under one shared deadline

Decision Outcome

Chosen option: "take a slot; keep each appservice's own timeout". Each of the six routes takes a slot before runtime.mutex is released, holds it until it returns, and answers 429 M_LIMIT_EXCEEDED with retry_after_ms when none is free. It takes one only when it will call an appservice: one with protocols for /protocols, the protocol's owners for the {protocol} routes, and any registered appservice for the bare routes.

Positive Consequences

  • A client's third-party lookups count against the same per-client and global caps as directory and media lookups.

Negative Consequences

  • A request still takes up to the sum of the appservices' timeouts, though it holds only its one slot while it does.

Pros and Cons of the Options

One shared deadline

  • Good, because it bounds a request's duration.
  • Bad, because the appservice client has no deadline parameter, and a deadline would drop later appservices' answers from the aggregate.