feat(notification): implement the notification delivery platform
Maps the 31-module plan onto the registry's 19 leaves as packages; the two edges the registry forbids (provider->httpclient, inbox->messaging) are replaced by application-owned ports. See docs/notification/module-mapping.md. Acceptance is not delivery: ProviderSubmissionResult refuses to carry a delivery outcome, and AMBIGUOUS is a first-class terminal state that blocks automatic retry and fallback until reconciliation resolves it. Providers: SES (SigV4 + SNS callback), Twilio (X-Twilio-Signature + reconciliation), FCM (FID-primary batch), APNs, Web Push (RFC 8030/8291/8292), SMTP and webhook. Contact points are AES-256-GCM encrypted with a separate HMAC lookup fingerprint; nothing raw reaches a log, metric tag or exception. Dispatch commits the attempt row, calls the provider with no transaction open, then records the outcome; the durable queue uses FOR UPDATE SKIP LOCKED. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3b5aee50e3
commit
701ba67456
@@ -0,0 +1,68 @@
|
||||
# Notification support matrix
|
||||
|
||||
What each channel can actually prove, and what the platform refuses to claim.
|
||||
|
||||
## Channels
|
||||
|
||||
| Channel | Reference implementation | Grade | Strongest evidence the platform records by default |
|
||||
|---|---|---|---|
|
||||
| Email | SMTP, Amazon SES API | Stable | Provider acceptance; recipient mail-server delivery, bounce and complaint when the provider publishes events |
|
||||
| SMS | Twilio Programmable Messaging | Stable | `accepted`/`queued`, `sent`, and carrier-DLR `delivered`/`undelivered` |
|
||||
| Mobile push (Android and cross-platform) | FCM, FID-first with legacy registration token compatibility | Stable | FCM acceptance and explicit failures |
|
||||
| Mobile push (Apple) | APNs HTTP/2 provider API | Stable | APNs acceptance |
|
||||
| Web Push | RFC 8030, RFC 8291, RFC 8292 | Stable | Push-service acceptance; user-agent acknowledgement only where the service offers receipts |
|
||||
| In-app inbox | Own database | Optional stable | `PERSISTED`, `SEEN`, `READ` |
|
||||
| Webhook | HTTP client platform | Extension | Whatever the receiving HTTP contract states |
|
||||
|
||||
## Evidence levels
|
||||
|
||||
`NONE` → `PLATFORM_QUEUED` → `PROVIDER_ACCEPTED` → `NETWORK_OR_CARRIER_ACCEPTED` →
|
||||
`DEVICE_DELIVERED` → `USER_AGENT_DISPLAYED` → `USER_READ`
|
||||
|
||||
| Provider signal | Highest evidence it may produce |
|
||||
|---|---|
|
||||
| Internal queue commit | `PLATFORM_QUEUED` |
|
||||
| SES `MessageId` | `PROVIDER_ACCEPTED` |
|
||||
| SES `Delivery` | `NETWORK_OR_CARRIER_ACCEPTED` |
|
||||
| Twilio `accepted` / `queued` | `PROVIDER_ACCEPTED` |
|
||||
| Twilio `sent` | `NETWORK_OR_CARRIER_ACCEPTED` |
|
||||
| Twilio `delivered` | `DEVICE_DELIVERED` |
|
||||
| FCM send success | `PROVIDER_ACCEPTED` |
|
||||
| APNs 2xx | `PROVIDER_ACCEPTED` |
|
||||
| Web Push `201` | `PROVIDER_ACCEPTED` |
|
||||
| Web Push receipt capability | `DEVICE_DELIVERED` |
|
||||
| In-app row commit | `PROVIDER_ACCEPTED` |
|
||||
| In-app `seen` endpoint | `USER_AGENT_DISPLAYED` |
|
||||
| In-app `read` endpoint, authenticated app receipt | `USER_READ` |
|
||||
|
||||
Promotions the platform will not make, in code or in configuration:
|
||||
|
||||
- FCM send success is not `DEVICE_DELIVERED`.
|
||||
- An APNs 2xx is not `DELIVERED`.
|
||||
- An SES `MessageId` is not `DELIVERED`.
|
||||
- An SMTP `250` is not inbox delivery.
|
||||
|
||||
## Submission outcomes
|
||||
|
||||
`NOT_SUBMITTED`, `CONFIRMED_ACCEPTED`, `CONFIRMED_REJECTED`, `AMBIGUOUS`.
|
||||
|
||||
`AMBIGUOUS` is a first-class stored state, not an error path. It means the request body was committed
|
||||
to the provider and the outcome could not be read. While an ambiguous attempt exists on a recipient
|
||||
delivery, automatic retry and automatic cross-channel fallback are both blocked.
|
||||
|
||||
## Not supported
|
||||
|
||||
The platform will not claim any of the following, because no channel above can support them:
|
||||
|
||||
- guaranteed delivery
|
||||
- guaranteed read
|
||||
- exactly-once human notification
|
||||
- unconditional multi-provider failover after an unread response
|
||||
- provider SDK types in the public API
|
||||
- audience selection, campaign segmentation or jurisdiction rulings
|
||||
|
||||
## Target model
|
||||
|
||||
`FCM_FID` is the primary mobile push target. `FCM_REGISTRATION_TOKEN_LEGACY` and
|
||||
`APNS_DEVICE_TOKEN` are separate types with separate lifecycles; they are never flattened into one
|
||||
string field.
|
||||
Reference in New Issue
Block a user