django-axes, in TypeScript
Persistent failed-attempt tracking keyed on username, IP, combinations, or +user_agent. Trip a failure limit and the identity is locked out for a cooloff — including tiered cooloff that escalates as failures accumulate.
Framework-agnostic core
@authlock/core has zero runtime dependencies — no NestJS, no DI, no decorators. Use it from Express, inversify, tsyringe, or a bare script. The neutral core is the cross-framework story.
Pluggable store
A LockoutStore seam — an in-memory store ships in the box, and a Drizzle-backed store (SQLite, Postgres, MySQL) is the batteries-included default, with an atomic increment that is correct across instances.
Honest NestJS adapter
NestJS has no ambient login-failure signal, so @nest-native/lockout is explicit by design: a LockoutGuard, a LockoutService you call from your login handler, and a Passport recipe.
fail-open by default
If the store errors, the engine allows and logs — a DB blip must not lock every user out. Flip failMode: 'closed' for high-security deployments. Both paths are tested.
Correctness is the product
This is security-critical code: the atomic cross-instance increment, the any-key-trips-the-lock rule, and both failure modes are covered to 100%, and every change ships with a security pass.