# 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 ` 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..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`. |