ADR 0010 — A limit is a row

ADR 0010 — A limit is a row An architecture diagram generated by Archify. A limit is a row in Postgres · one row per key per window · only kit/limit writes it · Architecture component A limit is a row in Postgres one row per key per window · only kit/limit writes it platformkit_limits(key, window_start, count) · FORCE RLS · platformkit_is_system() in both directions · the mechanism platformkit_limits(key, window_start, count) FORCE RLS · platformkit_is_system() in both directions limit.Postgres(conns) · one statement · ON CONFLICT (key) DO UPDATE · own system tx · 2 s budget · the mechanism limit.Postgres(conns) · one statement ON CONFLICT (key) DO UPDATE · own system tx · 2 s budget contracts.NewLimiter(limit.Postgres(…)) · httpx.ConnFrom is conns · Check reads · Failed writes · the mechanism contracts.NewLimiter(limit.Postgres(…)) httpx.ConnFrom is conns · Check reads · Failed writes TestTwoReplicasShareOneLimit · the 4th attempt is refused from either pool · kept true by TestTwoReplicasShareOneLimit the 4th attempt is refused from either pool Rejected: a counter per process · 3 pods, 3 limits, reset by a deploy · Memory() is a fake · Architecture component Rejected: a counter per process 3 pods, 3 limits, reset by a deploy · Memory() is a fake auth-login, password/forgot, password/reset · Precheck before the tx · Login · MayAsk · MayRedeem · Architecture component auth-login, password/forgot, password/reset Precheck before the tx · Login · MayAsk · MayRedeem auth-sweep · limit.Purge, hourly · DELETE rows whose window closed a day ago · cron 0 * * * * · Architecture component auth-sweep · limit.Purge, hourly DELETE rows whose window closed a day ago · cron 0 * * * * held in written by used by checked by instead of purges closed windows a verdict before the attempt the mechanism kept true by Legend Backend Database Security External

Context

  • • Before: modules/auth's lockout was a map in the process — ten failures in fifteen minutes — whose own comment said three replicas gave thirty attempts and a deploy reset the count
  • • Then two replicas went behind one service, and the E6 review found a client's redeem route taking 1,054 guesses a second at a code space small enough to walk

Consequences

  • • The counters outlive a deploy and an attacker's window is the window; a limiter that cannot be reached allows the attempt and says so
  • • A limit costs a round trip, noise next to one argon2id hash; an hourly DELETE in auth's sweep empties windows that closed a day ago, and the memory limiter stayed as the fake

Evidence

  • • go test ./kit/limit -run TestTwoReplicasShareOneLimit, TestBothLimitersAgree and TestPurgeDropsWindowsThatClosedLongAgo
  • • go test ./migrations -run TestEveryTableIsScopedOrExemptOnPurpose; not claimed: the window edge, twice the limit for one instant, or a limit on a route that does no other work