Keys are `_enc` (02, top: `crypto/secrets.ts`): the API returns `hasKey`, never the key; the audit
log masks it. Validated with zod (`packages/shared`).
### 7.1 Settings → Spam check
- **Spam check on** (the switch asks to confirm the first time, §8).
- **Use IPQualityScore**: key, **Test** (the Credit Usage API: shows the credits left without
spending one), "paused until credits come back" while paused.
- **Use the FTC list**: api.data.gov key (a link to its sign-up), **Test** (one request),
re-download every N hours, keep N days, last sync time and result, **Sync now**. With both on,
the tab says IPQualityScore answers first and the FTC list when it can't.
- Keep answers: flagged N days, others N days.
- Desk phone labels: high, medium. Saving refuses a label `phoneSafeName` would change.
- Test buttons use the stricter Test rate limit, as the others do (04 §2.2).
## 8. Privacy
- Spam check is off until the admin turns it on. With IPQualityScore it is an exception to
"nothing leaves the server": the caller's number (and nothing else: no name, no extension)
goes to IPQualityScore. The FTC list sends nothing about calls.
- Turning on IPQualityScore asks once: "The numbers of outside
callers who aren't in your contacts will be sent to IPQualityScore. You may need to say so in
your privacy notice, and to have a data processing agreement with them. In the EU and UK the
GDPR applies." The audit log records who turned it on.
- GDPR covers businesses in the EU or UK and calls from people there, so it isn't the owner's case
(US callers only). The admin guide says so for everyone else, along with US state privacy laws
such as California's. It's not legal advice.
- Admin guide: a new page `docs/admin-guide/spam-check.md` (what each provider is, what leaves the
server, the FTC caveat, the free allowance) and a line in `legal.md`.
## 9. Order of work
| Task | What | Done when |
|---|---|---|
| SD1 | Decision 0006; this spec; the §7 migration and settings; `spam/check.ts` with both providers' lookups, the cache and the shared requests; Settings → Spam check with keys, Test, privacy confirmation | `pnpm lint && typecheck && test` pass; checks, cache times, the order (IPQualityScore, then the FTC list) and failures are tested against fake HTTP |
| SD2 | §3: pause, events, the `spam_quota` warning and Dismiss, `spamQuotaCheck` | an exhausted fake pauses, falls back to the FTC list, warns once and resumes; the owner's real key shows the credit call is free |
| SD3 | §2.2 sync: paging, pacing, the saved cursor, the window, Sync now | a fake FTC API with 3 days of data syncs, resumes after a 429 and drops old complaints; one real sync with the owner's key |
| SD4 | §5: `/api/me/caller-check`, the softphone badge and line, the desk phone labels | an unknown number rings the softphone and shows its answer; a contact or a low risk shows nothing; a desk phone shows "SPAM LIKELY" when the answer is in time |
| SD5 | §6 call history; the admin guide page | a checked call keeps its badge in call history after its cache expires |
Later, not now:
- **Report as spam** (owner, 2026-10-27: not now): a button in call history that reports the
number to a provider that takes reports (OpenSpam has `POST /api/report`).
- **OpenSpam** as a provider for Spanish numbers (the owner has a key; it has little US data).
- **Low-risk details** (owner, 2026-10-27: nothing for now): show what IPQualityScore knows
about a low-risk caller too (line type, carrier, city, name), without a badge.
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.