Class: Insika::Channels::Webhook

Inherits:
Object
  • Object
show all
Defined in:
lib/insika/channels/webhook.rb

Overview

A webhook as a Shape B "channel": the recipient of operator ALERTS (WS6). One instance per configured URL, registered in the ChannelRegistry so ChannelDelivery's outbox+claim+retry pipeline delivers the alert the same way it delivers a chat answer — at-most-once, bounded retry, boot sweep. Deliberately NOT Slack/CRM/anything: it POSTs the event as JSON and the consumer interprets it (the engine transports, it does not integrate).

The target URL is operator configuration, so the POST crosses the SAME egress guard the Relay applies: https-only (fails closed), private/ loopback/metadata targets and DNS-rebindable hosts blocked. Without it the alerts.webhook URL is an SSRF vector — a URL pointed at cloud metadata or an internal API exfiltrates alert events out of the boundary (WS6).

Instance Method Summary collapse

Constructor Details

#initialize(url, http:, allow_http: false, allow_private: false) ⇒ Webhook

Returns a new instance of Webhook.



20
21
22
23
24
# File 'lib/insika/channels/webhook.rb', line 20

def initialize(url, http:, allow_http: false, allow_private: false)
  @http = http
  @allow_http = allow_http
  @allow_private = allow_private
end

Instance Method Details

#deliver(payload, to:, delivery_id: nil) ⇒ Object

The ChannelDelivery contract: -> HTTP status (200..299 = delivered). Every failure becomes a DeliveryError so the bounded retry records it.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/insika/channels/webhook.rb', line 28

def deliver(payload, to:, delivery_id: nil)
  if (reason = egress_violation(to))
    raise Insika::DeliveryError, "webhook egress blocked for #{to}: #{reason}"
  end

  result = @http.request(
    method: :post, url: to,
    headers: { "content-type" => "application/json" },
    body: JSON.generate(payload)
  )
  result[:status].to_i
rescue Insika::DeliveryError
  raise
rescue StandardError => e
  raise Insika::DeliveryError, "webhook: #{e.message}"
end