channels

package
v0.0.0-...-b97db65 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package channels provides the inbound channel gateway for multi-channel messaging.

It is distinct from core/pkg/connectors/slack/ which is the outbound effect connector. This package handles inbound message normalization, envelope validation, session routing, and receipt generation for messages arriving from Slack, Telegram, Lark, WhatsApp, and Signal.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ValidChannelKind

func ValidChannelKind(kind ChannelKind) bool

ValidChannelKind returns true if kind is one of the recognised channel constants.

func ValidateEnvelope

func ValidateEnvelope(env ChannelEnvelope) error

ValidateEnvelope validates a ChannelEnvelope for structural correctness. It is fail-closed: any missing or implausible field returns an error.

Types

type Adapter

type Adapter interface {
	// Kind returns the ChannelKind this adapter handles.
	Kind() ChannelKind
	// NormalizeInbound parses a raw inbound wire payload and returns a ChannelEnvelope.
	// The returned envelope must satisfy ValidateEnvelope.
	NormalizeInbound(ctx context.Context, raw []byte) (ChannelEnvelope, error)
	// Send delivers an outbound message to the given tenant session.
	Send(ctx context.Context, tenantID string, sessionID string, body OutboundMessage) error
	// Health returns nil when the adapter's downstream dependencies are reachable.
	Health(ctx context.Context) error
}

Adapter is the interface that every channel integration must implement. Adapters handle both inbound normalisation and outbound delivery for a single platform.

type AdapterRegistry

type AdapterRegistry struct {
	// contains filtered or unexported fields
}

AdapterRegistry is a thread-safe registry of channel adapters keyed by ChannelKind. Each ChannelKind may have at most one registered adapter.

func NewAdapterRegistry

func NewAdapterRegistry() *AdapterRegistry

NewAdapterRegistry returns an empty AdapterRegistry.

func (*AdapterRegistry) Get

func (r *AdapterRegistry) Get(kind ChannelKind) (Adapter, error)

Get returns the adapter registered for the given ChannelKind. It returns an error when no adapter is registered for that kind.

func (*AdapterRegistry) List

func (r *AdapterRegistry) List() []ChannelKind

List returns the ChannelKinds of all registered adapters in undefined order.

func (*AdapterRegistry) Register

func (r *AdapterRegistry) Register(adapter Adapter) error

Register adds an adapter to the registry. It returns an error if an adapter for the same ChannelKind is already registered, or if the adapter is nil.

type AntiSpoofResult

type AntiSpoofResult struct {
	// Passed is true when all checks passed and the envelope is considered legitimate.
	Passed bool `json:"passed"`
	// SenderTrust is the trust class assigned after the check.
	// When Passed is false this will be SenderTrustSuspicious.
	SenderTrust SenderTrustClass `json:"sender_trust"`
	// Reason is a human-readable explanation of the result.
	Reason string `json:"reason"`
}

AntiSpoofResult is the outcome of an anti-spoofing validation check.

type AntiSpoofValidator

type AntiSpoofValidator interface {
	// Validate checks the envelope for spoofing indicators.
	// It always returns a non-nil *AntiSpoofResult; err is non-nil only on internal failure.
	Validate(ctx context.Context, env ChannelEnvelope) (*AntiSpoofResult, error)
}

AntiSpoofValidator validates incoming channel envelopes for spoofing indicators.

type ChannelAttachmentRef

type ChannelAttachmentRef struct {
	ArtifactID   string `json:"artifact_id"`
	MediaType    string `json:"media_type"`
	ContentHash  string `json:"content_hash"`
	PayloadClass string `json:"payload_class"`
}

ChannelAttachmentRef is a reference to an attachment artifact. The actual payload is stored in the artifact store; this struct is a content-addressed pointer.

type ChannelEnvelope

