Documentation
¶
Overview ¶
Package webpush sends reminders as Web Push notifications to the browsers and installed apps (PWA) where a member turned them on (ADR 0026).
Each device is one destination: a channel link whose external id is the push endpoint, next to the browser's keys. Messages are encrypted for the browser (RFC 8291) and signed with the instance's VAPID key (RFC 8292), both written on the standard library.
A notification can carry up to two action buttons taken from the message's own buttons (actions.PushButtons). Each carries a signed token for that device, charge and verb, so the service worker can run it with no tab open and no session.
Index ¶
Constants ¶
const ActionTTL = 72 * time.Hour
ActionTTL is how long a notification's buttons keep working. After that the service worker opens the charge in the web instead.
const MaxPlaintext = recordSize - headerLen - 16 - 1
MaxPlaintext is the longest message that still fits in one record: the record holds the plaintext, the padding delimiter and the 16-octet tag, and push services limit the body (header included) to 4096 octets.
const Name = "webpush"
Name is the channel name in channel_links.
const TTL = 72 * time.Hour
TTL is how long a push service keeps a message for a device that is offline.
Variables ¶
var ErrBadToken = errors.New("this button has expired")
ErrBadToken means the token is not one of ours, or has expired.
var ErrNoDevice = errors.New("no such device")
ErrNoDevice is returned by a Lookup that does not know the endpoint.
var ErrTooLarge = errors.New("push message too large")
ErrTooLarge is returned for a message longer than MaxPlaintext.
Functions ¶
func SignAction ¶
SignAction returns a token for a, signed with key.
Types ¶
type Action ¶
type Action struct {
LinkID string // the device (channel_links.id) the notification went to
MemberID string
ChargeID string
Verb string // button verb of the action layer, e.g. "paid"
Expires time.Time
}
Action is what a token allows.
type ActionButton ¶
type ActionButton struct {
Action string `json:"action"`
Title string `json:"title"`
Token string `json:"token"`
}
ActionButton is a button of a notification.
type Channel ¶
type Channel struct {
notify.OneWay
// Lookup returns the device of an endpoint.
Lookup func(ctx context.Context, endpoint string) (Device, error)
// Gone forgets a device the push service says no longer exists.
Gone func(ctx context.Context, endpoint string)
// Keys returns the instance's VAPID key pair and the key that signs
// action tokens.
Keys func(ctx context.Context) (VAPID, []byte, error)
// Subject is the contact in the VAPID claims (mailto: or https:).
Subject string
// Client must come from a netguard.Guard: endpoints are sent by
// browsers, so anyone signed in can choose one.
Client *http.Client
// Now defaults to time.Now.
Now func() time.Time
}
Channel is the Web Push channel. Its chat id is the push endpoint.
func (*Channel) Push ¶
func (c *Channel) Push(ctx context.Context, endpoint string, keys Keys, vapid VAPID, p Payload, urgency string) error
Push delivers one payload to an endpoint.
type Keys ¶
type Keys struct {
P256dh []byte // uncompressed P-256 point, 65 octets
Auth []byte // authentication secret, 16 octets
}
Keys are a browser's keys from its PushSubscription.
type Payload ¶
type Payload struct {
Title string `json:"title"`
Body string `json:"body,omitempty"`
Tag string `json:"tag,omitempty"`
URL string `json:"url"`
Actions []ActionButton `json:"actions,omitempty"`
}
Payload is what the service worker receives (web/public/sw.js).
type VAPID ¶
type VAPID struct {
// contains filtered or unexported fields
}
VAPID is the instance's key pair. Generate it once and keep it: changing it invalidates every browser subscription.
func ParseVAPID ¶
ParseVAPID reads a private key saved with PrivateKey.
func (VAPID) Header ¶
Header returns the Authorization header for a request to endpoint: `vapid t=<JWT>, k=<public key>`. subject is a mailto: or https: contact for the push service's operators.
func (VAPID) PrivateKey ¶
PrivateKey is the private scalar, base64url, for saving.