Files
suaveplan/openspec/archive/2026-05-25-enterprise-sms-providers/proposal.md
2026-05-27 23:29:53 +01:00

51 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Enterprise migration — `@suaveplan/sms-providers`
## Why
`@suaveplan/sms` shipped a clean send/receive/template/scheduler/opt-out core with `InMemorySmsProvider` as the canonical test double. Production deployments need real provider clients — Twilio, Vonage, MessageBird, Plivo, AWS SNS — each speaking its own REST dialect, authenticating with its own scheme, returning its own status vocabulary, and signing its own webhooks. Upstream `packages/universal/sms-providers` shipped *simulated* providers (random IDs, no network IO, no signature verification) — not enterprise grade.
This change ports the package fresh under `packages/communication/messaging/sms-providers/` (per `assignment.yml`), replacing every simulated implementation with real HTTP clients over an injectable `FetchLike` seam, real HMAC/signature verifiers for each provider's inbound webhook contract, and a typed `SMS_PROVIDERS.*` error registry. Tests bind a real `node:http` server on a random port for end-to-end flows; no mocks.
## What changes
- **ADDED** Package `@suaveplan/sms-providers` at `packages/communication/messaging/sms-providers/`, version `0.1.0`, universal-tier `src/` (zero hard browser/node deps; injectable `fetch` + injectable HMAC seam).
- **ADDED** `SMS_PROVIDERS.*` namespaced error code registry via `defineCodes("SMS_PROVIDERS", […])` with eight codes: `HTTP_ERROR`, `TIMEOUT`, `PARSE_ERROR`, `CONFIG_MISSING`, `SIGNATURE_INVALID`, `INVALID_NUMBER`, `PROVIDER_NOT_REGISTERED`, `PROVIDER_ALREADY_REGISTERED`.
- **ADDED** `SmsProvidersError` discriminated subclass of `SuaveplanError` carrying `provider`, `httpStatus`, `url`, `bodyExcerpt` discriminants; factory helpers per code.
- **ADDED** Slim foundation: `httpRequest` over `FetchLike` with composed `AbortSignal` + per-call timeout + JSON parse + typed error translation; `composeAbortSignal` helper.
- **ADDED** `ProviderRegistry` indexing `SmsProvider` instances by name (insertion-ordered, strict register/lookup, frozen `list()`).
- **ADDED** Five real provider clients implementing `SmsProvider` from `@suaveplan/sms`:
1. `TwilioProvider` — `POST /2010-04-01/Accounts/{AccountSid}/Messages.json`, HTTP Basic auth (`AccountSid:AuthToken`), form-encoded body, MMS `MediaUrl` pass-through, status URL → polled, X-Twilio-Signature HMAC-SHA1 webhook verifier.
2. `VonageProvider` — `POST /sms/json`, `api_key`+`api_secret` body params, alphanumeric / short-code / long-code `from`, HMAC-SHA256 webhook signature verifier honouring `sig`/`timestamp`.
3. `MessageBirdProvider` — `POST /messages`, `AccessKey <key>` auth header, JSON body, `Messagebird-Signature-Jwt` JWT (HS256) webhook verifier with timestamp freshness window.
4. `PlivoProvider` — `POST /v1/Account/{AuthId}/Message/`, HTTP Basic auth (`AuthId:AuthToken`), JSON body, `X-Plivo-Signature-V3` HMAC-SHA256 webhook verifier (canonical-string of URL+nonce+body).
5. `AwsSnsProvider` — `POST /` to `sns.{region}.amazonaws.com` with AWS SigV4 signing (computed inline; no aws-sdk dep), `Action=Publish` form body, message-attribute mapped sender + `SMSType` (Transactional/Promotional); SNS webhook verifier accepts subscription confirmation, notification, and unsubscribe-confirmation payloads with PEM-cert injectable resolver.
- **ADDED** Per-provider raw-status → `SmsDeliveryStatus` map; injectable `Clock` + `IdGenerator` per provider for deterministic message-id fallbacks (when the provider does not return one synchronously).
- **ADDED** Inbound webhook router pipeline per provider: signature verify → header timestamp freshness check → discriminated `InboundSmsEvent` (`message_received` | `delivery_receipt` | `opt_out` | `opt_in`) — opt-in/opt-out keywords (STOP/UNSTOP/HELP family) recognised from inbound body.
- **ADDED** OTel instruments (5):
- counter `sms_providers.send_total{provider,outcome}`
- counter `sms_providers.webhook_total{provider,event,outcome}`
- counter `sms_providers.signature_failures_total{provider}`
- histogram `sms_providers.send_duration_ms{provider}`
- histogram `sms_providers.webhook_verify_duration_ms{provider}`
- **ADDED** Real `node:http` round-trip integration tests per provider (test binds an HTTP server on a random port, captures the request, returns a canned response, asserts URL/method/headers/body) and real-`node:crypto`-driven webhook signature suites (accept + reject paths).
- **ADDED** Co-located `.md` per `.ts` (six required sections, ≥200 words excl. code) and a root `README.md`.
- **MODIFIED** STATUS.md row for `@suaveplan/sms-providers` flipped `–` → `✅`.
## Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
| `TwilioProvider` simulated send | ✅ | ⬆️ | Replaced: real HTTP Basic + form-encoded POST + MMS `MediaUrl` + status poll + X-Twilio-Signature HMAC-SHA1 verifier. |
| `VonageProvider` simulated send | ✅ | ⬆️ | Replaced: real POST + alpha/short/long sender parsing + HMAC-SHA256 webhook verifier. |
| `MessageBirdProvider` simulated send | ✅ | ⬆️ | Replaced: real `AccessKey` POST + JWT (HS256) webhook verifier with timestamp freshness. |
| `AwsSnsProvider` simulated send | ✅ | ⬆️ | Replaced: real SigV4-signed POST to `sns.<region>.amazonaws.com` + PEM-cert injectable SNS verifier. |
| `PlivoProvider` | 🚫 | ✅ | ADDED — orchestrator scope explicitly names Plivo. |
| `httpRequest` helper | ✅ | ⬆️ | Re-implemented over injectable `FetchLike` + composed `AbortSignal` + `SMS_PROVIDERS.*` codes. |
| `SMS_PROVIDER_*` codes on shared `ErrorCodes` | ✅ | ⬆️ | Migrated to genesis-native `defineCodes("SMS_PROVIDERS", […])`. |
| `mapStatus(raw)` per provider | ✅ | ✅ | Preserved on every provider class. |
| Webhook signature verification | 🚫 | ✅ | ADDED — production requirement. |
| Provider registry | 🚫 | ✅ | ADDED — multiplexing by name (mirrors `@suaveplan/payment-providers`). |
| OTel instrumentation | 🚫 | ✅ | ADDED — production requirement. |
| `node:http` round-trip tests | 🚫 | ✅ | ADDED — orchestrator requirement; real in-process server, no mocks. |
| Inbound opt-in/opt-out keyword detection | 🚫 | ✅ | ADDED — required for compliance integration with `@suaveplan/sms`'s `OptOutRegistry`. |