ubiquitous-invention/plans/Plan-multitenant-saas-hardening/Epic-tenant-lifecycle/Task-audit-instrument-rate-limit-trips.md

43 lines
2.1 KiB
Markdown
Raw Permalink Normal View History

---
kind: task
slug: audit-instrument-rate-limit-trips
title: Write audit-log rows on every rate-limit trip
plan_slug: multitenant-saas-hardening
epic_slug: tenant-lifecycle
status: draft
priority: P2
tenant_id: global
owner: unassigned
cursor_todo_id: null
updated_at: "2026-06-02"
---
# Task summary
Rate-limit trips currently only go to stdout (`console.warn`). The parent task spec called for an `audit_log` row on every trip so we can:
1. See in the audit view whether anyone's actually hitting the limits in practice.
2. Build abuse-signal dashboards on top of `audit_log` instead of scraping logs.
This is a small follow-up because we kept `Task-rate-limit-and-abuse-guardrails` scoped to "the wire-ins ship cleanly" and didn't want to churn `auth.ts` and `invites.ts` a second time inside the same task.
## Subtasks
- [ ] At every `if (!rl.allowed) { ... }` site, call `recordAudit(...)` before the rejection. For the Credentials path, the workspace context isn't known (the user isn't even authenticated yet), so we need a different write path:
- Option A: skip audit for sign-in trips and only log to stdout (current behavior).
- Option B: add a `system_audit_log` table for non-workspace events (sign-in attempts, rate-limit trips on unauthenticated routes).
- Option C: stamp `workspace_id = '00000000-0000-0000-0000-000000000000'` as a sentinel "system" workspace.
- Recommended: **A for v1** (no schema change), revisit when we have a real need for cross-workspace audit. For workspace-scoped trips (invite-create), use the regular `recordAudit()` flow.
- [ ] Wire `recordAudit` into the `invites.create` rate-limit branch. Action: `rate_limit.tripped`. Metadata: `{ key, retry_after_ms, limit, window_ms }`.
- [ ] Add the `rate_limit.tripped` action to the audit page's `ACTION_LABELS` map.
## Acceptance criteria
- [ ] Triggering the `invites.create` rate-limit produces a `rate_limit.tripped` row in the audit view.
- [ ] Sign-in trips remain logged to stdout (decision A above) until a `system_audit_log` table is justified.
## Links
- Parent: `./Task-rate-limit-and-abuse-guardrails.md`
- Epic: `./Epic-tenant-lifecycle.md`