Document the push providers in the README and the audit catalogue
README covers the provider model, the two providers and their transports, the admin configuration surface at /admin/services/push, the delivery loop and the updated file map. events.md records that push.subscribe and push.update now carry the provider in their metadata, with endpoint_host set only for endpoint-based providers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7674dac628
commit
a78b656ef9
53
README.md
53
README.md
@ -15,6 +15,12 @@ make test-headed # same tests in visible browser
|
|||||||
|
|
||||||
Open `http://localhost:10500`.
|
Open `http://localhost:10500`.
|
||||||
|
|
||||||
|
PDF export (DeepSearch reports, via weasyprint) needs the Pango text stack installed at system level. The production image installs it; on a development host install it once:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get install -y libglib2.0-0 libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libfontconfig1 fonts-dejavu-core
|
||||||
|
```
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
| Layer | Technology |
|
| Layer | Technology |
|
||||||
@ -40,7 +46,7 @@ devplacepy/
|
|||||||
avatar.py # Multiavatar generation, URL builder
|
avatar.py # Multiavatar generation, URL builder
|
||||||
utils/ # Password hashing, session mgmt, time_ago, notification hook (package)
|
utils/ # Password hashing, session mgmt, time_ago, notification hook (package)
|
||||||
models.py # Pydantic schemas
|
models.py # Pydantic schemas
|
||||||
push.py # Web push crypto, VAPID keys, encrypt/send/register
|
push/ # Push delivery: provider protocol, Web Push, APNs, registrations
|
||||||
routers/ # One file per domain (auth, feed, posts, push, ...)
|
routers/ # One file per domain (auth, feed, posts, push, ...)
|
||||||
templates/ # Jinja2 HTML templates
|
templates/ # Jinja2 HTML templates
|
||||||
static/css/ # Page-specific CSS files
|
static/css/ # Page-specific CSS files
|
||||||
@ -811,9 +817,37 @@ calling itself.
|
|||||||
|
|
||||||
## Push notifications & PWA
|
## Push notifications & PWA
|
||||||
|
|
||||||
Authenticated users can receive native web push notifications, and the site is an
|
Authenticated users can receive native push notifications, and the site is an
|
||||||
installable Progressive Web App. Push uses only standard libraries (`cryptography`,
|
installable Progressive Web App. Push uses only standard libraries (`cryptography`,
|
||||||
`PyJWT`, `httpx`) against the Web Push Protocol - no third-party push wrapper.
|
`PyJWT`, `httpx`) against the Web Push Protocol and the Apple Push Notification service -
|
||||||
|
no third-party push wrapper.
|
||||||
|
|
||||||
|
### Providers
|
||||||
|
|
||||||
|
Delivery is split into providers behind one protocol (`devplacepy/push/providers/`). A user
|
||||||
|
receives a notification through every provider they hold a live subscription for.
|
||||||
|
|
||||||
|
| Provider | Registration | Transport |
|
||||||
|
|----------|--------------|-----------|
|
||||||
|
| `webpush` | `PushSubscription` from the browser `PushManager` (endpoint + `p256dh`/`auth` keys) | Web Push Protocol, VAPID signed, `aesgcm` encrypted payload |
|
||||||
|
| `apns` | Hexadecimal device token | `POST https://api.push.apple.com/3/device/{token}` over HTTP/2, ES256 provider token |
|
||||||
|
|
||||||
|
`POST /push.json` accepts a registration for any active provider; a body without a
|
||||||
|
`provider` field is a `webpush` body, so browsers need no change. `GET /push.json` returns
|
||||||
|
the VAPID public key plus the providers currently accepting registrations. A provider that
|
||||||
|
is disabled or not fully configured accepts no registrations and is skipped during
|
||||||
|
delivery, so an unconfigured provider is inert rather than an error.
|
||||||
|
|
||||||
|
Every provider setting is edited at **`/admin/services/push`**: per provider an `Enabled`
|
||||||
|
toggle, the VAPID subject for `webpush`, and team id, key id, `.p8` auth key (stored as a
|
||||||
|
masked secret), topic and environment (production or sandbox) for `apns`. The same page
|
||||||
|
holds the shared delivery timeout and the retention window after which dead subscriptions
|
||||||
|
are removed. Push delivery does not depend on that service running; stopping it only stops
|
||||||
|
the pruning sweep.
|
||||||
|
|
||||||
|
Adding a third provider is one file plus one registry entry: the registration route, the
|
||||||
|
delivery loop, the admin page, the audit record and the metrics are all written against the
|
||||||
|
provider protocol.
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
@ -836,9 +870,11 @@ in real time, bridged onto the in-process pub/sub bus by a lock-owner relay:
|
|||||||
|
|
||||||
`create_notification` schedules delivery as a fire-and-forget async task, so a dead
|
`create_notification` schedules delivery as a fire-and-forget async task, so a dead
|
||||||
subscription or push-service error never blocks the triggering request. Delivery
|
subscription or push-service error never blocks the triggering request. Delivery
|
||||||
(`push.notify_user`) iterates a user's subscriptions, encrypts the payload
|
(`push.notify_user`) reads a user's subscriptions once, groups them by provider, builds
|
||||||
(legacy `aesgcm` content encoding), and POSTs to each endpoint; subscriptions that
|
each provider's payload once, and sends over a single shared HTTP client. A subscription
|
||||||
return `404`/`410` are soft-deleted.
|
the push service reports as gone (`404`/`410` for Web Push, `410` or an `Unregistered`
|
||||||
|
class reason for APNs) is soft-deleted; any other failure is logged and the subscription is
|
||||||
|
kept.
|
||||||
|
|
||||||
A notification is also **marked read automatically when you open the page that shows its
|
A notification is also **marked read automatically when you open the page that shows its
|
||||||
content** - viewing a post clears its comment, reply, upvote and mention notifications;
|
content** - viewing a post clears its comment, reply, upvote and mention notifications;
|
||||||
@ -895,7 +931,10 @@ offline. Installation requires a secure origin (HTTPS, or `localhost` for develo
|
|||||||
|
|
||||||
| File | Role |
|
| File | Role |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `devplacepy/push.py` | VAPID keys, payload encryption, send, register |
|
| `devplacepy/push/providers/` | Provider protocol, Web Push (VAPID keys, payload encryption), APNs |
|
||||||
|
| `devplacepy/push/store.py` | `push_registration` reads and writes |
|
||||||
|
| `devplacepy/push/delivery.py` | `notify_user` - group by provider, deliver, reap dead subscriptions |
|
||||||
|
| `devplacepy/services/push/service.py` | Provider configuration at `/admin/services/push`, retention sweep, metrics |
|
||||||
| `devplacepy/routers/push.py` | `/push.json`, `/service-worker.js`, `/manifest.json` |
|
| `devplacepy/routers/push.py` | `/push.json`, `/service-worker.js`, `/manifest.json` |
|
||||||
| `static/js/PushManager.js` | Service-worker registration + subscribe + opt-in UI |
|
| `static/js/PushManager.js` | Service-worker registration + subscribe + opt-in UI |
|
||||||
| `static/service-worker.js` | Receives push, shows notification, offline fallback |
|
| `static/service-worker.js` | Receives push, shows notification, offline fallback |
|
||||||
|
|||||||
@ -408,6 +408,8 @@ Every state-changing action in DevPlace records one append-only row through `dev
|
|||||||
| `push.subscribe` | `routers/push.py` |
|
| `push.subscribe` | `routers/push.py` |
|
||||||
| `push.update` | `routers/push.py` |
|
| `push.update` | `routers/push.py` |
|
||||||
|
|
||||||
|
Both carry `metadata.provider` (`webpush`, `apns`, ...) plus `created`; `metadata.endpoint_host` is set for endpoint-based providers and `null` for token-based ones.
|
||||||
|
|
||||||
## Gamification (`reward`)
|
## Gamification (`reward`)
|
||||||
|
|
||||||
| Event key | Recorded in |
|
| Event key | Recorded in |
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user