Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.gateway.relay

Relayed push — how a gateway wakes a phone WITHOUT holding Apple's or Google's signing key.

APNs binds a topic to the Apple team that owns it: a key minted by anyone else, aimed at someone else's bundle id, is refused forever (403 InvalidProviderToken / TopicDisallowed). So a self-hosted gateway can never push to a companion built and signed by somebody else — unless the signing key stays on infrastructure the app's publisher runs, and the gateway is given a capability instead of a credential.

That capability is a GRANT. The device asks the relay for one and hands it to this gateway during "notify this device"; the gateway POSTs {grant, title, body} and the relay signs and forwards. Consequences worth the indirection:

  • this gateway holds no .p8, no service-account JSON, nothing revocable only by breaking push for everyone else;
  • this gateway never learns the raw APNs/FCM device token, so a gateway you do not trust cannot fingerprint the device it notifies;
  • a grant expires by itself. Its expiry travels inside it, sealed, so the relay keeps no list of anybody and an abandoned gateway simply goes mute.

The relay itself lives in apps/vis-companion-relay (a Cloudflare Worker), and every gateway names the publisher's one by DEFAULT (DEFAULT-URL): a machine nobody configured is already able to push. A device may name another and is believed — which relay can sign for a build is a property of the BUILD, so the app mints its grant at the relay serving the app it is and posts {grant, relay_url} to /v1/devices. VIS_PUSH_RELAY_URL, or ~/.vis/relay.edn {:url "https://push.example.com"}, replaces the default on one machine; the direct gateway.push / gateway.fcm credentials still work exactly as before.

Relayed push — how a gateway wakes a phone WITHOUT holding Apple's or
Google's signing key.

APNs binds a topic to the Apple team that owns it: a key minted by anyone
else, aimed at someone else's bundle id, is refused forever (`403
InvalidProviderToken` / `TopicDisallowed`). So a self-hosted gateway can
never push to a companion built and signed by somebody else — unless the
signing key stays on infrastructure the app's publisher runs, and the
gateway is given a capability instead of a credential.

That capability is a GRANT. The *device* asks the relay for one and hands
it to this gateway during "notify this device"; the gateway POSTs
`{grant, title, body}` and the relay signs and forwards. Consequences worth
the indirection:

* this gateway holds no `.p8`, no service-account JSON, nothing revocable
  only by breaking push for everyone else;
* this gateway never learns the raw APNs/FCM device token, so a gateway you
  do not trust cannot fingerprint the device it notifies;
* a grant expires by itself. Its expiry travels inside it, sealed, so the
  relay keeps no list of anybody and an abandoned gateway simply goes mute.

The relay itself lives in `apps/vis-companion-relay` (a Cloudflare Worker),
and every gateway names the publisher's one by DEFAULT (`DEFAULT-URL`): a
machine nobody configured is already able to push. A device may name another
and is believed — which relay can sign for a build is a property of the
BUILD, so the app mints its grant at the relay serving the app it is and
posts `{grant, relay_url}` to `/v1/devices`. `VIS_PUSH_RELAY_URL`, or
`~/.vis/relay.edn` `{:url "https://push.example.com"}`, replaces the default
on one machine; the direct `gateway.push` / `gateway.fcm` credentials still
work exactly as before.
raw docstring

configclj

(config)

The relay this gateway names for every device that named none.

Configured by default; VIS_PUSH_RELAY_URL or ~/.vis/relay.edn replaces DEFAULT-URL on this machine, and an empty value turns relaying off. Never throws.

The relay this gateway names for every device that named none.

Configured by default; `VIS_PUSH_RELAY_URL` or `~/.vis/relay.edn` replaces
`DEFAULT-URL` on this machine, and an empty value turns relaying off. Never
throws.
sourceraw docstring

configured?clj

(configured?)

True when a grant registered here can actually be delivered.

True when a grant registered here can actually be delivered.
sourceraw docstring

dead-grant?clj

(dead-grant? {:keys [status]})

True when the relay's verdict means this grant will never deliver again — revoked by its owner, or dropped because the device unregistered.

True when the relay's verdict means this grant will never deliver again —
revoked by its owner, or dropped because the device unregistered.
sourceraw docstring

DEFAULT-URLclj

The relay a gateway names until an operator names another.

It is the publisher's, and the same address the app mints at: PUBLISHER_RELAY_URL in apps/vis-companion/src/lib/relay.ts. The two are one constant in two languages and relay-test reads that file to prove they have not drifted — a grant is sealed by ONE relay, so a gateway that guessed a different address could only ever be refused.

Naming it here is what makes the product claim true from the gateway's side too: features.push is available on a machine that was never configured, so nothing has to pretend push is broken until the first phone registers. Ship your own build and both constants move together, beside the bundle id and google-services.json that already had to.

The relay a gateway names until an operator names another.

It is the publisher's, and the same address the app mints at:
`PUBLISHER_RELAY_URL` in `apps/vis-companion/src/lib/relay.ts`. The two are
one constant in two languages and `relay-test` reads that file to prove they
have not drifted — a grant is sealed by ONE relay, so a gateway that guessed
a different address could only ever be refused.

Naming it here is what makes the product claim true from the gateway's side
too: `features.push` is available on a machine that was never configured, so
nothing has to pretend push is broken until the first phone registers. Ship
your own build and both constants move together, beside the bundle id and
`google-services.json` that already had to.
sourceraw docstring

maskclj

(mask grant)

A grant is a bearer capability. This is the ONLY form allowed into a log.

A grant is a bearer capability. This is the ONLY form allowed into a log.
sourceraw docstring

send!clj

(send! relay-url grant notification)

Ask relay-url to deliver one alert to the device this grant names. Returns {:status int :reason str} — status 0 for a transport failure, so this never throws. A stumble is tried once more; the relay answers 404/410 once the grant is gone, which is the caller's cue to forget the device.

The address is an argument, not a global: a grant is sealed by ONE relay, so it is only ever spendable at the one the device named when it registered.

Ask `relay-url` to deliver one alert to the device this grant names. Returns
`{:status int :reason str}` — status 0 for a transport failure, so this never
throws. A stumble is tried once more; the relay answers 404/410 once the
grant is gone, which is the caller's cue to forget the device.

The address is an argument, not a global: a grant is sealed by ONE relay, so
it is only ever spendable at the one the device named when it registered.
sourceraw docstring

statusclj

(status)

Relay half of the push capability. Carries the URL, never the grants.

Relay half of the push capability. Carries the URL, never the grants.
sourceraw docstring

usable-urlclj

(usable-url url)

The address a grant may be handed to, trimmed of trailing slashes — or nil.

A grant is a BEARER capability and the alert carries the title and body of what just happened, so the address both are handed to is TLS or nothing: silently trusting cleartext would put a permanent right to push to that phone on the wire. Loopback is the exception; it never reaches a network.

The address a grant may be handed to, trimmed of trailing slashes — or nil.

A grant is a BEARER capability and the alert carries the title and body of
what just happened, so the address both are handed to is TLS or nothing:
silently trusting cleartext would put a permanent right to push to that phone
on the wire. Loopback is the exception; it never reaches a network.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close