-- Postgres-backed login rate limiting โ€” Phase 4.1 ยง15. Replaces the in-memory sliding-window -- Map in security.js, which reset on every Platform API restart (an attacker could always just -- wait for a deploy) and would silently stop being shared the moment there's more than one -- Platform API instance. One small table, one cheap query per login attempt (this is a login -- form, not a hot path) โ€” no Redis, no new infrastructure dependency. CREATE TABLE IF NOT EXISTS login_attempts ( id BIGSERIAL PRIMARY KEY, rate_key TEXT NOT NULL, -- "ip:email", same composite key security.js always used created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- The hot query is "count attempts for this key within the last N minutes" โ€” this index makes -- that a range scan on one key instead of a table scan. CREATE INDEX IF NOT EXISTS idx_login_attempts_key_time ON login_attempts(rate_key, created_at);