0
2
grain-serve keeps the devices notifications are pushed to
Merged Approved
This adds registerPush and unregisterPush to grain-serve, backed by index::push over the existing push_token table. The import now maps apns to ios and fcm to android. I found nothing that has to change.
- Schema and lexicons: the upsert on
(did, token)fits the table's primary key. Thetoken-only delete and lookup are covered by thepush_token_tokenindex. Both lexicons already exist and requireserviceDid,token,platformandappId, which matches the handler's input struct. - Tests: they cover the refusals, the repeat registration, and another account's device being left alone. The test that queries read by index now includes the push queries. The removed entry in
index/tests.rsis the drop query, whichindex/push.rstests now. - Unregister: it requires a valid
platformandappId, because the lexicon does, so a malformed unregister gets a 400 rather than being silently accepted. - Not checked: I did not read the runner's
harnessandrustchecks, so I can't say whether they pass.
Approved
Carried forward: the change this pull makes at 014f65995067e1e8ac3f0fc114347686ed1a0529 is the one reviewed at d658e39d952fdf0db0d236d85cbaa1efbf63ea83, so that review stands. What is new comes from its base.
Merged as 8707ce6
Part of a stack of 2
- grain-serve keeps the devices notifications are pushed to
- main
What changes
Devices register for pushes. grain-serve answers
social.grain.notification.registerPushandunregisterPush, taken as Bluesky'sapp.bsky.notification.registerPushtakes them:serviceDid,token,platform(ios,android,web) andappId, from an account with a service token for grain-serve's DID (or the web's cookie, from a page of the site, throughviewer::writer). A device is kept inpush_tokenby (account, token); registering it again keeps it once, on the platform last named. Unregistering one the caller never registered is no failure, and leaves another account's alone.The index's push devices (
index::push):register_push,unregister_push,push_tokensanddrop_push_token, the last for the sender (the next pull) to forget a token the platform's service says no device answers to, for every account it was kept for.The import carries
_push_tokensintopush_tokenas before, but names a device's platform as registerPush does: the old server'sapnsisios, itsfcmisandroid;web, and anything else, stay as they were.Why
The first of the push steps on feature/rust: the sender needs somewhere to send.
push_tokenwas made in V1 and the import already copied into it; this gives it its writers and readers, so no migration.Differences from the old server:
social.grain.notification.registerPush/unregisterPushwith Bluesky's input, not the oldregisterToken({token, platform})/unregisterToken({token}). The apps still call the old names (Grain/API/PushManager.swift,data/api/Endpoints.kt), and sendapns/fcm: they must move to these before cutover, sendingserviceDid(grain-serve'sdid:web),platformios/android, andappId.serviceDidmust be grain-serve's own DID; another is refused.platformoutsideios/android/webis refused rather than kept (the old one defaulted a missing platform toapns). A token is 1 to 4096 bytes.appIdis required, as the lexicon says, but not kept: APNs's topic comes from the server's settings (next pull), as the old server's came from its config.How it was checked
tests/push.rsagainst the reference PDS with real service tokens: register, re-register, unregister, another account's device left alone, and every refusal (no token, missing fields, another serviceDid, an unknown platform, an empty or too long token, a server with no index). Unit tests of the index methods and that their queries read by index (push_token's primary key andpush_token_token). The import's test now starts fromapns/fcmrows and an unknown platform. Everything else is left to the runner'sharnessandrustchecks.Shortcomings
notification::arisingfinds).🤖 Generated with Claude Code