Documentation
¶
Overview ¶
Package webhook delivers outbound notifications to operator-configured URLs (used by alerts and the daily digest). Two delivery contracts: HMAC-signed JSON for generic endpoints, and Slack's {"text": ...} shape for Slack incoming webhooks (which reject anything else). Persisted store, best-effort async delivery.
Index ¶
Constants ¶
const ( // FormatSlack — {"text": "<plain-text rendering>"}. Also matches Mattermost and Rocket.Chat, // both of which implement Slack's incoming-webhook contract deliberately. FormatSlack = "slack" // FormatDiscord — {"content": ...}. Discord 400s on {"text": ...}, so a Discord URL pasted // into the webhook box was rejected on every single delivery, forever, silently: the send is // fire-and-forget, so the failure went nowhere. Discord is where this ICP actually is, which // made it the most expensive missing line in the file. FormatDiscord = "discord" )
Chat formats. Each is a receiver that will NOT accept our signed-JSON contract: it wants its own tiny envelope and rejects anything else, so an endpoint on one of these hosts gets the human rendering and no signature (there is nowhere to put one).
Variables ¶
This section is empty.
Functions ¶
func Send ¶
Send POSTs one delivery to an endpoint and returns the HTTP status the endpoint answered with (0 when no response arrived). Slack-format endpoints receive {"text": text} — Slack rejects any other body shape and cannot verify signature headers — while every other endpoint keeps the signed-JSON contract unchanged: the body verbatim plus X-Smolanalytics-Signature. text is the plain-text rendering of body; if a caller passes none, the raw JSON body is used as the text so a Slack message still carries the facts instead of failing.
func SendTest ¶ added in v0.8.0
SendTest fires a synthetic delivery through the exact path real alerts and digests take (same format rules, signing, SSRF guard, and HTTP client), so a 2xx here means real deliveries will land. Returns the endpoint's HTTP status. Shared by POST /v1/webhooks/{id}/test and the MCP test_webhook tool.
Types ¶
type Endpoint ¶
type Endpoint struct {
ID string `json:"id"`
Name string `json:"name"`
URL string `json:"url"`
Secret string `json:"secret"` // signs the payload so the receiver can verify
Format string `json:"format,omitempty"`
Enabled bool `json:"enabled"`
Created time.Time `json:"created"`
}
Endpoint is one registered webhook target.
func (Endpoint) Chat ¶ added in v0.60.0
Chat reports whether this endpoint takes a chat envelope rather than signed JSON.
func (Endpoint) DiscordFormat ¶ added in v0.60.0
DiscordFormat reports whether deliveries use Discord's {"content": ...} contract, either because the endpoint says so or because the URL is plainly a Discord webhook — the host check also covers endpoints persisted before this format existed, which is every one of them.
func (Endpoint) SlackFormat ¶ added in v0.8.0
SlackFormat reports whether deliveries to e use Slack's {"text": ...} contract: either the endpoint was created with format "slack", or the URL is a Slack incoming webhook (hooks.slack.com) — the host check also covers endpoints persisted before the format field existed.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func (*Store) Add ¶
Add registers a new endpoint. format is "" (auto-detect: Slack contract for hooks.slack.com URLs, signed JSON for everything else) or "slack" to force the Slack text contract for Slack-compatible receivers on other hosts (Mattermost, Rocket.Chat, …).
func (*Store) Delete ¶
Delete removes an endpoint by id. found is true only when an endpoint actually went away, so callers never claim a removal that did not occur. A miss is not an error.
func (*Store) DeliverAll ¶
DeliverAll fires the payload to every enabled endpoint, async + best-effort. text is the plain-text rendering that Slack-format endpoints receive.