Skip to content

🔒 Invitations: config-gated lifetime cap per inviter #3986

Description

@PierreBrisorgueil

rateLimit.invitationsCreate (prod 20/15min, wired on POST /api/invitations) bounds burst rate, but a sliding window never bounds cumulative volume: a patient client staying under the window cap can create invitations indefinitely. When billing.referral rewards are enabled, total invitations per account — not rate — is the exposure that matters (credit farming via fake referrer/referee pairs).

Mechanism (config-gated, stack-neutral):

  • DB-backed check in invitations.service.js::create(): count invitations with the same invitedBy (all statuses), reject with the existing 422 AppError pattern (same shape as the self-invite guard) once the count reaches config.invitations.maxLifetime.
  • Default: absent/null = disabled (mirrors billing.referral, OFF at stack level). Downstream consumers opt in with a number in their config layer.
  • Tests: capped path (422 at threshold), boundary (cap−1 passes), uncapped default (no behavior change).

Out of scope: the burst limiter itself (already wired, per-route profile).

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions