R9 Web Push, then the honest version: VAPID key, push subscriptions, encrypted pushes, 4.5.0 advertised
The server's VAPID key (PushKeys, made on first need, in Mongo) is the instance API's vapid.public_key and every app's vapid_key. /api/v1/push/subscription (POST, GET, PUT, DELETE) keeps one subscription per access token, so per persona; every notification kept (never a filtered one) is queued as a SendPush job for the subscriptions whose alert and policy admit it, and sent through the `push` client (the safe handler, https only) encrypted as RFC 8291 aes128gcm or the aesgcm draft with an ES256 VAPID JWT. 404/410 delete the subscription, 429/5xx/timeouts retry, other answers drop the push. Subscriptions end with their tokens (revocation, sessions ended, suspension, deletion, a restore); backups hold neither keys nor subscriptions. The walk of every Mastodon 4.3-4.5 client route (docs/MASTODON-API.md) added conversations/:id/unread, domain_blocks/preview, v2 notifications clear and PUT policy, /oauth/userinfo and stubs, so both instance APIs now say 4.5.0 (compatible; PrivaPub <release>). The pasture gets a push sink (pushsink.test) and scenarios/push.sh in batch A. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
This commit is contained in:
1 parent
87529bca49
commit
9b792042e6
43 files changed
+2079
-49
No files matched your search
@@ -0,0 +1,70 @@
|
||||
# The Mastodon client API: what PrivaPub answers
|
||||
|
||||
The walk behind the version PrivaPub advertises (completion program R9, 2026-10-18). Every client route of Mastodon 4.3.0,
|
||||
4.4.0 and 4.5.0, as their `config/routes/api.rb` and `config/routes.rb` define them, set against PrivaPub's controllers
|
||||
(`PrivaPub/Api/Mastodon/Controllers`). Since nothing a client relies on is missing, both instance APIs say
|
||||
`4.5.0 (compatible; PrivaPub <release>)` (`InstanceController.Version`; Mastodon invariant 6 in CLAUDE.md), and
|
||||
`api_versions.mastodon` is 7, 4.5's own.
|
||||
|
||||
- **Served:** the route does what Mastodon's does, within PrivaPub's rules (a token is one persona, per-persona state,
|
||||
located posts outside the Mastodon API).
|
||||
- **Stub:** the route answers as Mastodon answers when the feature is off or empty here (an empty list, `{}`, 403, 404 or
|
||||
422 with Mastodon's message), so clients degrade instead of breaking (Mastodon invariant 5). Each is a decision, named.
|
||||
- **Missing:** no route. None of these is the client API.
|
||||
|
||||
When a route is added to Mastodon, add it here, and keep the advertised version honest.
|
||||
|
||||
## Served
|
||||
|
||||
| Area | Routes |
|
||||
|---|---|
|
||||
| Statuses | `GET /api/v1/statuses` (`id[]`), `POST`, `GET`/`PUT`/`DELETE /api/v1/statuses/:id`, `context`, `history`, `source`, `reblogged_by`, `favourited_by`, `reblog`/`unreblog`, `favourite`/`unfavourite`, `bookmark`/`unbookmark`, `mute`/`unmute`, `pin`/`unpin`; 4.5's `GET …/quotes`, `POST …/quotes/:id/revoke` and `PUT …/interaction_policy` |
|
||||
| Timelines | `home`, `public`, `tag/:hashtag`, `list/:id` |
|
||||
| Streaming | `/api/v1/streaming` (WebSocket) and `/api/v1/streaming/*` (server-sent events), `health`: `user`, `user:notification`, `public`, `public:local`, `public:remote`, `hashtag`, `hashtag:local`, `list`, `direct` |
|
||||
| Accounts | `verify_credentials` (4.4: the `profile` scope too), `update_credentials`, `search`, `lookup`, `relationships`, `familiar_followers` (empty for every account by design: R8), `GET /api/v1/accounts` (`id[]`), `:id`, `:id/statuses`, `followers`, `following`, `lists`, `featured_tags`, `endorsements` (4.4; to the persona itself), `follow`, `unfollow`, `remove_from_followers`, `block`, `unblock`, `mute`, `unmute`, `pin`/`unpin` and 4.4's `endorse`/`unendorse`, `note` |
|
||||
| Relationships | `blocks`, `mutes`, `domain_blocks` (`GET`, `POST`, `DELETE`), `domain_blocks/preview` (R9: how many follows and followers the block would take), `follow_requests` with `authorize` and `reject`, `endorsements` |
|
||||
| Notifications | v1 `notifications` (index, `:id`, `clear`, `unread_count`, `:id/dismiss`), `notifications/requests` (index, `:id`, `accept`, `dismiss`, the bulk `accept` and `dismiss`, `merged`), `notifications/policy` (`GET`, `PUT`, `PATCH`); v2 grouped `notifications` (index, `:group_key`, `:group_key/accounts`, `:group_key/dismiss`, `unread_count`, `clear` since R9), `notifications/policy` (`GET`, `PATCH`, `PUT` since R9) |
|
||||
| Web Push | `POST`, `GET`, `PUT`, `DELETE /api/v1/push/subscription` (R9; CLAUDE.md, Web Push) |
|
||||
| Conversations | `conversations` (index), `DELETE :id`, `:id/read`, `:id/unread` (R9) |
|
||||
| Media and polls | `POST /api/v1/media`, `/api/v2/media`, `GET`/`PUT`/`DELETE /api/v1/media/:id`; `polls/:id`, `polls/:id/votes` |
|
||||
| Lists, filters, tags | `lists` (CRUD, `:id/accounts` `GET`/`POST`/`DELETE`); v1 `filters` and v2 `filters` with `keywords` and `statuses` (both nested and by id); `tags/:name` with `follow`, `unfollow` and 4.4's `feature`, `unfeature`; `followed_tags`; `featured_tags` (index, create, destroy) and `featured_tags/suggestions` |
|
||||
| Discovery | `/api/v2/search`, `trends` and `trends/tags`, `trends/statuses`, `trends/links`, `directory`, `suggestions` (v1 index and destroy, v2 index) |
|
||||
| The persona's own | `bookmarks`, `favourites`, `markers` (`GET`, `POST`), `preferences`, `scheduled_statuses` (index, show, update, destroy), `profile/avatar` and `profile/header` (`DELETE`), `reports` |
|
||||
| The server | v1 and v2 `instance`, `instance/peers` (empty by design: which servers this one talks to is not published), `rules`, `domain_blocks` (the published ones), `extended_description`, `privacy_policy`, 4.4's `terms_of_service` and `terms_of_service/:date`, `languages`, `translation_languages` (none: translation is cut), `activity` (empty unless the administrator publishes it), `custom_emojis`, `announcements` with `dismiss` and `reactions` |
|
||||
| Apps and OAuth | `POST /api/v1/apps`, `apps/verify_credentials`, `/oauth/authorize`, `/oauth/token`, `/oauth/revoke`, `/.well-known/oauth-authorization-server`, 4.4's `/oauth/userinfo` (`GET` and `POST`, the `profile` scope; R9) |
|
||||
|
||||
## Stubs
|
||||
|
||||
| Route | Answer | Why |
|
||||
|---|---|---|
|
||||
| `POST /api/v1/statuses/:id/translate` | 422 `Translation is not available on this server` | translation is cut (owner decision 2026-10-17) |
|
||||
| `GET /api/v1/timelines/link` | `[]` | 4.3's timeline of a trending link: not built (trending links themselves are served) |
|
||||
| `GET /api/v1/annual_reports`, 4.4's `GET /api/v1/annual_reports/:year` | `{"annual_reports": [], "accounts": [], "statuses": []}` | no yearly reports are made |
|
||||
| 4.4's `POST /api/v1/annual_reports/:year/read` | `{}` | there is none to read |
|
||||
| `POST /api/v1/accounts` | 403 `This action is not allowed` (Mastodon's answer with registrations closed) | registration through the API is cut: logins are made by setup or invitation on `/clientapi` |
|
||||
| `POST /api/v1/emails/confirmations` | 403 `This method is only available while the e-mail is awaiting confirmation` | no account here waits for a confirmation |
|
||||
| `GET /api/v1/emails/check_confirmation` | `true` | the same |
|
||||
| `GET /api/v1/peers/search` | `[]` | peers are not published, as `instance/peers` |
|
||||
| 4.4's `GET /api/v1_alpha/async_refreshes/:id` | 404 `Record not found` | PrivaPub never sends `Mastodon-Async-Refresh`: a thread's replies are read in the background and show up |
|
||||
| `GET /api/v1/accounts/:id/identity_proofs` | `[]` | gone from Mastodon long ago; still asked by old clients |
|
||||
| every verb under `/api/v1/admin/**`, `GET`/`POST` under `/api/v2/admin/**` | 403 `This action is not allowed`, signed in or not | the Mastodon admin API is cut: moderation is on `/clientapi` |
|
||||
|
||||
## Missing, and not the client API
|
||||
|
||||
| Route | Why it is not served |
|
||||
|---|---|
|
||||
| `GET /api/oembed` | embedding a post in another website; PrivaPub's public pages have no embed view |
|
||||
| `/api/web/*` (`settings`, `embeds`, `push_subscriptions`) | Mastodon's own web interface, signed in by its session cookie; PrivaPub's client is decePub on `/clientapi` |
|
||||
| `/oauth/authorized_applications` | Doorkeeper's HTML page of a user's applications, in Mastodon's web interface |
|
||||
| `/invite/:code` (JSON) | Mastodon's app shows an invitation before registering through the API, which is cut; PrivaPub's invitations are `/clientapi`'s |
|
||||
| `/.well-known/host-meta` | OStatus-era discovery; every client and server here uses WebFinger directly |
|
||||
| `POST /api/v1/polls` | in 4.3's route list with no action behind it; gone in 4.4 |
|
||||
|
||||
Beyond routes: the `public:media` streams (and their `:local` and `:remote` forms) are not offered; the media-only public
|
||||
timelines (`only_media`) are, so a client polls them.
|
||||
|
||||
## PrivaPub's own, on the same API
|
||||
|
||||
`/api/v1/pleroma/statuses/:id/reactions` (Pleroma's emoji reactions), `/api/v1/statuses/:id/downvote` and `undownvote`,
|
||||
`/api/v1/feeds` and `/api/v1/timelines/feed/:id` (PieFed's and Lemmy's feeds), and `/api/privapub/v1/*` (provenance,
|
||||
servers, CDNs, events' join and leave): Mastodon invariant 7.
|
||||
Reference in new issue
Block a user