1
0
Fork 0
mirror of https://github.com/imjasonh/webpush synced 2026-07-06 23:52:25 +00:00
No description
Find a file
Jason Hall ee97c9b955 deploy script, use kms
Signed-off-by: Jason Hall <jason@chainguard.dev>
2025-11-25 11:04:19 -06:00
example deploy script, use kms 2025-11-25 11:04:19 -06:00
keys Implement Web Push API Go library with VAPID support and pluggable storage 2025-11-24 21:19:16 +00:00
storage deploy script, use kms 2025-11-25 11:04:19 -06:00
vapid Implement Web Push API Go library with VAPID support and pluggable storage 2025-11-24 21:19:16 +00:00
deploy.sh deploy script, use kms 2025-11-25 11:04:19 -06:00
go.mod deploy script, use kms 2025-11-25 11:04:19 -06:00
go.sum deploy script, use kms 2025-11-25 11:04:19 -06:00
integration_test.go Implement Web Push API Go library with VAPID support and pluggable storage 2025-11-24 21:19:16 +00:00
LICENSE Initial commit 2025-11-24 15:08:56 -06:00
README.md Use go:embed for static content and simplify README to reference example 2025-11-24 22:28:44 +00:00
webpush.go Implement Web Push API Go library with VAPID support and pluggable storage 2025-11-24 21:19:16 +00:00
webpush_test.go Implement Web Push API Go library with VAPID support and pluggable storage 2025-11-24 21:19:16 +00:00

webpush

A Go library for sending Web Push API notifications with VAPID authentication.

Features

  • RFC 8291 compliant message encryption (aes128gcm)
  • RFC 8292 VAPID authentication
  • Pluggable VAPID key providers:
    • File-based (PEM or base64 encoded)
    • Google Cloud KMS
  • Pluggable subscription storage:
    • In-memory (for testing/development)
    • SQLite
  • Easy integration with JavaScript Push API clients

Installation

go get github.com/imjasonh/webpush

Quick Start

See the example/ directory for a complete working demo with:

  • VAPID key generation and storage on disk
  • SQLite subscription storage
  • Web client with service worker for push notifications
  • Automatic periodic notifications and manual /ping endpoint

To run the example:

cd example
go run main.go

Then open http://localhost:8080 in your browser.

Basic Usage

package main

import (
    "context"
    "github.com/imjasonh/webpush"
    "github.com/imjasonh/webpush/keys"
)

func main() {
    // Load or generate VAPID keys
    signer, err := keys.NewFileSigner("vapid-private.pem")
    if err != nil {
        panic(err)
    }

    // Create client with VAPID subject (must be mailto: or https: URL)
    client := webpush.NewClient(signer, "mailto:admin@example.com")

    // Parse subscription from client (received from browser)
    sub, err := webpush.ParseSubscription([]byte(`{
        "endpoint": "https://fcm.googleapis.com/fcm/send/...",
        "keys": {
            "p256dh": "...",
            "auth": "..."
        }
    }`))
    if err != nil {
        panic(err)
    }

    // Send notification
    err = client.Send(context.Background(), sub, []byte(`{"title":"Hello","body":"World"}`), &webpush.Options{
        TTL:     3600,
        Urgency: "high",
    })
    if err != nil {
        panic(err)
    }
}

Using Google Cloud KMS

For production environments, you can store your VAPID private key in Google Cloud KMS:

signer, err := keys.NewKMSSigner(ctx, "projects/my-project/locations/global/keyRings/webpush/cryptoKeys/vapid/cryptoKeyVersions/1")
if err != nil {
    panic(err)
}
defer signer.Close()

client := webpush.NewClient(signer, "mailto:admin@example.com")

Create a KMS key:

gcloud kms keyrings create webpush --location=global
gcloud kms keys create vapid \
    --keyring=webpush \
    --location=global \
    --purpose=asymmetric-signing \
    --default-algorithm=ec-sign-p256-sha256

API Reference

Types

type Subscription struct {
    Endpoint string `json:"endpoint"`
    Keys     Keys   `json:"keys"`
}

type Options struct {
    TTL     int    // Time-to-live in seconds (default: 2419200 = 4 weeks)
    Urgency string // very-low, low, normal, high
    Topic   string // Topic for message replacement
}

Key Providers

// File-based (PEM)
signer, err := keys.NewFileSigner("path/to/key.pem")

// File-based (base64)
signer, err := keys.NewFileSignerFromBase64("base64-private-key")

// Google Cloud KMS
signer, err := keys.NewKMSSigner(ctx, "projects/.../cryptoKeyVersions/1")

// Generate new keys
signer, err := keys.GenerateKey("path/to/save.pem")
privateB64, publicB64, err := keys.GenerateKeyPair()

Storage

// In-memory (for testing)
store := storage.NewMemory()

// SQLite
store, err := storage.NewSQLite("subscriptions.db")

// Operations
store.Save(ctx, record)
store.Get(ctx, id)
store.GetByEndpoint(ctx, endpoint)
store.GetByUserID(ctx, userID)
store.List(ctx, limit, offset)
store.Delete(ctx, id)
store.DeleteByEndpoint(ctx, endpoint)

Custom Implementations

Custom Storage

Implement the storage.Storage interface:

type Storage interface {
    Save(ctx context.Context, record *Record) error
    Get(ctx context.Context, id string) (*Record, error)
    GetByEndpoint(ctx context.Context, endpoint string) (*Record, error)
    GetByUserID(ctx context.Context, userID string) ([]*Record, error)
    Delete(ctx context.Context, id string) error
    DeleteByEndpoint(ctx context.Context, endpoint string) error
    List(ctx context.Context, limit, offset int) ([]*Record, error)
    Close() error
}

Custom Key Provider

Implement the webpush.Signer interface:

type Signer interface {
    Sign(ctx context.Context, data []byte) ([]byte, error)
    PublicKey() []byte
}

The Sign method receives a SHA-256 hash and should return an IEEE P1363 format signature (64 bytes for P-256).

License

Apache 2.0 - See LICENSE for details.