notify

package
v0.3.7 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Overview

Package notify tells someone about alerts (docs/PHASE3.md §9.3): one digest per evaluation and channel, sent by e-mail (net/smtp) or as a signed webhook, queued in system.db with retries. The channels and their secrets come from the configuration and files, never from the database, so a sealed server still reports that it is sealed.

Index

Constants

View Source
const (
	Opened   = "opened"   // new alert
	Raised   = "raised"   // warning → critical
	Reminder = "reminder" // still open after repeat_every
	Resolved = "resolved"
	Test     = "test" // `myownpki notify test`
)

States of an event in a digest.

View Source
const ActionTest = "notification.test"

ActionTest is the audit action of a test message.

View Source
const SignatureHeader = "X-MyOwnPKI-Signature"

SignatureHeader carries the HMAC-SHA256 of the body: "sha256=<hex>".

Variables

View Source
var ErrNoChannel = errors.New("no notification channel is configured – set notify.email or notify.webhook in the server's configuration")

ErrNoChannel: nothing to test, because neither notify.email nor notify.webhook is configured.

View Source
var Retries = []time.Duration{time.Minute, 5 * time.Minute, 30 * time.Minute, 2 * time.Hour}

Retries are the waits after the 1st, 2nd, … failed attempt; after the last one the message is dropped (and logged as an error).

Functions

func Enqueue

func Enqueue(ctx context.Context, tx *sql.Tx, now time.Time, d Digest, channels []string) error

Enqueue adds d for every channel inside tx (the transaction that marks the alerts as notified, so a crash loses or doubles nothing).

func Sign

func Sign(secret, body []byte) string

Sign is the signature header value for body.

Types

type Channel

type Channel interface {
	Name() string
	Send(ctx context.Context, d Digest) error
}

Channel delivers a digest.

type Digest

type Digest struct {
	Server  string    `json:"server"`
	Created time.Time `json:"created"`
	Events  []Event   `json:"events"`
}

Digest is one message: everything that changed in one evaluation.

func ParseDigest

func ParseDigest(data []byte) (Digest, error)

ParseDigest reads a queued payload.

func TestDigest

func TestDigest(server string, now time.Time) Digest

TestDigest is the message of `myownpki notify test`.

func (Digest) JSON

func (d Digest) JSON() ([]byte, error)

JSON is the webhook body and the queued payload.

func (Digest) Subject

func (d Digest) Subject() string

Subject is the e-mail subject: "[MyOwnPKI] pki01: 2 new, 1 resolved".

func (Digest) Text

func (d Digest) Text() string

Text is the plain-text e-mail body, grouped by state.

type Email

type Email struct {
	Addr         string // host:port
	Security     string // config.SMTPStartTLS, SMTPTLS or SMTPNone
	Username     string
	PasswordFile string
	From         string
	To           []string
	CAFile       string // extra roots; "" = the system roots
	Hello        string // name for EHLO; "" = localhost
}

Email sends a digest over SMTP.

func NewEmail

func NewEmail(c config.Email, hello string) *Email

NewEmail builds the channel from notify.email.

func (*Email) Name

func (e *Email) Name() string

Name is the channel in the notifications queue.

func (*Email) Send

func (e *Email) Send(ctx context.Context, d Digest) error

Send delivers one message. The password is read from its file now, so a changed password needs no restart; it never appears in an error.

type Event

type Event struct {
	Kind     string    `json:"kind"`
	Subject  string    `json:"subject"`
	Severity string    `json:"severity"`
	State    string    `json:"state"`
	Since    time.Time `json:"since"`
	Detail   string    `json:"detail"`
	Link     string    `json:"link,omitempty"`
}

Event is one alert in a digest.

type Queue

type Queue struct {
	DB       *sql.DB // system.db, the write connection
	Log      *slog.Logger
	Channels []Channel
}

Queue delivers the queued digests of system.db.

func (*Queue) Deliver

func (q *Queue) Deliver(ctx context.Context, now time.Time) error

Deliver sends every due message once. A failure schedules the next attempt; the error is logged, never returned – one broken channel must not stop the other one.

func (*Queue) Names

func (q *Queue) Names() []string

Names are the names of the configured channels.

func (*Queue) Prune

func (q *Queue) Prune(ctx context.Context, cutoff time.Time) (int64, error)

Prune deletes messages sent or dropped before cutoff.

type Result

type Result struct {
	Channel string `json:"channel"`
	OK      bool   `json:"ok"`
	Error   string `json:"error,omitempty"`
}

Result is the outcome of one channel. Error is the channel's own error text, shortened: never a secret (password, HMAC key, webhook path or query), but it may name hosts, file paths and recipient addresses.

func SendAll

func SendAll(ctx context.Context, channels []Channel, d Digest) []Result

SendAll sends d through every channel at once (a slow mail server must not delay the webhook) and returns one result per channel, in order.

type Tester

type Tester struct {
	Channels []Channel
	Server   string // the host name in the message
	Clock    clock.Clock
	Audit    *sql.DB // writer of the tenant's audit log
	Scope    string
	Tenant   string
	// contains filtered or unexported fields
}

Tester sends the test message of the web UI and the REST API (`myownpki notify test` uses SendAll directly). It checks the role, sends synchronously and audits the attempt.

func (*Tester) Names

func (t *Tester) Names() []string

Names lists the configured channels.

func (*Tester) Send

func (t *Tester) Send(ctx context.Context, who keys.Who) ([]Result, error)

Send checks that who may send a test (tenant admin), sends it through every channel and appends one audit entry with the result per channel – never a secret (password, HMAC key, webhook path or query); the error texts may name hosts, file paths and recipient addresses. A refusal is audited as denied; with no channel there is nothing to send and nothing to audit. The audit entry is written after the send (it holds the result), so an audit failure is reported although the message went out.

type WaitError

type WaitError struct{ RetryAfter time.Duration }

WaitError refuses a test that comes too soon after the previous one.

func (*WaitError) Error

func (e *WaitError) Error() string

type Webhook

type Webhook struct {
	URL        string
	SecretFile string
	CAFile     string
	UserAgent  string
}

Webhook posts a digest as JSON.

func NewWebhook

func NewWebhook(c config.Webhook, userAgent string) *Webhook

NewWebhook builds the channel from notify.webhook.

func (*Webhook) Name

func (w *Webhook) Name() string

Name is the channel in the notifications queue.

func (*Webhook) Send

func (w *Webhook) Send(ctx context.Context, d Digest) error

Send posts one digest; any 2xx answer is success. Errors name the host, never the full URL – its path or query may hold a token.

Jump to

Keyboard shortcuts

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