Documentation
¶
Overview ¶
Package push implements VAPID-encrypted Web Push delivery for v1.16 phase 4. The daemon generates a single VAPID keypair at first boot (persisted in app_kv), users opt in per device via the PushManager.subscribe() browser API, and the daemon fans critical-finding alerts out as encrypted push payloads.
No third-party push provider — Firebase / OneSignal would push payloads through their servers and need long-lived API keys. VAPID is the IETF-standard alternative: keys live in the daemon, pushes go directly to the browser push services (FCM, Mozilla, Apple). Honors the no-phone-home invariant (ADR-013 spirit).
Index ¶
- type Payload
- type Sender
- type Store
- func (s *Store) DeleteByEndpoint(ctx context.Context, userID, endpoint string) (int64, error)
- func (s *Store) EnsureVAPID(ctx context.Context) (publicKey, privateKey string, err error)
- func (s *Store) ListForUser(ctx context.Context, userID string) ([]Subscription, error)
- func (s *Store) Save(ctx context.Context, sub Subscription) (Subscription, error)
- type Subscription
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Payload ¶
type Payload struct {
Title string `json:"title"`
Body string `json:"body"`
URL string `json:"url,omitempty"`
Severity string `json:"severity,omitempty"`
Tag string `json:"tag,omitempty"` // browser dedupe key
}
Payload is the message envelope delivered to the client's service worker push handler. The browser's notification UI renders Title + Body; URL routes the user when they click the notification.
type Sender ¶
type Sender struct {
// contains filtered or unexported fields
}
Sender owns the HTTP client + VAPID keys used to push to every subscriber. Reused across calls — webpush-go's send is per-request so the sender itself stays cheap to construct.
func NewSender ¶
NewSender returns a Sender that signs every push with the daemon's VAPID keys. contactEmail goes into the JWT `sub` claim browsers use to identify the sender to push services. Empty email falls back to the deploy-time admin email or a placeholder.
func (*Sender) PublicKey ¶
PublicKey returns the VAPID public key in URL-safe base64. The browser needs this to call PushManager.subscribe(applicationServerKey).
func (*Sender) SendToUser ¶
func (s *Sender) SendToUser(ctx context.Context, userID string, p Payload) (sent, failed int, err error)
SendToUser dispatches the payload to every subscription belonging to userID. Returns (sent, failed) counts; failed deliveries are logged but don't abort the loop. A 404/410 response from the push service means the subscription is stale and gets deleted.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store wraps the SQLite/Postgres push tables.
func (*Store) DeleteByEndpoint ¶
DeleteByEndpoint removes a subscription matching the user + endpoint. Returns the number of rows deleted (0 if the user didn't have a subscription with that endpoint).
func (*Store) EnsureVAPID ¶
EnsureVAPID returns the daemon's VAPID keypair, generating + saving it on first call. Subsequent calls return the persisted pair. Returning the keys (rather than caching in memory) keeps the implementation stateless across daemon restarts + HA replicas.
func (*Store) ListForUser ¶
ListForUser returns every subscription the user has registered, most-recent first. Used by the /settings/notifications UI to render the per-device list.
func (*Store) Save ¶
func (s *Store) Save(ctx context.Context, sub Subscription) (Subscription, error)
Save upserts a Subscription by (user_id, endpoint). Idempotent; a refresh-token / reinstall returns the same id.
type Subscription ¶
type Subscription struct {
ID string
UserID string
Endpoint string
P256dhKey string
AuthKey string
DeviceLbl string
CreatedAt time.Time
LastUsedAt time.Time
}
Subscription is the server-side record of a single browser/ device push registration. Mirrors the wire shape browsers send when ServiceWorkerRegistration.pushManager.subscribe() returns.