Mytty push relay
A Cloudflare Worker that forwards Attention alerts from a Mac to APNs.
It exists so the APNs provider key does not have to live on every user's Mac. An APNs key is bound to the App ID it was issued for, so only whoever signed the iOS app can hold one — pushing that requirement onto users would mean push only ever worked for people building the app themselves.
The relay is deliberately incurious. It stores a device token and a shared secret per phone, and forwards an opaque blob:
Mac ──HTTPS──> Worker ──HTTP/2──> APNs ──> iPhone
(.p8) └ notification service extension
decrypts with the pairing key
Alert text is sealed on the Mac with the pairing key that Mac and phone established when they paired, which the relay never sees. What reaches Cloudflare is a device token, a random Mac identifier, and ciphertext. The placeholder title and body in the payload are what a phone shows if it cannot decrypt (an older build, or a Mac it has since unpaired).
Endpoints
| Method | Path | Caller | Purpose |
|---|---|---|---|
| POST | /v1/register |
iPhone | Exchanges an APNs device token for a pushID and relaySecret. |
| POST | /v1/push |
Mac | Delivers one sealed alert, signed with the relaySecret. |
/v1/register is unauthenticated because there is nothing to authenticate:
the caller already holds the device token, and registering only creates a
mailbox for a phone they can already push to. /v1/push requires
x-mytty-push-id, x-mytty-timestamp, and x-mytty-signature — a
hex HMAC-SHA256 of <timestamp>.<body> under the registration's secret.
Timestamps more than five minutes from the relay's clock are refused, and
an unknown push ID answers exactly like a bad signature so live IDs cannot
be enumerated.
Registrations expire after 180 days of no deliveries, and are dropped
immediately when APNs reports the token is dead (Unregistered,
BadDeviceToken, DeviceTokenNotForTopic).
Deploying
One-time, from this directory:
npx wrangler kv namespace create REGISTRATIONS
# put the printed id into wrangler.toml
npx wrangler secret put APNS_TEAM_ID # 10 characters
npx wrangler secret put APNS_KEY_ID # 10 characters, from AuthKey_XXXXXXXXXX.p8
npx wrangler secret put APNS_PRIVATE_KEY # paste the whole .p8, BEGIN/END lines included
npx wrangler deploy
APNS_TOPIC in wrangler.toml must match the iOS app's bundle identifier,
and the key must belong to the team that signed it.
Then point the apps at the deployed Worker by setting PushRelay.defaultURL
in Sources/MyTTYRemoteKit/PushRelay.swift to its URL. Both the Mac app and
the iOS app read that one constant, so they cannot drift apart.
Tests
npm test # node --test, no dependencies
Covers the parts worth getting wrong: ES256 provider tokens (verified
against a real public key), request signing and its replay window, and
which APNs rejections are permanent. Routing itself is left to wrangler dev.