Files
where_woof/openspec/changes/scan-limits-gating/proposal.md

28 lines
1.8 KiB
Markdown

## Why
The product needs cost protection and the 2014 paid-gating model, but a payment gateway (Stripe) is explicitly deferred. This change delivers the **mechanism** without the gateway: alerts only fire for tags whose linked order is `paid` (the 2014 `is_paid`/`late_payment` suspension), plus hard **per-tag daily alert caps** and a **per-IP rate limit** — all pure app logic on the existing schema. Stripe/Paddle plugs in later (Phase 5 full) by just setting `orders.status`.
## What Changes
- **Paid-gating** (`sms-alerting`): if a tag has a linked order (`order_id` set) and that order's status is not `paid`, scans are recorded but **no SMS is sent** (owner sees "payments suspended" behaviour). No order yet → alerts allowed (transitional until sales flow exists).
- **Per-tag daily alert cap**: a tag can alert at most N times per 24 h (configurable constant, e.g. 5) — protects SMS cost against heavy repeated scanning.
- **Per-IP rate limit**: a scanner IP can trigger at most M alerts per hour (configurable) — guards against automation (works alongside the existing fingerprint block).
- **Admin visibility**: TagResource already links orders; the order status drives the gating — no new admin UI needed.
## Capabilities
### New Capabilities
- `alert-limits`: per-tag daily cap + per-IP hourly rate limit.
### Modified Capabilities
- `sms-alerting`: ADDED — paid-order gating (no order = allowed, transitional; non-paid order = blocked).
- `database`: no schema change required (orders.status + tag.order_id already exist).
## Impact
- **Go**: `internal/handlers/scan.go` (`shouldAlert` gains paid-gating + cap checks), `internal/db/queries.sql` (2 new count queries), regenerated sqlc.
- **Constants**: `maxAlertsPerTagDay`, `maxAlertsPerIPHour` (env-overridable via `ALERT_*` vars).
- No external services; no Stripe; no schema migration.