~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

grain-serve keeps the devices notifications are pushed to

Merged
Merged chadtmiller.com asked to merge into feature/rust from grain.social/grain:push/register

What changes

Devices register for pushes. grain-serve answers social.grain.notification.registerPush and unregisterPush, taken as Bluesky's app.bsky.notification.registerPush takes them: serviceDid, token, platform (ios, android, web) and appId, from an account with a service token for grain-serve's DID (or the web's cookie, from a page of the site, through viewer::writer). A device is kept in push_token by (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_tokens and drop_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_tokens into push_token as before, but names a device's platform as registerPush does: the old server's apns is ios, its fcm is android; 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_token was 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:

  • The methods are social.grain.notification.registerPush / unregisterPush with Bluesky's input, not the old registerToken({token, platform}) / unregisterToken({token}). The apps still call the old names (Grain/API/PushManager.swift, data/api/Endpoints.kt), and send apns / fcm: they must move to these before cutover, sending serviceDid (grain-serve's did:web), platform ios / android, and appId.
  • serviceDid must be grain-serve's own DID; another is refused. platform outside ios / android / web is refused rather than kept (the old one defaulted a missing platform to apns). A token is 1 to 4096 bytes.
  • appId is 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.rs against 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 and push_token_token). The import's test now starts from apns / fcm rows and an unknown platform. Everything else is left to the runner's harness and rust checks.

Shortcomings

  • Nothing is sent yet: that is the next two pulls (the APNs and FCM transports, then the worker that sends what notification::arising finds).
  • A token registered by two accounts (one device, an account switch whose unregister failed) goes on receiving both, as with the old server.

🤖 Generated with Claude Code

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. The token-only delete and lookup are covered by the push_token_token index. Both lexicons already exist and require serviceDid, token, platform and appId, 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.rs is the drop query, which index/push.rs tests now.
  • Unregister: it requires a valid platform and appId, 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 harness and rust checks, 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
  1. grain-serve keeps the devices notifications are pushed to Landed as 8707ce6
  2. main