Summary
A self-hosted relay can advertise NIP-PL correctly, run the delivery worker, and still never deliver a single push — because the only gateway it can point at is yours, and the only gateway that could hold the APNs key for the App Store client is also yours. I do not think this is a bug so much as a gap in the self-hosting story, and I would like to know which of the two exits you intend.
What we run
wss://buzz.scalarly.org, our own relay, with the public App Store Buzz iOS app as the client. That combination is the whole issue: we operate the relay, you own the app.
The relay half looks healthy. NIP-11 today:
"push": {
"app_profiles": [
{ "id": "buzz-ios-production", "transport": "apns" },
{ "id": "buzz-ios-sandbox", "transport": "apns" }
],
"origin": "wss://buzz.scalarly.org",
"push_kinds": [7, 9, 1059, 40007, 46010],
"keys": [{ "id": "relay-v1", "current": true, "pubkey": "863d7a6b…" }]
}
supported_extensions includes nip-pl, and the relay logs NIP-PL push matcher and delivery worker started.
What actually happens
Zero leases, ever. select count(*) from events where kind = 30350 → 0. Not one device has published a lease against us.
The delivery URL defaults to yours. With no push env set, DEFAULT_PUSH_GATEWAY_DELIVERY_URL (crates/buzz-relay/src/config.rs:448) is https://push.buzz.xyz/v1/deliveries/apns — block's hosted gateway, which does not know our tenant or our executor key. So the one configuration that "just works" points at a service that will reject us, and it does so silently from the operator's side.
Running the gateway ourselves does not help. crates/buzz-push-gateway/src/config.rs:179-182 requires BUZZ_PUSH_APNS_KEY_PATH, _KEY_ID, _TEAM_ID and _TOPIC. apns_topic is the iOS bundle identifier, and APNs only accepts a push signed by the Apple Developer team that owns that bundle id. The App Store app's bundle id is yours. A gateway we stand up authenticates to APNs as nobody, for an app it does not own. There is no relay-side configuration that fixes this.
The ask
Either of these would close it; I am asking which you prefer rather than proposing one:
- Onboard self-hosted relays as tenants of
push.buzz.xyz. The relay is already pointed there by default, already advertises nip-pl, already runs the worker. If there is a registration path — an executor key you can enroll, a tenant record, a signed origin — this likely starts working with no code change on either side. This is the outcome we would like.
- Say plainly in the self-hosting docs that iOS push requires shipping your own client build, so operators stop treating a healthy-looking NIP-PL advertisement as a working feature. Today the relay honestly advertises a capability that cannot function, and nothing in the logs says why.
If neither is on the table, a third useful thing: have the relay refuse to advertise nip-pl (or log a warning at startup) when BUZZ_PUSH_GATEWAY_DELIVERY_URL is still the default and the operator is not block. That turns a silent dead end into a startup message.
Happy to test any tenant-onboarding flow against our relay.
Summary
A self-hosted relay can advertise NIP-PL correctly, run the delivery worker, and still never deliver a single push — because the only gateway it can point at is yours, and the only gateway that could hold the APNs key for the App Store client is also yours. I do not think this is a bug so much as a gap in the self-hosting story, and I would like to know which of the two exits you intend.
What we run
wss://buzz.scalarly.org, our own relay, with the public App Store Buzz iOS app as the client. That combination is the whole issue: we operate the relay, you own the app.The relay half looks healthy. NIP-11 today:
supported_extensionsincludesnip-pl, and the relay logsNIP-PL push matcher and delivery worker started.What actually happens
Zero leases, ever.
select count(*) from events where kind = 30350→0. Not one device has published a lease against us.The delivery URL defaults to yours. With no push env set,
DEFAULT_PUSH_GATEWAY_DELIVERY_URL(crates/buzz-relay/src/config.rs:448) ishttps://push.buzz.xyz/v1/deliveries/apns— block's hosted gateway, which does not know our tenant or our executor key. So the one configuration that "just works" points at a service that will reject us, and it does so silently from the operator's side.Running the gateway ourselves does not help.
crates/buzz-push-gateway/src/config.rs:179-182requiresBUZZ_PUSH_APNS_KEY_PATH,_KEY_ID,_TEAM_IDand_TOPIC.apns_topicis the iOS bundle identifier, and APNs only accepts a push signed by the Apple Developer team that owns that bundle id. The App Store app's bundle id is yours. A gateway we stand up authenticates to APNs as nobody, for an app it does not own. There is no relay-side configuration that fixes this.The ask
Either of these would close it; I am asking which you prefer rather than proposing one:
push.buzz.xyz. The relay is already pointed there by default, already advertisesnip-pl, already runs the worker. If there is a registration path — an executor key you can enroll, a tenant record, a signed origin — this likely starts working with no code change on either side. This is the outcome we would like.If neither is on the table, a third useful thing: have the relay refuse to advertise
nip-pl(or log a warning at startup) whenBUZZ_PUSH_GATEWAY_DELIVERY_URLis still the default and the operator is not block. That turns a silent dead end into a startup message.Happy to test any tenant-onboarding flow against our relay.