$ hookstr

● relay online

A store-and-forward webhook service backed by nostr. Webhook providers demand an always-online HTTPS endpoint and auto-disable it after repeated delivery failures. A consumer behind a tunnel — sometimes offline, tunnel URL rotating — loses that race constantly. hookstr splits delivery from consumption so providers see one endpoint that never goes down, and the consumer sees every webhook, eventually, in order, with valid signatures.

Lifetime of one webhook

Provider → hookstrd · T + 0 ms
A webhook arrives
A provider POSTs to /ingest/{token}/…. The reverse proxy terminates TLS and hands the request to hookstrd's axum ingest.
raw HTTP · body + headers
On hookstrd · T + ~1 ms
hookstrd turns it into a signed event
axum wraps the raw body (base64), the kept headers, and the URL path into one immutable kind:3003 nostr event, signed by the server key.
nostr event · kind 3003
On hookstrd · T + ~5 ms
It's saved, and the provider gets its 204
The event is committed to nostrdb. An id-subscription fires on commit — and only then does the provider get its 204. If the write fails it gets a 5xx and retries.
nostr event · kind 3003
Your machine is offline — the event sits on hookstrd for seconds or days. Nothing is ever deleted.
hookstrd → your machine · minutes or days later
Your drain comes back and pulls it down
The drain reconnects, authenticates (NIP-42), and negentropy-reconciles (NIP-77) every event it missed — then keeps following new ones live.
nostr event · over ws
On your machine · signature checked
The signature is checked before anything replays
Replay acts only on notes read back out of the drain's local nostrdb, after its ingester has verified the signature and committed them — never on wire frames.
nostr event · local db
Your machine → your app · replay
It's replayed as the original HTTP request
Unwrapped back into a real HTTP POST against your local target — byte-exact body, preserved headers — so an x-signature HMAC still validates.
raw HTTP · body + headers
In your app · done
Your app handles it, exactly as if it were live
Your handler dedupes on the delivery-id header and returns 2xx. Delivery is at-least-once, so a crash-mid-replay retry is always safe.
raw HTTP · body + headers
raw HTTP signed nostr event  — wrapped once, then unwrapped byte-for-byte.

Two halves

hookstrd

Runs permanently on always-online infra behind a TLS-terminating reverse proxy. Accepts any webhook with a fast 2xx, persists it as a signed nostr event in nostrdb, and serves those events back over an authenticated (NIP-42) embedded nostr relay.

hookstr drain

Runs wherever the consumer lives. Negentropy-syncs (NIP-77) whatever it missed while offline, replays each delivery as a real HTTP request — byte-exact body, preserved headers — against a local target, then keeps following in realtime.

Design

This URL is a nostr relay. Point a NIP-42-authenticated client at wss://<this host> to sync. Providers POST to /ingest/{token}/….