type ChannelEnvelope struct {
	// EnvelopeID is a unique identifier generated by the receiving adapter.
	EnvelopeID string `json:"envelope_id"`
	// Channel identifies the platform this message arrived on.
	Channel ChannelKind `json:"channel"`
	// TenantID scopes the envelope to a specific HELM tenant.
	TenantID string `json:"tenant_id"`
	// SessionID is the HELM session this message belongs to.
	SessionID string `json:"session_id"`
	// MessageID is the platform-native message identifier.
	MessageID string `json:"message_id"`
	// ThreadID is the optional platform-native thread or conversation identifier.
	ThreadID string `json:"thread_id,omitempty"`
	// SenderID is the platform-native identifier of the sender.
	SenderID string `json:"sender_id"`
	// SenderHandle is the optional human-readable sender name or username.
	SenderHandle string `json:"sender_handle,omitempty"`
	// SenderTrust is the trust classification assigned to this sender.
	SenderTrust SenderTrustClass `json:"sender_trust"`
	// IdentityBindingRef links to the identity binding record in the identity store.
	IdentityBindingRef string `json:"identity_binding_ref,omitempty"`
	// ReceivedAtUnixMs is the UTC timestamp in milliseconds when the gateway received the message.
	ReceivedAtUnixMs int64 `json:"received_at_unix_ms"`
	// Text is the plain-text body of the message.
	Text string `json:"text,omitempty"`
	// Attachments holds references to attached artifacts.
	Attachments []ChannelAttachmentRef `json:"attachments,omitempty"`
	// Metadata carries channel-specific key/value pairs that do not fit the standard fields.
	Metadata map[string]string `json:"metadata,omitempty"`
	// SignatureRef links to the cryptographic signature of the original wire payload.
	SignatureRef string `json:"signature_ref,omitempty"`
}

ChannelEnvelope is the normalised, channel-agnostic representation of an inbound message. All adapter implementations must map their raw wire format into this struct before passing the message further into the HELM pipeline.

type ChannelKind

type ChannelKind string

ChannelKind identifies the messaging platform a message arrived on.

const (
	ChannelSlack    ChannelKind = "slack"
	ChannelTelegram ChannelKind = "telegram"
	ChannelLark     ChannelKind = "lark"
	ChannelWhatsApp ChannelKind = "whatsapp"
	ChannelSignal   ChannelKind = "signal"
)

type ChannelReceipt

type ChannelReceipt struct {
	// ReceiptID is a unique identifier for this receipt.
	ReceiptID string `json:"receipt_id"`
	// EnvelopeID links back to the ChannelEnvelope for inbound receipts.
	// For outbound receipts this field holds a generated reference.
	EnvelopeID string `json:"envelope_id"`
	// Channel identifies the platform this receipt relates to.
	Channel ChannelKind `json:"channel"`
	// Direction is "inbound" or "outbound".
	Direction string `json:"direction"`
	// TenantID scopes the receipt to a HELM tenant.
	TenantID string `json:"tenant_id"`
	// SessionID identifies the HELM session this message belongs to.
	SessionID string `json:"session_id"`
	// ProcessedAtMs is the UTC timestamp in milliseconds when the receipt was generated.
	ProcessedAtMs int64 `json:"processed_at_unix_ms"`
	// ContentHash is the SHA-256 hex digest of the message content.
	ContentHash string `json:"content_hash"`
}

ChannelReceipt is an immutable record of a processed channel message. Receipts are generated for both inbound and outbound messages, providing an audit trail that links channel activity back to HELM sessions and tenants.

func NewInboundReceipt

func NewInboundReceipt(env ChannelEnvelope) *ChannelReceipt

NewInboundReceipt builds a ChannelReceipt from a processed inbound ChannelEnvelope.

func NewOutboundReceipt

func NewOutboundReceipt(tenantID, sessionID string, channel ChannelKind, msg OutboundMessage) *ChannelReceipt

NewOutboundReceipt builds a ChannelReceipt for a message sent via a channel adapter.

type ChannelSignatureVerifier

type ChannelSignatureVerifier interface {
	// Verify checks the signature on the given envelope.
	// It returns nil if the signature is valid, or an error describing the failure.
	Verify(env ChannelEnvelope) error
}

ChannelSignatureVerifier verifies the cryptographic signature of an inbound channel envelope. Each channel platform has a different signature scheme; implementations of this interface encapsulate the platform-specific logic.

func NewSignatureVerifier

func NewSignatureVerifier(kind ChannelKind, secrets SignatureSecrets) ChannelSignatureVerifier

NewSignatureVerifier returns a ChannelSignatureVerifier for the given channel kind using the provided secrets. If the secret for the given channel is empty, a verifier that requires the signature metadata key to be absent is returned (i.e. it accepts envelopes that have no signature claim but rejects envelopes that claim a signature when no secret is available to verify it).

type DefaultAntiSpoofValidator

type DefaultAntiSpoofValidator struct {
	// contains filtered or unexported fields
}

DefaultAntiSpoofValidator performs basic envelope integrity checks. It is intentionally conservative: any suspicious signal causes the envelope to fail.

func NewAntiSpoofValidator

func NewAntiSpoofValidator() *DefaultAntiSpoofValidator

NewAntiSpoofValidator returns a DefaultAntiSpoofValidator with no signature secrets. Signature verification is skipped when secrets are not configured.

func NewAntiSpoofValidatorWithSecrets

