notify: codes replace the type enum, and android goes data-only
the sidecar now sends what the design describes.
producer contract: { code, userId, n?, id? }. validated on the SHAPE of the
code, never against the catalogue — a producer firing a code the mapping has
not caught up with is sending a real event, and dropping it here would trade a
degraded notification for none when the device already has a defined fallback.
the shape check still catches a typo at the producer instead of on a phone.
`ok` is gone. a failed job is its own code, officer/core:job-failed, which is
the thing the five-value enum could not express without a boolean bolted on.
apns: mutable-content: 1, alert.title fixed to the generic, and officer:{c,n,id,mv}
beside it. deliberately NOT the catalogue's title — sending the real text would
hand apple the category vocabulary in plaintext, which is what the code exists
to withhold.
fcm: data-only, no notification block. a notification block is rendered by the
system, which would put the code on the lock screen and give the app no chance
to resolve it. priority high is load-bearing — a normal-priority data message
can sit in doze indefinitely.
discord renders server-side from the catalogue, because it has no app to
resolve anything. safe: catalogue titles are templates and carry no user
content.
the catalogue is loaded ONCE per dispatch, not per channel or per device, so
every copy of one notification carries the same mv. two channels reading disk
either side of a plugin install would otherwise tell two devices different
things about the same event.
40 tests. verified live: a malformed code is refused, officer/core:test is
accepted and dispatches to zero configured channels.
This commit is contained in:
@@ -179,7 +179,7 @@ notifier the owner can uninstall is one those producers cannot rely on.
|
||||
|
||||
```
|
||||
producer (queue / email / agent / platform / plugin)
|
||||
│ POST /_officer/notify { c, userId, n?, id? }
|
||||
│ POST /_officer/notify { code, userId, n?, id? }
|
||||
▼
|
||||
officer-notify ─┬─ apns → api.push.apple.com (HTTP/2, ES256 JWT)
|
||||
├─ fcm → fcm.googleapis.com/v1 (OAuth2 bearer)
|
||||
@@ -246,10 +246,14 @@ tokens rotate.
|
||||
- **`bundle_id` / `app_slug` on a DEVICE go near-constant.** The columns exist to let several apps register
|
||||
separately; with a hub there is one bundle id per device. Keep them — a second receiver later (a watch, a
|
||||
desktop build) costs nothing this way — but "which apps has this user got" stops being a delivery question.
|
||||
- **`NotifyType` is replaced by codes.** The `'job' | 'mail' | 'agent' | 'download' | 'test'` enum was the
|
||||
rigidity this design exists to remove. `test` should survive as a reserved code for first light.
|
||||
- **`text.ts` moves to the device.** Server-side `renderTitle`/`renderBody` becomes the mapping. Keep a
|
||||
minimal server-side generic for the `alert` fallback.
|
||||
- ~~**`NotifyType` is replaced by codes.**~~ Done. The producer contract is now
|
||||
`POST /_officer/notify { code, userId, n?, id? }`, validated on the SHAPE of the code
|
||||
(`<publisher>/<appName>:<key>`) and deliberately **not** against the catalogue — a producer firing a
|
||||
code the catalogue has not caught up with is sending a real event, and the device already has a defined
|
||||
fallback. `ok` is gone: a failed job is its own code (`officer/core:job-failed`), which is what made the
|
||||
enum rigid in the first place.
|
||||
- ~~**`text.ts` moves to the device.**~~ Done — replaced by `render.ts`, which renders server-side only for
|
||||
Discord (no app to resolve a code) and for the `alert` fallback string.
|
||||
|
||||
## Failure handling
|
||||
|
||||
|
||||
Reference in New Issue
Block a user