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`.
|
||||
|
||||
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
|
||||
|
||||
| Layer | Technology |
|
||||
@ -40,7 +46,7 @@ devplacepy/
|
||||
avatar.py # Multiavatar generation, URL builder
|
||||
utils/ # Password hashing, session mgmt, time_ago, notification hook (package)
|
||||
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, ...)
|
||||
templates/ # Jinja2 HTML templates
|
||||
static/css/ # Page-specific CSS files
|
||||
@ -811,9 +817,37 @@ calling itself.
|
||||
|
||||
## 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`,
|
||||
`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
|
||||
|
||||
@ -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
|
||||
subscription or push-service error never blocks the triggering request. Delivery
|
||||
(`push.notify_user`) iterates a user's subscriptions, encrypts the payload
|
||||
(legacy `aesgcm` content encoding), and POSTs to each endpoint; subscriptions that
|
||||
return `404`/`410` are soft-deleted.
|
||||
(`push.notify_user`) reads a user's subscriptions once, groups them by provider, builds
|
||||
each provider's payload once, and sends over a single shared HTTP client. A subscription
|
||||
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
|
||||
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 |
|
||||
|------|------|
|
||||
| `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` |
|
||||
| `static/js/PushManager.js` | Service-worker registration + subscribe + opt-in UI |
|
||||
| `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.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`)
|
||||
|
||||
| Event key | Recorded in |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user