func NewAntiSpoofValidatorWithSecrets(secrets SignatureSecrets) *DefaultAntiSpoofValidator

NewAntiSpoofValidatorWithSecrets returns a validator configured with channel signing secrets. When secrets are provided, the validator will verify HMAC/token signatures for each channel.

func (*DefaultAntiSpoofValidator) Validate

Validate performs structural and temporal anti-spoofing checks on env.

Checks performed:

  1. EnvelopeID is non-empty.
  2. SenderID is non-empty.
  3. ReceivedAtUnixMs is plausible (not in the future, not too old).
  4. Channel-specific signature/HMAC checks.

type DefaultRouter

type DefaultRouter struct {
	// contains filtered or unexported fields
}

DefaultRouter routes inbound envelopes based on the tenant + sender + channel combination. It maintains an in-memory session map and creates new sessions on first contact. For production deployments the session map should be backed by a persistent store.

func NewRouter

func NewRouter() *DefaultRouter

NewRouter returns a DefaultRouter with an empty session map.

func (*DefaultRouter) CreateSession

func (r *DefaultRouter) CreateSession(_ context.Context, tenantID string, channel ChannelKind) (string, error)

CreateSession explicitly allocates a new session for the given tenant and channel. It returns an error when tenantID is empty or channel is invalid.

func (*DefaultRouter) Route

Route resolves the SessionRoute for the given envelope. If no existing session matches the tenant+sender+channel combination a new session is created. The envelope must have a non-empty TenantID and SenderID.

type OutboundMessage

type OutboundMessage struct {
	// Text is the plain-text body of the outbound message.
	Text string `json:"text"`
	// ThreadID is the optional platform-native thread or conversation identifier.
	// When non-empty the adapter must deliver the message into that thread.
	ThreadID string `json:"thread_id,omitempty"`
	// Attachments holds artifact IDs to include in the outbound message.
	Attachments []string `json:"attachments,omitempty"`
	// RequireAck indicates that the caller requires a delivery acknowledgement.
	RequireAck bool `json:"require_ack"`
}

OutboundMessage is the normalised representation of a message to be sent via a channel adapter.

type Router

type Router interface {
	// Route resolves the SessionRoute for an inbound envelope.
	// Implementations may create a new session when no existing session matches.
	Route(ctx context.Context, env ChannelEnvelope) (*SessionRoute, error)
	// CreateSession creates a new session for the given tenant and channel,
	// returning the new session ID.
	CreateSession(ctx context.Context, tenantID string, channel ChannelKind) (string, error)
}

Router maps inbound envelopes to HELM sessions, creating new sessions as required.

type SenderTrustClass

type SenderTrustClass string

SenderTrustClass classifies the trust level assigned to a message sender.

const (
	// SenderTrustVerified indicates the sender identity has been cryptographically verified.
	SenderTrustVerified SenderTrustClass = "verified"
	// SenderTrustKnownLow indicates the sender is known but has reduced trust.
	SenderTrustKnownLow SenderTrustClass = "known_low"
	// SenderTrustUnknown indicates the sender identity is unverified.
	SenderTrustUnknown SenderTrustClass = "unknown"
	// SenderTrustSuspicious indicates the sender exhibits suspicious behaviour.
	SenderTrustSuspicious SenderTrustClass = "suspicious"
)

type SessionRoute

type SessionRoute struct {
	TenantID  string
	SessionID string
	Channel   ChannelKind
}

SessionRoute describes the HELM session that an inbound envelope should be delivered to.

type SignatureSecrets

type SignatureSecrets struct {
	// SlackSigningSecret is the Slack app signing secret (used for X-Slack-Signature HMAC).
	SlackSigningSecret string
	// TelegramBotToken is the Telegram bot token (used for webhook verification).
	TelegramBotToken string
	// LarkVerificationToken is the Lark app verification token.
	LarkVerificationToken string
	// WhatsAppAppSecret is the WhatsApp Business API app secret.
	WhatsAppAppSecret string
	// SignalServerCert is the Signal webhook certificate fingerprint (SHA-256 hex).
	SignalServerCert string
}

SignatureSecrets holds the per-channel secrets needed for signature verification. Only the channels that are configured need non-empty values.

Directories

Path Synopsis
Package lark provides the inbound channel adapter for Lark (Feishu) messaging.
Package lark provides the inbound channel adapter for Lark (Feishu) messaging.
Package slack provides the inbound channel adapter for Slack messaging.
Package slack provides the inbound channel adapter for Slack messaging.
Package telegram provides the inbound channel adapter for Telegram messaging.
Package telegram provides the inbound channel adapter for Telegram messaging.

Jump to

Keyboard shortcuts

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