feat(db): WhatsApp channel schema (phone links, messages, inbox source) (#1337)

Foundation for WhatsApp receipt intake (plan 2026-08-01), no runtime
callers yet:

- whatsapp_phone_links + whatsapp_link_codes: verified phone -> user
  binding via single-use 10-min codes (invite-token pattern). Peppered
  HMAC lookup hash + AES-GCM encrypted number; one ACTIVE link per
  phone and per user (partial unique, revocation preserves history).
  User-scoped RLS (a binding belongs to a person, not a company).
- whatsapp_conversations + whatsapp_messages: deterministic state
  machine state and the message log, which doubles as the durable job
  record for persist-first webhook processing. Partial unique index on
  inbound wamid = the at-least-once dedupe key. Service-role only.
- whatsapp_sender_rate_counters + check_and_increment_whatsapp_sender_quota:
  the pre-binding limiter keyed by phone hash; EXECUTE granted to
  service_role only.
- invoice_inbox_items: source CHECK widened to 'whatsapp', plus
  whatsapp_message_id (one item per delivering message) and
  channel_context jsonb, kept separate from extracted_data so verified
  human answers never share a container with untrusted OCR output.
  document_attachments.upload_source CHECK gains 'whatsapp'.
- whatsapp_conversations triaged into ARCHIVE_EXCLUDED_TABLES
  (full-archive coverage contract).

pg-real on a fresh DB: 975/975 incl. the new whatsapp-channel suite
(RLS visibility, unique/rebinding semantics, wamid dedupe, CHECK
widenings, quota RPC caps + grant lockdown).

Co-authored-by: Jakob Wennberg <jakob.wennberg@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-05 14:36:00 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent 02e48efe11
commit bd3592cb99
7 changed files with 721 additions and 1 deletions
+2
View File
@@ -978,6 +978,8 @@ export const ARCHIVE_EXCLUDED_TABLES: Record<string, string> = {
stripe_payment_events: 'mirror of Stripe data, re-fetchable at source',
stripe_payouts: 'mirror of Stripe data, re-fetchable at source',
webhook_deliveries: 'automation delivery log',
whatsapp_conversations:
'WhatsApp bot conversation state (company_id is only a which-company pin); receipts live in document_attachments',
webhooks: 'automation config with signing secrets',
}