Files
clean-architecture-backend-…/docs/notification/configuration-reference.md
T

100 lines
5.2 KiB
Markdown

# Configuration reference
The notification delivery platform binds under `ca-skeleton.notification.platform`. The tree lives in
`src/app-bootstrap/src/main/resources/application.yml`, disabled by default, and every value carries
an inline default so a deployment that leaves the platform off supplies nothing.
Until 2026-08-15 this page named properties the binding did not have — `max-retry-concurrency`,
`scheduler-poll-interval`, `callback-worker-concurrency` — and omitted three it did. There was no
tree in `application.yml` at all, so the only way to configure the platform was to guess environment
variable names from Boot's relaxed binding. `./gradlew verifyNotificationConfiguration` now fails
when this page, the YAML tree and `docs/registries/env-keys.yaml` disagree.
## Master switch
| Property | Environment variable | Default | Meaning |
|---|---|---|---|
| `enabled` | `APP_NOTIFICATION_PLATFORM_ENABLED` | `false` | Binds nothing at all while false: no runtime, no schema check, no scheduler thread, no secret required |
| `mode` | `APP_NOTIFICATION_PLATFORM_MODE` | `SERVING` | `SERVING` refuses to start without a working provider; `ACCEPT_ONLY` stores requests and does not dispatch |
## Dispatch
| Property | Environment variable | Default | Bound |
|---|---|---|---|
| `dispatch.claim-batch-size` | `APP_NOTIFICATION_PLATFORM_CLAIM_BATCH_SIZE` | `50` | 1..1000 |
| `dispatch.lease-duration` | `APP_NOTIFICATION_PLATFORM_LEASE_DURATION` | `2m` | positive, finite |
| `dispatch.poll-interval` | `APP_NOTIFICATION_PLATFORM_POLL_INTERVAL` | `1s` | positive, finite |
| `dispatch.max-global-concurrency` | `APP_NOTIFICATION_PLATFORM_MAX_CONCURRENCY` | `64` | positive |
| `dispatch.max-additional-attempts` | `APP_NOTIFICATION_PLATFORM_MAX_ADDITIONAL_ATTEMPTS` | `4` | non-negative |
| `dispatch.max-queue-age` | `APP_NOTIFICATION_PLATFORM_MAX_QUEUE_AGE` | `24h` | positive |
| `dispatch.allow-ambiguous-fallback` | `APP_NOTIFICATION_PLATFORM_ALLOW_AMBIGUOUS_FALLBACK` | `false` | boolean |
Every value is bounded. "Unlimited" is not an accepted configuration.
The lease must outlast a provider call plus its timeout. Below that, a delivery a live worker is
still waiting on gets claimed by a second worker, and the recipient receives the notification twice.
## Callbacks
| Property | Environment variable | Default | Bound |
|---|---|---|---|
| `callbacks.enabled` | `APP_NOTIFICATION_PLATFORM_CALLBACKS_ENABLED` | `false` | boolean |
| `callbacks.max-body-bytes` | `APP_NOTIFICATION_PLATFORM_CALLBACK_MAX_BODY_BYTES` | `65508` | 1..65508 |
| `callbacks.replay-skew` | `APP_NOTIFICATION_PLATFORM_CALLBACK_REPLAY_SKEW` | `5m` | positive |
65508 is not a round number by accident: it is the ciphertext column's 65536 bytes minus the AES-GCM
nonce and tag. A larger configured value would pass every check above the database and fail the
`CHECK` constraint after the callback had already been acknowledged to the provider.
## Provider profiles
Profiles are a map under `providers`, keyed by profile id. There are no environment variables for
them, because the keys are deployment-chosen; supply them as YAML or as
`CA_SKELETON_NOTIFICATION_PLATFORM_PROVIDERS_<ID>_<FIELD>`.
| Field | Meaning |
|---|---|
| `type` | `APNS`, `FCM`, `SES`, `SMTP`, `TWILIO`, `WEB_PUSH`, `WEBHOOK` — a closed enum, so an unknown value fails binding rather than assembling into nothing |
| `enabled` | A disabled profile is bound and validated but contributes no runtime |
| `primary-for-channel` | Exactly one primary per channel |
| `environment` | Required when enabled |
| `credential-profile` | Resolved through `SecretMaterialProvider`; never an inline secret |
| `topic` | APNs bundle id |
| `vapid-public-key` | Web Push application server key |
| `callback-signing-secret-ref` | Reference, not material |
| `timeout` | Positive and finite |
| `max-concurrency` | Positive |
| `rate-per-second` | Positive |
A profile pins provider type, environment, credential profile, timeouts, concurrency and rate limit.
Sender identity and credential profile are separate concerns.
## Startup failures
Startup fails rather than degrading when:
- a payload or queue setting is unbounded
- a timeout is negative
- a TTL-required profile has no expiry source
- a callback signing secret is missing
- a production profile enables trust-all
- an APNs profile is missing its environment or topic
- a Web Push profile is missing its VAPID key
- two provider profiles share an id
- a route points only at disabled providers
- `mode` is `SERVING` and no provider profile is enabled
- the notification schema stream is not applied and promoted
## Secrets
All key material arrives through `SecretMaterialProvider`. Nothing is read from source, from a
committed file, or from a plaintext log. Contact point encryption and lookup HMAC keys must be
distinct, and the encryption key must be exactly 256 bits.
## Readiness
The platform contributes a `notifications` actuator endpoint and a health indicator. It reports DOWN
when a provider's credentials were rejected, when a configured provider has no channel route, and
when the measured backlog, stuck-lease count, projection lag or reconciliation lag passes the
thresholds in `NotificationServingThresholds`. See [operations.md](operations.md).