uebpush

package module
v0.0.0-...-0f5ab51 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 5, 2026 License: MIT Imports: 12 Imported by: 0

README

uebpush

A Go library and CLI for receiving Web Push notifications via GCM/FCM.

This project is a Golang implementation based entirely on Chromium's GCM (Google Cloud Messaging). It reproduces the browser's Web Push registration and message receiving process, allowing you to receive browser push notifications on a server or any Go application.

How it works

  1. Device Checkin: Simulates a Chrome browser registering with Google's servers
  2. FCM Registration: Obtains a push endpoint just like a browser would
  3. MCS Connection: Maintains a persistent connection to Google's Mobile Connection Server (mtalk.google.com)
  4. Message Decryption: Decrypts incoming push messages using Web Push encryption (RFC 8291)

This enables server-side applications to receive the same push notifications that would normally only be available in a browser context.

Installation

As a library
go get github.com/pedrohavay/uebpush
As a CLI
go install github.com/pedrohavay/uebpush/cmd/uebpush@latest

Usage

CLI

The CLI provides two commands: register to create a new push subscription, and listen to receive notifications.

Register a new subscription
uebpush register -origin https://example.com -vapid <VAPID_PUBLIC_KEY>

Flags:

Flag Required Default Description
-origin Yes - The origin URL of the web application (e.g., https://example.com)
-vapid Yes - The VAPID public key from the push service (base64url encoded)
-out No credentials.json Output file path for the generated credentials

What happens:

  1. Performs a device checkin with Google's servers
  2. Generates ECDH P-256 keys for message encryption
  3. Registers with FCM to obtain a push endpoint
  4. Saves all credentials to the output file

Output example:

Subscription info:
  endpoint:  https://fcm.googleapis.com/fcm/send/...
  p256dh:    BNx5...
  auth:      abc123...

Use these values to subscribe to push notifications on the target website.

Listen for notifications
uebpush listen

Flags:

Flag Default Description
-creds credentials.json Path to credentials file
-ids persistent_ids.json Path to persistent IDs file (tracks unacknowledged messages)

The listener maintains a persistent connection and automatically:

  • Reconnects on disconnection with exponential backoff
  • Sends heartbeat pings to keep the connection alive
  • Acknowledges received messages to prevent duplicates

Library

The library provides a programmatic API with hooks for handling events.

Basic Usage
package main

import (
    "context"
    "fmt"
    "log"
    "os/signal"
    "syscall"

    "github.com/pedrohavay/uebpush"
)

func main() {
    // Create configuration with origin and VAPID key
    config := uebpush.NewConfig("https://example.com", "BPxH...")

    // Create client with event hooks
    client, err := uebpush.NewClient(config,
        uebpush.WithOnMessage(func(msg *uebpush.Message) {
            fmt.Printf("Received: %s\n", string(msg.Payload))
        }),
        uebpush.WithOnConnected(func() {
            log.Println("Connected to MCS!")
        }),
        uebpush.WithOnError(func(err error) {
            log.Printf("Error: %v", err)
        }),
    )
    if err != nil {
        log.Fatal(err)
    }

    // Setup graceful shutdown
    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer stop()

    // Run will register (if needed) and start listening
    if err := client.Run(ctx); err != nil && ctx.Err() == nil {
        log.Fatal(err)
    }
}
Separate Registration and Listening

If you need more control, you can separate the registration and listening steps:

// First, register to get subscription info
info, err := client.Register(ctx)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Send this to your push server:\n")
fmt.Printf("  Endpoint: %s\n", info.Endpoint)
fmt.Printf("  P256dh:   %s\n", info.P256dh)
fmt.Printf("  Auth:     %s\n", info.Auth)

// Later, start listening for notifications
if err := client.Listen(ctx); err != nil {
    log.Fatal(err)
}
Custom Storage (Database, Redis, etc.)

By default, uebpush stores credentials and persistent IDs in JSON files. You can provide your own storage backend:

// Load credentials from your database
creds := loadCredentialsFromDB()

client, err := uebpush.NewClient(config,
    // Provide credentials directly (skips file loading)
    uebpush.WithCredentials(creds),

    // Disable all file-based storage
    uebpush.WithDisableFileStorage(),

    // Custom persistent IDs handlers
    uebpush.WithPersistentIDsLoader(func() ([]string, error) {
        return redis.LRange("push:persistent_ids", 0, -1)
    }),
    uebpush.WithPersistentIDsSaver(func(ids []string) error {
        return redis.Set("push:persistent_ids", ids)
    }),

    uebpush.WithOnMessage(func(msg *uebpush.Message) {
        processNotification(msg)
    }),
)

Available storage options:

Option Description
WithCredentials(creds) Provide credentials directly, bypassing file loading
WithPersistentIDs(ids) Provide initial persistent IDs directly
WithPersistentIDsLoader(fn) Custom function to load persistent IDs
WithPersistentIDsSaver(fn) Custom function to save persistent IDs
WithDisableFileStorage() Disable all automatic file operations
Using Environment Variables

Example loading credentials from environment:

import (
    "encoding/json"
    "os"

    "github.com/pedrohavay/uebpush"
    "github.com/pedrohavay/uebpush/credentials"
)

func main() {
    // Load credentials from environment variable
    var creds credentials.Credentials
    if err := json.Unmarshal([]byte(os.Getenv("PUSH_CREDENTIALS")), &creds); err != nil {
        log.Fatal(err)
    }

    config := &uebpush.Config{} // No file paths needed

    client, err := uebpush.NewClient(config,
        uebpush.WithCredentials(&creds),
        uebpush.WithDisableFileStorage(),
        uebpush.WithOnMessage(handleMessage),
    )
    // ...
}
Full Example with All Hooks
client, err := uebpush.NewClient(config,
    // Called after successful registration
    uebpush.WithOnRegister(func(info *uebpush.SubscriptionInfo) {
        log.Printf("Registered! Endpoint: %s", info.Endpoint)
        // Save subscription info to your server
        saveSubscription(info)
    }),

    // Called when a push notification is received
    uebpush.WithOnMessage(func(msg *uebpush.Message) {
        log.Printf("Message ID: %s", msg.PersistentID)
        log.Printf("Payload: %s", string(msg.Payload))
        // msg.AppData contains additional metadata
    }),

    // Called when connected to MCS
    uebpush.WithOnConnected(func() {
        log.Println("Connected and ready to receive notifications")
    }),

    // Called when connection is lost (will auto-reconnect)
    uebpush.WithOnDisconnected(func(err error) {
        log.Printf("Disconnected: %v (reconnecting...)", err)
    }),

    // Called on any error during operation
    uebpush.WithOnError(func(err error) {
        log.Printf("Error: %v", err)
    }),
)

Configuration

Config struct
type Config struct {
    // Origin is the web push origin URL (e.g., "https://example.com")
    // Required for registration
    Origin string

    // VAPIDKey is the VAPID public key (base64url encoded)
    // Required for registration
    VAPIDKey string

    // CredentialsFile is the path to store/load credentials
    // Default: "credentials.json"
    CredentialsFile string

    // PersistentIDsFile is the path to store unacknowledged message IDs
    // Default: "persistent_ids.json"
    PersistentIDsFile string
}
Helper function
// Creates config with defaults
config := uebpush.NewConfig("https://example.com", "VAPID_KEY")

// Or create manually for more control
config := &uebpush.Config{
    Origin:            "https://example.com",
    VAPIDKey:          "VAPID_KEY",
    CredentialsFile:   "/path/to/creds.json",
    PersistentIDsFile: "/path/to/ids.json",
}

Hooks

Hook Type Description
OnRegister func(*SubscriptionInfo) Called after successful registration
OnMessage func(*Message) Called when a notification is received and decrypted
OnConnected func() Called when MCS connection is established
OnDisconnected func(error) Called when MCS connection is lost
OnError func(error) Called when an error occurs

Types

SubscriptionInfo

Returned after registration. Send these values to your push server:

type SubscriptionInfo struct {
    Endpoint string // FCM push endpoint URL
    P256dh   string // Client public key (base64url)
    Auth     string // Auth secret (base64url)
}
Message

Received when a push notification arrives:

type Message struct {
    PersistentID string            // Unique message ID from GCM
    Payload      []byte            // Decrypted message content
    AppData      map[string]string // Additional metadata from headers
}
Credentials

The full credentials structure (can be serialized to JSON):

type Credentials struct {
    GCM struct {
        AndroidID     uint64 `json:"android_id"`
        SecurityToken uint64 `json:"security_token"`
    } `json:"gcm"`
    FCM struct {
        Token    string `json:"token"`
        Endpoint string `json:"endpoint"`
    } `json:"fcm"`
    Keys struct {
        PrivateKey string `json:"private_key"`
        PublicKey  string `json:"public_key"`
        AuthSecret string `json:"auth_secret"`
    } `json:"keys"`
    InstanceID string `json:"instance_id"`
    AppID      string `json:"app_id"`
}

Client Methods

// Create a new client
func NewClient(config *Config, opts ...Option) (*Client, error)

// Register for push notifications (generates new credentials)
func (c *Client) Register(ctx context.Context) (*SubscriptionInfo, error)

// Listen for notifications (blocks until context is cancelled)
func (c *Client) Listen(ctx context.Context) error

// Convenience: Register if needed, then Listen
func (c *Client) Run(ctx context.Context) error

// Get current credentials
func (c *Client) Credentials() *credentials.Credentials

// Set credentials programmatically
func (c *Client) SetCredentials(creds *credentials.Credentials) error

// Save credentials to a specific path
func (c *Client) SaveCredentials(path string) error

// Get list of unacknowledged message IDs
func (c *Client) PersistentIDs() []string

// Clear all persistent IDs
func (c *Client) ClearPersistentIDs() error

// Close the client
func (c *Client) Close() error

License

MIT

Documentation

Overview

Package uebpush provides a Go library for receiving Web Push notifications via Google Cloud Messaging (GCM) / Firebase Cloud Messaging (FCM).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a Web Push client that can register for push notifications and listen for incoming messages.

func NewClient

func NewClient(config *Config, opts ...Option) (*Client, error)

NewClient creates a new Web Push client with the given configuration. If credentials are not provided via WithCredentials, attempts to load from file.

func (*Client) ClearPersistentIDs

func (c *Client) ClearPersistentIDs() error

ClearPersistentIDs removes all stored persistent IDs.

func (*Client) Close

func (c *Client) Close() error

Close gracefully shuts down the client.

func (*Client) Credentials

func (c *Client) Credentials() *credentials.Credentials

Credentials returns the current credentials, or nil if not registered.

func (*Client) Listen

func (c *Client) Listen(ctx context.Context) error

Listen connects to MCS and listens for push notifications. This method blocks until the context is cancelled. Credentials must be available (either loaded, from Register, or via WithCredentials).

func (*Client) PersistentIDs

func (c *Client) PersistentIDs() []string

PersistentIDs returns the list of unacknowledged message IDs. If a custom loader was provided, it will be used. Otherwise, reads from file.

func (*Client) Register

func (c *Client) Register(ctx context.Context) (*SubscriptionInfo, error)

Register performs device registration with GCM/FCM to obtain push credentials. Returns subscription info that can be sent to a push server.

func (*Client) Run

func (c *Client) Run(ctx context.Context) error

Run performs registration (if needed) and starts listening for notifications. This is a convenience method that combines Register and Listen.

func (*Client) SaveCredentials

func (c *Client) SaveCredentials(path string) error

SaveCredentials saves the current credentials to the specified path.

func (*Client) SetCredentials

func (c *Client) SetCredentials(creds *credentials.Credentials) error

SetCredentials sets the credentials directly.

type Config

type Config struct {
	// Origin is the web push origin URL (e.g., "https://example.com").
	// Required for registration.
	Origin string

	// VAPIDKey is the VAPID public key from the push service (base64url encoded).
	// Required for registration.
	VAPIDKey string

	// CredentialsFile is the path to store/load credentials.
	// Defaults to "credentials.json".
	CredentialsFile string

	// PersistentIDsFile is the path to store unacknowledged message IDs.
	// Defaults to "persistent_ids.json".
	PersistentIDsFile string
}

Config holds the configuration for the Web Push client.

func NewConfig

func NewConfig(origin, vapidKey string) *Config

NewConfig creates a new Config with the given origin and VAPID key. Uses default paths for credentials and persistent IDs files.

type Hooks

type Hooks struct {
	// OnRegister is called after successful registration with subscription info
	OnRegister func(info *SubscriptionInfo)

	// OnMessage is called when a push notification is received and decrypted
	OnMessage func(msg *Message)

	// OnConnected is called when the MCS connection is established
	OnConnected func()

	// OnDisconnected is called when the MCS connection is lost
	OnDisconnected func(err error)

	// OnError is called when an error occurs during operation
	OnError func(err error)
}

Hooks contains callback functions for various client events.

type Message

type Message struct {
	// PersistentID is the unique message identifier from GCM
	PersistentID string
	// Payload is the decrypted message content
	Payload []byte
	// AppData contains metadata from the push message headers
	AppData map[string]string
}

Message represents a decrypted push notification message.

type Option

type Option func(*Client)

Option configures a Client.

func WithCredentials

func WithCredentials(creds *credentials.Credentials) Option

WithCredentials sets the credentials directly, bypassing file loading.

func WithDisableFileStorage

func WithDisableFileStorage() Option

WithDisableFileStorage disables automatic file-based storage for credentials and persistent IDs. Useful when providing custom storage handlers.

func WithOnConnected

func WithOnConnected(fn func()) Option

WithOnConnected sets the OnConnected hook.

func WithOnDisconnected

func WithOnDisconnected(fn func(error)) Option

WithOnDisconnected sets the OnDisconnected hook.

func WithOnError

func WithOnError(fn func(error)) Option

WithOnError sets the OnError hook.

func WithOnMessage

func WithOnMessage(fn func(*Message)) Option

WithOnMessage sets the OnMessage hook.

func WithOnRegister

func WithOnRegister(fn func(*SubscriptionInfo)) Option

WithOnRegister sets the OnRegister hook.

func WithPersistentIDs

func WithPersistentIDs(ids []string) Option

WithPersistentIDs sets the initial persistent IDs directly.

func WithPersistentIDsLoader

func WithPersistentIDsLoader(loader PersistentIDsLoader) Option

WithPersistentIDsLoader sets a custom function to load persistent IDs. Takes precedence over file-based loading.

func WithPersistentIDsSaver

func WithPersistentIDsSaver(saver PersistentIDsSaver) Option

WithPersistentIDsSaver sets a custom function to save persistent IDs. Takes precedence over file-based saving.

type PersistentIDsLoader

type PersistentIDsLoader func() ([]string, error)

PersistentIDsLoader is a function that loads persistent IDs from custom storage.

type PersistentIDsSaver

type PersistentIDsSaver func(ids []string) error

PersistentIDsSaver is a function that saves persistent IDs to custom storage.

type SubscriptionInfo

type SubscriptionInfo struct {
	// Endpoint is the FCM push endpoint URL
	Endpoint string `json:"endpoint"`
	// P256dh is the client's ECDH public key (base64url encoded)
	P256dh string `json:"p256dh"`
	// Auth is the authentication secret (base64url encoded)
	Auth string `json:"auth"`
}

SubscriptionInfo contains the Web Push subscription details needed to send push notifications to this client.

Directories

Path Synopsis
cmd
uebpush command
internal
mcs

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL