Documentation
¶
Overview ¶
Package notify delivers completed investigations to chat (Slack, Matrix).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Format ¶
func Format(inv providers.Investigation) string
Format renders an Investigation as a concise markdown-ish message used by all notifiers.
Invariant: every literal this function emits (labels, separators, bullets) avoids the three mrkdwn-meta chars & < >. The Slack fallback is escapeMrkdwn(Format(inv)) — only untrusted content (evidence, summaries) may carry those chars, so escaping leaves the scaffolding intact. Use · • and *bold*; TestFormatScaffoldingHasNoMrkdwnMeta guards it.
func FormatProgress ¶ added in v0.3.0
func FormatProgress(up providers.ProgressUpdate) string
FormatProgress renders an interim progress update as a concise plain-text status line, shared by notifiers (Slack fallback; Matrix/webhook later). The fields are untrusted (title + model interim text), so a mrkdwn-parsing notifier escapes the composed output before sending.
func Register ¶ added in v0.2.0
func Register(d Descriptor)
Register adds a notifier descriptor to the registry. Panics on duplicate names.
Types ¶
type Descriptor ¶ added in v0.2.0
type Descriptor struct {
Name string
Build func(Deps) (providers.Notifier, error) // returns nil when unconfigured (disabled)
}
Descriptor describes a self-registering notifier.
type FeedbackSink ¶ added in v0.8.0
FeedbackSink records a human 👍/👎 rating on a delivered investigation (implemented by *outcome.Ledger — the same method the Slack path records through, so dedup, trust weighting and the recurrence-cooldown re-arm are shared by construction).
type MatchedPayload ¶ added in v0.10.0
type MatchedPayload struct {
Path string `json:"path,omitempty"`
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
Score float64 `json:"score,omitempty"`
}
MatchedPayload mirrors providers.MatchedEntry for delivery consumers: the pre-existing KB entry this investigation's kb_search matched at clear-match strength (distinct from prior, which reports recurrence).
type Matrix ¶
type Matrix struct {
// contains filtered or unexported fields
}
Matrix delivers via the Matrix client-server send API.
func NewMatrix ¶
NewMatrix builds a Matrix notifier. homeserver is the base URL (e.g. https://matrix.org); roomID is like "!abc:hs"; token is an access token.
The txn counter is seeded from the wall clock (UnixNano) rather than 0 so that transaction ids keep increasing across process restarts. Homeservers dedupe by (access_token, txnId); a fresh process starting back at "runlore-1" could otherwise collide with a pre-restart id and have its message silently dropped. Caveat: a backwards wall-clock jump across a restart could still collide — an acceptable residual given the dedup window is minutes and the prior behaviour offered zero protection.
func (*Matrix) Deliver ¶
Deliver sends the formatted investigation as an m.notice message. It carries both a plaintext body (the fallback Matrix renders literally) and a rich formatted_body (org.matrix.custom.html) so the message's mrkdwn renders as bold/links/code instead of leaking raw *asterisks* to Matrix clients.
type MatrixFeedback ¶ added in v0.8.0
type MatrixFeedback struct {
// contains filtered or unexported fields
}
MatrixFeedback is the opt-in reaction listener (notify.matrix.feedback_reactions): it long-polls the homeserver's /sync for m.reaction events in the configured room and records 👍/👎 on RunLore's own investigation messages into the outcome ledger.
Unlike the Slack feedback path, NOTHING is exposed: /sync is an outbound HTTPS long-poll authenticated by the notifier's access token — no inbound endpoint, no signing secret, no NetworkPolicy change. The trade is a long-lived poll loop, which runs leader-only (started in startWork, cancelled with the leadership context) so an HA deployment records each reaction once.
The first /sync response is a position handshake only: historical reactions are deliberately skipped, so feedback counts from startup onward — replaying a room's history through the ledger on every restart would re-stamp old votes with fresh timestamps.
func NewMatrixFeedback ¶ added in v0.8.0
func NewMatrixFeedback(homeserver, roomID, token string, sink FeedbackSink, log *slog.Logger) *MatrixFeedback
NewMatrixFeedback builds the reaction listener. homeserver/roomID/token are the notifier's own settings; sink is where ratings land (the outcome ledger).
func (*MatrixFeedback) Run ¶ added in v0.8.0
func (f *MatrixFeedback) Run(ctx context.Context)
Run long-polls /sync until ctx is cancelled (leadership loss / shutdown). Errors are logged and retried after a fixed backoff — a flaky homeserver must never crash the agent; at worst feedback pauses.
type Multi ¶
type Multi struct {
// contains filtered or unexported fields
}
Multi delivers to several notifiers, best-effort: a failing notifier is logged, not propagated, so one bad sink doesn't block the others.
func BuildEnabled ¶ added in v0.2.0
BuildEnabled builds every registered notifier whose Build returns non-nil, in deterministic (name-sorted) order, and wraps them in a Multi.
func (*Multi) Deliver ¶
Deliver fans out to every notifier (best-effort: one bad sink never blocks the others), logs each failure, and returns the joined errors so the caller can tell delivery was incomplete. Returns nil when all sinks succeed.
func (*Multi) DeliverProgress ¶ added in v0.3.0
DeliverProgress fans an interim progress ping out to every wrapped notifier that implements ProgressNotifier (the type-assert capability check), skipping those that don't (Matrix/webhook may no-op for now). It is best-effort by contract: a failing sink is logged and swallowed, never propagated — a progress ping must never fail an investigation. Returns nil always.
type Payload ¶ added in v0.10.0
type Payload struct {
Title string `json:"title"`
Confidence float64 `json:"confidence"`
Namespace string `json:"namespace,omitempty"`
Resource string `json:"resource,omitempty"`
CuratedURL string `json:"curated_url,omitempty"`
Text string `json:"text"`
Verdict string `json:"verdict,omitempty"`
Severity string `json:"severity,omitempty"`
Cluster string `json:"cluster,omitempty"`
Environment string `json:"environment,omitempty"`
Tenant string `json:"tenant,omitempty"`
AlertName string `json:"alert_name,omitempty"`
StartedAt string `json:"started_at,omitempty"` // RFC3339; "" when unknown
Occurrences int `json:"occurrences,omitempty"`
PrevCuratedURL string `json:"prev_curated_url,omitempty"`
RuledOut []string `json:"ruled_out,omitempty"`
DataGaps []string `json:"data_gaps,omitempty"`
Prior *PriorPayload `json:"prior,omitempty"`
MatchedKnowledge *MatchedPayload `json:"matched_knowledge,omitempty"`
}
Payload is the exported delivery payload: the single definition of what an outbound notification carries. The webhook notifier marshals it as-is; the templated notifier exposes it as the template dot. Field set and json tags are the webhook notifier's original wire format — do not change tags without a compatibility note, external consumers parse them.
func NewPayload ¶ added in v0.10.0
func NewPayload(inv providers.Investigation) Payload
NewPayload maps an (already-redacted) Investigation to the delivery payload. Matched knowledge is surfaced only when Prior is nil so the structured field never disagrees with the rendered Text (Prior already covers "seen before").
type PriorPayload ¶ added in v0.10.0
type PriorPayload struct {
Cause string `json:"cause,omitempty"`
Resolution string `json:"resolution,omitempty"`
EntryPath string `json:"entry_path,omitempty"`
Recalls int `json:"recalls,omitempty"`
Resolved int `json:"resolved,omitempty"`
}
PriorPayload mirrors providers.PriorKnowledge for delivery consumers: what the merged KB entry said last time this incident fired.
type Slack ¶
type Slack struct {
// FeedbackButtons (opt-in, notify.slack.feedback_buttons) appends 👍/👎 buttons
// so the on-call can rate the diagnosis; clicks land in the outcome ledger via
// the exposed /slack/interactions endpoint.
FeedbackButtons bool
// contains filtered or unexported fields
}
Slack delivers via a Slack incoming webhook.
func (*Slack) Deliver ¶
Deliver posts the formatted investigation to the webhook. When an action carries an ApprovalID, it renders interactive Approve/Reject buttons (Block Kit).
func (*Slack) DeliverProgress ¶ added in v0.3.0
DeliverProgress posts an interim progress ping to the webhook (ProgressNotifier).
type SlackBot ¶
type SlackBot struct {
// FeedbackButtons — see Slack.FeedbackButtons; on the bot path the buttons sit
// on the channel summary message, never on the detail thread reply.
FeedbackButtons bool
// contains filtered or unexported fields
}
SlackBot delivers via the Slack Web API (chat.postMessage) using a bot token, for workspaces that provision a bot app instead of an incoming webhook. Unlike a webhook, chat.postMessage targets an explicit channel and returns HTTP 200 with {"ok":false,"error":...} on logical failures (e.g. not_in_channel).
func NewSlackBot ¶
NewSlackBot builds a bot-token Slack notifier posting to channel (ID or name).
func (*SlackBot) Deliver ¶
Deliver posts the compact summary to the channel, then the full analysis as a thread reply so the channel stays a scannable triage feed. The summary IS the notification; the detail reply is secondary, so a failed thread post returns a wrapped error that records the summary already landed — Multi logs it without implying the alert went undelivered. Nothing is threaded when the summary post yields no ts (empty-body path) or the investigation has no detail beyond it.
func (*SlackBot) DeliverProgress ¶ added in v0.3.0
DeliverProgress posts an interim progress ping to the channel (ProgressNotifier).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package templated is a config-only generic notifier: it renders a user-supplied Go text/template over notify.Payload and POSTs the result to any endpoint (Teams, Discord, ntfy, incident.io, …).
|
Package templated is a config-only generic notifier: it renders a user-supplied Go text/template over notify.Payload and POSTs the result to any endpoint (Teams, Discord, ntfy, incident.io, …). |
|
Package webhook is a generic outgoing-webhook notifier: it POSTs each investigation's findings as JSON to an operator-configured URL.
|
Package webhook is a generic outgoing-webhook notifier: it POSTs each investigation's findings as JSON to an operator-configured URL. |