clone

nostr relays on demand at <name>.bind.ws: claim one with your key, pay in zaps past the free allowance

dependabot/npm_and_yarn/npm-f01a459b0f
bindws / docs / 25-nip-9a-relay-push.md
25-nip-9a-relay-push.md 7.2K rendered
1---2title: NIP-9a relay push3audience: developer4---56# NIP-9a relay push78bind.ws implements opt-in relay-to-callback delivery for community and inbox9notifications, Git activity and automation hooks. A callback can bridge to a10mobile notification service. bind.ws does not provide device registration,11FCM, APNs or Web Push infrastructure.1213The implementation pins [proposal 2194](https://github.com/nostr-protocol/nips/pull/2194)14to [5a908b8dc5190a46eac47992cba5ba6d8e7fc094](https://github.com/coracle-social/nips/blob/5a908b8dc5190a46eac47992cba5ba6d8e7fc094/9a.md),15checked 2026-09-04. This is an unmerged draft; clients should periodically16resync their registrations and follow proposal changes.1718## Enable delivery1920The host operator sets `PUSH_CALLBACK_ORIGINS` to a JSON array of trusted,21public HTTPS origins in the selected Wrangler or celld environment. The22shipped value is `"[]"`, so no endpoint receives requests by default:2324```json25"PUSH_CALLBACK_ORIGINS": "[\"https://push.example.com\"]"26```2728The relay owner enables push and selects origins using the console's29Features settings, `setpolicy`, or a relay configuration:3031```json32{33 "format": "bind.ws/relay-config/2",34 "policy": {35 "features": { "push": true },36 "pushCallbacks": ["https://push.example.com"]37 }38}39```4041Both origin lists must authorize a callback. Owners cannot expand the host's42list. Entries are exact HTTPS origins without paths, credentials or wildcard43hosts; callback registrations supply the path and query. Only public DNS44names on port 443 are accepted. IP literals, local names, fragments and45redirects are refused. The host operator must trust the endpoint's operator46and DNS control and keep it publicly routed: the origin allowlist is the47SSRF boundary, not a DNS preflight that could race a later resolution. A host48must not approve arbitrary tenant-controlled origins or callback services49that proxy arbitrary destinations. No credentials or cookies are attached.5051The existing templates keep push off. Templates with kind allowlists must52also allow kind `30390` before members can register. Outgoing traffic and53background work use the relay's fuel budget; queue storage is part of its54SQLite storage.5556## Register and receive5758The owner or a current member authenticates with NIP-42 and publishes a59signed addressable event. The HTTP bridge can instead authenticate the same60author using NIP-98. Ordinary relay write and kind restrictions still apply.6162```json63{64 "kind": 30390,65 "content": "",66 "tags": [67 ["d", "community-inbox"],68 ["relay", "wss://community.bind.ws/"],69 ["filter", "{\"kinds\":[1,1111],\"#p\":[\"<recipient pubkey>\"]}"],70 ["ignore", "{\"#t\":[\"muted-topic\"]}"],71 ["callback", "https://push.example.com/opaque-secret-token"],72 ["include_event"]73 ]74}75```7677For Git activity, filters can select accepted repository state or78collaboration events, for example `{"kinds":[30618,1617,1621]}`. Events held79pending Git admission do not trigger delivery until accepted. Filters match80new events only; registration does not backfill history. Multiple `filter`81tags are ORed, and any matching `ignore` suppresses delivery.8283The callback receives `POST`, `Content-Type: application/json`:8485```json86{87 "id": "<event id>",88 "relay": "wss://community.bind.ws/",89 "event": { "id": "<event id>", "pubkey": "...", "kind": 1 }90}91```9293The actual `event` is the complete signed Nostr event and is present only94when the registration contains `include_event`. A receiver should verify95included signatures, treat URL tokens as secrets, and deduplicate by relay,96registration destination and event ID. The payload itself has no separate97relay signature. An ID-only notification still reveals an event's existence,98so it uses the same read authorization as full-event delivery.99100## Privacy and revocation101102Kind 30390 is author-only regardless of feature state. Authenticated authors103can read their own registrations through REQ, COUNT, NIP-77 and the HTTP104bridge, subject to the relay's current read rule. A `p` tag grants no access.105The relay owner has no special read override for another author's callbacks.106Registrations are excluded from shared dashboard views, dumps, forks and107rebroadcast jobs. Config exports contain origin policy, never registration108callback paths. Clients must export or resync their own registrations.109110Every callback attempt rechecks the current registration, membership, bans,111write/read rules, callback approvals and target event visibility. Removing a112member deletes their registrations. Changing callback approval or read/write113policy cancels queued work. Hiding/deleting an event, replacing or114deleting a registration, or disabling push prevents later delivery. An115already dispatched HTTP request cannot be recalled. A `404` removes the116registration. NIP-09 deletions and NIP-62 vanish requests remove it through the117normal event store. The queue holds event and registration IDs, not copies118that could outlive deletion.119120## Bounds and retries121122Delivery runs from the existing Durable Object alarm, after publication has123returned. Queue persistence precedes external I/O. Delivery is best effort:124finite queues and retries can lose notifications, and ambiguous responses125can produce duplicates. Receivers must not assume exactly-once delivery.126127| Limit | Value |128| --- | --- |129| Registrations | 32 per relay, 4 per author, 8 KiB UTF-8 each |130| Filters | 8 matching and 8 ignore filters; standard event fields/tag filters |131| Pending deliveries | 256 references, 24-hour expiry |132| Alarm batch | 4 attempts, 5 seconds per HTTP request |133| Retries | 4 total attempts; 30, 120 and 600 seconds between failures |134| Deduplication | 2,048 terminal outcomes, at most 7 days |135| Event input | Stored JSON up to 1 MiB characters; larger imported/HTTP events are skipped |136| Payload | 4 MiB plus 4 KiB envelope allowance; complete event when included |137138Timeouts, network failures, 429 and 5xx responses retry; other non-2xx139responses end that delivery. Redirects are never followed. Attempts are140reserved durably before HTTP starts, so a crash also consumes an attempt.141A `404` deletes the registration. Response bodies are canceled unread.142Queue saturation drops new work instead of delaying event acceptance.143No successful registration promises unlimited delivery or an indefinitely144retained event payload. `include_event` always carries the full event for145accepted deliveries; ID-only callbacks can reduce traffic. `search`, `limit`146and unknown filter fields are rejected rather than silently broadened.147148NIP-11 keeps existing identifiers numeric and includes literal `9a` while149push is enabled. The same mode includes enabled `5A` and implemented `AD`.150Some clients reject mixed arrays; see151[NIP-11 identifier compatibility](26-nip11-compatibility.md). Disable push152and `letteredNips` to restore numeric-only advertisement.153154## Validation155156The workerd object tests use a controlled callback receiver and injected157fetch boundary; they do not POST to public notification services. They cover158registration, asynchronous delivery, authorization changes, privacy across159read/export paths, callback failures, deletion and queue bounds. This proves160the relay payload and lifecycle contract, not interoperability with every161mobile push service. The callback service remains application infrastructure.