Zig 0.17.0

push (Web Push)

On this page:

  • spider.push: Web Push notifications to browsers (VAPID).

spider.push

spider.push: Web Push. Sends an encrypted notification to a browser's push subscription (RFC 8291, aes128gcm), signed with the app's VAPID keys (RFC 8292).

spider.push.VapidKeys

zig
pub const VapidKeys = struct

A new VAPID key pair as raw bytes, from WebPush.generateKeys. To use it in a PushConfig (or in the environment), encode each key as base64url without padding.

Fields

  • private_key: [32]u8: The P-256 private scalar, big-endian.
  • public_key: [65]u8: The P-256 public point, uncompressed (it starts with 0x04).

push.VapidKeys.privateKeyText

zig
pub fn privateKeyText(self: VapidKeys) [43]u8

The private key as PushConfig.private_key and VAPID_PRIVATE_KEY take it: base64url without padding (43 characters).

push.VapidKeys.publicKeyText

zig
pub fn publicKeyText(self: VapidKeys) [87]u8

The public key as PushConfig.public_key, VAPID_PUBLIC_KEY and the page's applicationServerKey take it: base64url without padding (87 characters).

spider.push.PushConfig

zig
pub const PushConfig = struct

The identity the app sends pushes with.

Fields

  • subject: []const u8: The contact a push service may use, as a mailto: or https: URL. It goes in the sub claim.
  • private_key: []const u8: The VAPID private key: the 32 bytes, base64url without padding.
  • public_key: []const u8: The VAPID public key: the 65 bytes, base64url without padding. It is the applicationServerKey the page subscribes with, and is sent as it is.

spider.push.PushSubscription

zig
pub const PushSubscription = struct

One browser subscription, with the three values of the browser's PushSubscription.toJSON(). The app stores them when the user subscribes.

Fields

  • endpoint: []const u8: The push service URL of this subscription.
  • p256dh: []const u8: The browser's public key (keys.p256dh): 65 bytes, base64url without padding.
  • auth: []const u8: The authentication secret (keys.auth): 16 bytes, base64url without padding.

spider.push.WebPush

zig
pub const WebPush = struct

The Web Push sender.

zig
const wp = spider.push.WebPush.initFromEnv();
try wp.sendRaw(arena, io, .{
    .endpoint = s.endpoint,
    .p256dh = s.p256dh,
    .auth = s.auth,
}, payload, 86400);

Fields

  • config: PushConfig

push.WebPush.init

zig
pub fn init(config: PushConfig) WebPush

A sender with the given keys. config is not copied: its strings must outlive the sender.

push.WebPush.initFromEnv

zig
pub fn initFromEnv() WebPush

A sender configured from the variables VAPID_SUBJECT, VAPID_PRIVATE_KEY and VAPID_PUBLIC_KEY (the environment or .env). A variable that is not set becomes an empty string.

push.WebPush.generateKeys

zig
pub fn generateKeys(io: std.Io) VapidKeys

A new random VAPID key pair. Generate it once and keep it: subscriptions are tied to the public key. keys.privateKeyText() and keys.publicKeyText() are the two values to put in the config.

push.WebPush.sendRaw

zig
pub fn sendRaw(
    self: *const WebPush,
    arena: std.mem.Allocator,
    io: std.Io,
    subscription: PushSubscription,
    payload: []const u8,
    ttl: u32,
) !void

Encrypts payload for subscription and posts it to the subscription's push service, outside a request (a job, a boot hook). ttl is how many seconds the push service keeps the message for a device that is offline. The message is sent with Urgency: high. Everything is allocated in arena.

Fails with error.PushSubscriptionExpired when the service answers 410 (delete the subscription), error.PushForbidden for 403 (the VAPID keys are not the ones the subscription was made with), error.PushSendFailed for any other status that is not 200, 201 or 204, and with the HTTP client's error when the service cannot be reached. error.InvalidKeyLength when p256dh or auth of the subscription, or the private key of the config, does not decode to the size its kind has (65, 16 and 32 bytes; a forged or damaged subscription): nothing is sent. The public key of the config is not checked: it is sent as it is. A key that is not base64url, a p256dh that is not a point of the curve and an endpoint that is not a URL fail too, with the error of the decoder, of the curve or of the URL parser. error.PayloadTooLarge when payload is longer than max_payload_len (3993 bytes): what one push message holds.

push.WebPush.send

zig
pub fn send(
    self: *const WebPush,
    c: *Ctx,
    subscription: PushSubscription,
    payload: []const u8,
    ttl: u32,
) !void

sendRaw from a handler: it uses the request's arena and Io. Same errors; both log a refused push at warn with the status and the push service (never the whole endpoint).

spider.push.max_payload_len

zig
pub const max_payload_len = 4096 - 86 - 17

The largest payload one push message carries, in bytes. Push services take a body of 4096 bytes at most; 86 of them are the header of the encrypted content and 17 the padding mark and the authentication tag.