screentinker/server
ScreenTinker 9e3222a503 fix(#146) B: sustained flap-rate limiter (the trigger fix), SNAT-safe identity chain
The #142 burst throttle (5/10s) misses a device flapping every 3-5s (~2-3/10s) — yet
each cycle is an expensive register+build+acks and one status_log row (the spiral
trigger). Now that Item A ends the restart loop that used to wipe in-memory throttle
state every ~40s, an in-memory sustained limiter can finally bite.

- lib/device-identity.js: SNAT-safe identity resolution — device_id -> fingerprint
  (map via device_fingerprints -> device_id, else raw fp) -> device_token -> ONE bounded
  global anon bucket. NEVER IP (the fleet SNATs to 10.10.10.1). An unidentifiable client
  is still bucketed (collectively) so an anon flood is capped, never unthrottled.
- lib/flap-limiter.js: per-identity connect-frequency over a long window
  (CONNECT_RATE_WINDOW_MS=5min, CONNECT_RATE_MAX=20; anon bucket cap 60). Over the rate
  -> refuse + disconnect (cheap). Bounded by an idle sweep (anon bucket never swept).
  Optional auto-quarantine: a device_id-resolved hard flapper -> blocked=1.
- Wired at the device:register gate BEFORE fingerprint tracking/throttle/DB/build,
  skipping same-socket playlist refreshes. Sweep started in server.js.

Tests: 4s-flapper refused after the window max; 60s-normal never; two device_ids
independent (never IP); device_id-less bucketed by fingerprint; neither id nor
fingerprint capped via global anon; idle sweep preserves the anon bucket. Suite 254/254.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 20:59:53 -05:00
..
config PiP overlay MVP: push image/web overlays to a device or group (#109) (#127) 2026-06-18 14:54:44 -05:00
db fix(#146) A: non-blocking maintenance — chunked+yield+band-gate all sweeps 2026-06-30 20:55:05 -05:00
lib fix(#146) B: sustained flap-rate limiter (the trigger fix), SNAT-safe identity chain 2026-06-30 20:59:53 -05:00
middleware feat(api): per-agency-token auto-publish (#73) 2026-06-14 13:48:17 -05:00
player fix(player): composite multi-zone layouts in screenshot/stream capture 2026-06-22 23:22:12 -05:00
routes feat(#142): event-loop lag telemetry (perf_hooks) + bounded storage 2026-06-27 19:01:08 -05:00
scripts feat(scheduling): per-item schedule blocks (#74 dayparting, #75 auto-expire) 2026-06-11 15:46:41 -05:00
services fix(#146) A: non-blocking maintenance — chunked+yield+band-gate all sweeps 2026-06-30 20:55:05 -05:00
test fix(#146) B: sustained flap-rate limiter (the trigger fix), SNAT-safe identity chain 2026-06-30 20:59:53 -05:00
ws fix(#146) B: sustained flap-rate limiter (the trigger fix), SNAT-safe identity chain 2026-06-30 20:59:53 -05:00
.gitignore feat(email): Microsoft Graph send + alert spam protection + preferences UI 2026-05-12 18:16:40 -05:00
config.js fix(#146) B: sustained flap-rate limiter (the trigger fix), SNAT-safe identity chain 2026-06-30 20:59:53 -05:00
package-lock.json chore(release): v1.9.2-beta6 2026-06-29 23:38:35 -05:00
package.json chore(release): v1.9.2-beta6 2026-06-29 23:38:35 -05:00
server.js fix(#146) B: sustained flap-rate limiter (the trigger fix), SNAT-safe identity chain 2026-06-30 20:59:53 -05:00
version.js chore(version): single-source VERSION, env-configurable data paths, bump tooling 2026-06-10 12:56:03 -05:00