Read-only ActivityPub → atproto bridge for the threadiverse using Coves lexicons

Task 11: pre-internet-facing hardening — admission control, #account frame, pruners master

The internet-facing pass over the FOLLOWUPS backlog. Biggest diff of the loop; 17 FOLLOWUPS items closed. Admission control (new internal/ratelimit shared package — keyed token bucket, sweep throttle, 50k fail-closed cap, RemoteAddr keying): - /inbox: per-IP limit before the body read (bounds signature-verify cost), per-signer limit after verify, and a dedicated tighter per-IP cap INSIDE tombstonedSelfDelete before the outbound confirmation fetch — closing the SSRF/durable-write amplification path task 10 flagged. All inbox refusals are 503 (Lemmy drops 4xx forever, retries 5xx). - Sync surface: per-IP limit (429) + SYNC_MAX_SUBSCRIBERS connection cap (reserve-then-check, leak-free on every exit). - Refusals are observable: per-surface expvar counters + sampled Warn (a review caught that a mistuned limit would otherwise drop all traffic silently). /admin/metrics scoped to tidepool's own expvar keys. New firehose wire surface — the #account frame: - migration 011: firehose_events.kind (commit|account) + account_active/ account_status, commit columns nullable under a per-kind shape CHECK. - AppendAccountEvent shares the global commit advisory lock + in-tx pg_notify, so seq==visibility holds across both kinds and the frame always replays after the scrubs that precede it. - DeleteActor emits #account{active:false,status:deleted} LAST (verified through a real bigsky: the repo is carstore-purged, not just filtered — "deleted" is the only status token that purges, source-confirmed). Terminal state is a fixpoint (no double emit on redelivery). Protocol correctness & scrubs: - commitRecord writes record + ap_objects mapping in ONE tx via PutRecordTx/TxSideEffect; the NoOp path still commits the mapping while preserving the deterministic-rkey true-NoOp contract (no new seq/event). - DeleteActor scrubs blobs under community DIDs + own repo + the actor's vote_events (ScrubVoter, ordered aggregate locks, DELETE...RETURNING recompute so a racing vote can't leave a phantom count); a failed blob delete is retryable, not swallowed. - Media carry-forward on transient profile-refresh failure keeps the stored blob; a permanent 404/410 correctly drops it. - FollowRetrier: atomic UPDATE...RETURNING claim (no Accept clobber, no double-claim), loud at attempt exhaustion. Housekeeping: three batched pruners (ap_tombstones, undone vote_events, firehose) via a shared fail-closed retention runner; MAX_BLOB_BYTES wired to the AP client's response cap; MaxSeededCount sanity cap; service_keys.private_key_pem renamed to key_material; dead NewNoopVotes removed. delete-before-create: README was right (task 06 closed it), the FOLLOWUPS entry was stale — struck. Review: 6/7 delivered (glm watchdog-killed). No confirmed high-severity; gemini's lone HIGH (carry-forward assertion) was a false positive (typed atdata.Blob, test green) and discarded. Verification: unit suite green (incl. new prune/ratelimit packages), final clean make e2e 17/17 (232s). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>