Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

webpush-relay

A small, stateless Go service that emulates an RFC 8030 WebPush endpoint and forwards the still-encrypted payload to APNs (iOS) and FCM (Android). It exists so that concrnt's WebPush-only notification delivery (concrnt/internal/worker/NotificationReactor.go) can reach native mobile apps that can't speak WebPush directly.

The relay never decrypts anything. Decryption (RFC 8291 aes128gcm) happens on-device: an iOS Notification Service Extension and an Android FirebaseMessagingService hold the subscription's private key and auth secret and decrypt locally.

Endpoints

  • POST /apns/:environment/:device_tokenenvironment is production, sandbox, or development (alias for sandbox).
  • POST /fcm/:registration_token
  • GET /health

A client builds its PushSubscription.endpoint pointing at one of the first two routes; whatever WebPush sender POSTs to it gets relayed unchanged.

Configuration

See config.example.yaml. Config is loaded from the path in WEBPUSH_RELAY_CONFIG (default /etc/webpush-relay/config), which may be a single file or a directory of files that get merged together.

APNs uses token-based (JWT) auth via a .p8 key — not the legacy p12 certificate — so a single key authenticates against both the production and sandbox APNs hosts; the environment is selected per-request from the URL, not from config.

Known limitations

  • 4KB payload limit. Both APNs and FCM cap a message's payload/data size at 4096 bytes. base64url-encoding the encrypted WebPush body adds ~33% overhead, and the JSON envelope adds a little more, so the effective encrypted-body ceiling is roughly 2.9KB. concrnt's push payload is a full concrnt.Event (which can embed referenced documents), so larger events can exceed this. When a built payload would be too large, the relay falls back to sending a notification carrying no encrypted body (p omitted); the device shows a generic notification instead of failing silently. A permanent fix (trimming what NotificationReactor sends) is out of scope for this repository.
  • No subscription cleanup. The relay is stateless and has no way to tell concrnt to drop a dead subscription. It does propagate 410 Gone when APNs or FCM report a token as unregistered/invalid, which the WebPush sender could act on — but concrnt's NotificationReactor currently only logs non-201 responses. Actual cleanup needs a concrnt-side change.
  • No VAPID validation. Like Mastodon's webpush-apn-relay (the reference implementation this project follows the style of, but does not vendor), the relay ignores the WebPush Authorization/VAPID headers. The device token or registration token embedded in the URL is treated as an unguessable capability.

Development

go build ./...
go vet ./...
go test ./...

Local smoke test without real APNs/FCM credentials — with a provider left unconfigured, its route replies 503, so you can exercise request parsing end to end:

go run ./cmd/webpush-relay &
curl -i -X POST \
  -H 'Content-Encoding: aes128gcm' -H 'TTL: 30' -H 'Urgency: high' \
  --data-binary @sample-encrypted-body.bin \
  http://localhost:8000/fcm/some-token

Set fcm.dryRun: true to validate real FCM registration tokens against Google without actually delivering a notification.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages