Help us shape the future of Go! Take the 2026 Go Developer Survey

ownernotify

package
v0.54.17 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package ownernotify delivers owner notifications from the server a StackKit is installed on to the channels the owner declared in the StackSpec notification intent.

The ResolvedPlan carries only a secret-free projection of each channel (name, type, destination hash, secret references). Delivery reads the destination from the canonical StackSpec on the same node, recomputes the projection and refuses a channel whose destination no longer matches the verified plan. Credentials resolve through local custody at send time and never enter a receipt, a log line or an error message.

Index

Constants

View Source
const (
	// EventBackupFailed reports a backup that cannot currently protect data.
	EventBackupFailed = "backup-failed"
	// EventRestoreVerificationOverdue reports a missing or too old staged
	// restore proof while snapshots are otherwise healthy.
	EventRestoreVerificationOverdue = "backup-restore-verification-overdue"
	// EventBackupDigest is the opt-in "all is well" backup summary.
	EventBackupDigest = "backup-digest"
)
View Source
const (
	// EventApplyFailed reports an Apply that stopped before the runtime converged.
	EventApplyFailed = "apply-failed"
	// EventUpgradeAvailable reports a newer StackKit release the owner can adopt.
	EventUpgradeAvailable = "upgrade-available"
	// EventDriftDetected reports a runtime that no longer matches the applied plan.
	EventDriftDetected = "drift-detected"
	// EventCertificateExpiring reports a certificate that will expire soon.
	EventCertificateExpiring = "certificate-expiring"
)
View Source
const (
	// StatusAPIVersion identifies the `notifications` projection of status.
	StatusAPIVersion = "stackkit.owner-notification-status/v1"
	// ReceiptAPIVersion identifies one delivery receipt under .stackkit/runs.
	ReceiptAPIVersion = "stackkit.owner-notification-receipt/v1"

	StateNotConfigured = "not-configured"
	StateConfigured    = "configured"

	// EventVerify is the delivery check a Verify or an explicit test sends; it
	// is always admitted because it proves the channel rather than reporting a
	// lifecycle event.
	EventVerify = "delivery-check"
)

Variables

View Source
var ErrNotConfigured = errors.New("owner notifications: not configured; declare spec.notifications with at least one owner channel (ntfy, email, gotify, webhook, slack, discord or telegram)")

ErrNotConfigured reports that the verified plan declares no owner channel.

LifecycleEvents are the events ComposeLifecycleEvent can write.

Functions

func ChannelDeliveryBound

func ChannelDeliveryBound(channelType string) bool

ChannelDeliveryBound reports whether a channel type has a local delivery adapter in this package.

func IsLifecycleEvent

func IsLifecycleEvent(event string) bool

IsLifecycleEvent reports whether event is one ComposeLifecycleEvent writes.

func SpecChannels

func SpecChannels(canonicalStackSpec []byte) (map[string]SpecChannel, error)

SpecChannels returns the declared channels of a canonical StackSpec keyed by name, each paired with its recomputed plan projection.

func WriteReceipt

func WriteReceipt(workspace string, receipt Receipt) error

WriteReceipt stores one owner-only receipt under .stackkit/runs.

Types

type BackupDigestInput

type BackupDigestInput struct {
	// Configuration and Schedule are the binding states `stackkit status`
	// reports (not-configured, bound, enabled, stale, unavailable, ...).
	Configuration string
	Schedule      string
	// RepositoryReady is false when the repository status could not be read
	// or reports not ready; RepositoryProblem names why without a location.
	RepositoryReady   bool
	RepositoryProblem string
	// Snapshot and StagedRestore are evidence states (recorded, unverified,
	// clock-skew) with their age when recorded.
	SnapshotState         string
	SnapshotAgeSeconds    *int64
	StagedRestoreState    string
	StagedRestoreAge      *int64
	MaxRestoreEvidenceAge time.Duration
	// ObjectivesBreached and ObjectivesUnverified count recovery objectives
	// whose data-loss check is breached or unverified.
	ObjectivesBreached   int
	ObjectivesUnverified int
	// Attention carries the exact remedy commands status names.
	Attention []string
}

BackupDigestInput carries the facts the backup digest reports. It holds states and ages only: no repository location, path or credential.

type ChannelStatus

type ChannelStatus struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Delivery string `json:"delivery"`
}

ChannelStatus is the status view of one channel; it names no destination.

type LifecycleInput

type LifecycleInput struct {
	Event string
	// Subject names what the event is about (a service, version or certificate).
	Subject string
	// Facts are the short evidence lines of the message.
	Facts []string
	// Remedy is the exact command the owner can run next.
	Remedy string
}

LifecycleInput carries the facts of one lifecycle event. Facts are short states, counts, versions or subject names the caller owns; they are cleaned to one line and never carry a path, location or credential.

type Message

type Message struct {
	Event    string
	Title    string
	Body     string
	Priority int
	Tags     []string
}

Message is one owner notification. Body text must not carry secrets; callers build it from evidence summaries only.

func ComposeBackupDigest

func ComposeBackupDigest(input BackupDigestInput) Message

ComposeBackupDigest selects the event and writes the owner message for the backup digest recipe. A failure outranks an overdue restore proof, which outranks the plain digest, so the owner's event allowlist decides whether a healthy week produces a message at all.

func ComposeLifecycleEvent

func ComposeLifecycleEvent(input LifecycleInput) (Message, bool)

ComposeLifecycleEvent writes the owner message of a lifecycle event. It reports ok=false for an event it does not know.

type PlanChannel

type PlanChannel struct {
	Name            string   `json:"name"`
	Type            string   `json:"type"`
	DestinationHash string   `json:"destinationHash"`
	SecretRefs      []string `json:"secretRefs"`
	Delivery        string   `json:"delivery"`
}

PlanChannel is the secret-free plan projection of one channel.

type PlanIntent

type PlanIntent struct {
	APIVersion string        `json:"apiVersion"`
	Channels   []PlanChannel `json:"channels"`
	Events     []string      `json:"events"`
}

PlanIntent is the plan projection of the owner's notification intent.

func PlanIntentFromPlan

func PlanIntentFromPlan(plan resolvedplan.ResolvedPlan) (*PlanIntent, error)

PlanIntentFromPlan decodes the notification projection of a verified plan. It returns nil when the plan declares no channel.

type Receipt

type Receipt struct {
	APIVersion      string `json:"apiVersion"`
	Event           string `json:"event"`
	Channel         string `json:"channel"`
	Type            string `json:"type"`
	DestinationHash string `json:"destinationHash"`
	PlanHash        string `json:"planHash"`
	Result          string `json:"result"`
	Reason          string `json:"reason,omitempty"`
	AttemptedAt     string `json:"attemptedAt"`
}

Receipt records one delivery attempt. It never carries the destination, the credential or the message body.

func LatestReceipt

func LatestReceipt(workspace string) *Receipt

LatestReceipt returns the newest readable receipt, or nil.

type SecretResolver

type SecretResolver func(ref string) ([]byte, error)

SecretResolver returns the material behind one secret reference from local custody.

type Sender

type Sender struct {
	HTTPClient *http.Client
	// SendMail overrides SMTP delivery in tests.
	SendMail func(addr string, auth smtp.Auth, from string, to []string, msg []byte) error
	Now      func() time.Time
}

Sender delivers notifications. The zero value uses net/http and net/smtp.

func (Sender) Deliver

func (s Sender) Deliver(
	ctx context.Context,
	workspace, planHash string,
	intent *PlanIntent,
	specChannels map[string]SpecChannel,
	resolve SecretResolver,
	message Message,
	channelFilter string,
) ([]Receipt, error)

Deliver sends message to every plan channel that admits its event and writes one receipt per attempt. channelFilter, when set, limits delivery to that channel name. It returns the receipts and an error when any admitted channel failed.

type SpecChannel

type SpecChannel struct {
	Name            string
	Type            string
	DestinationHash string
	Destination     map[string]any
}

SpecChannel is one declared channel with its destination as authored.

type Status

type Status struct {
	APIVersion string          `json:"apiVersion"`
	State      string          `json:"state"`
	Channels   []ChannelStatus `json:"channels,omitempty"`
	Events     []string        `json:"events,omitempty"`
	// LastDelivery is the newest local delivery receipt, when one exists.
	LastDelivery *Receipt `json:"lastDelivery,omitempty"`
	Attention    []string `json:"attention,omitempty"`
}

Status is the additive `notifications` projection of `stackkit status`.

func BuildStatus

func BuildStatus(intent *PlanIntent, workspace string) *Status

BuildStatus renders the status projection. It never fails: an unreadable receipt directory only omits the last delivery.

Jump to

Keyboard shortcuts

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