Senders are outbound notification adapters. Channels and providers use them to push alerts to email, log files, consoles, or complex routing pipelines — all without touching core code.
Senders are fire-and-forget output adapters that sit alongside the channel and provider subsystems. Any channel or provider calls senders.send(id, configName, message) and the sender subsystem takes care of delivery.
Each sender can hold multiple named configurations, so a single email-smtp sender can deliver to several different inboxes simultaneously, each identified by a config name. Senders are installed and configured from the Manage Senders dialog in the Kwirth UI without writing any code.
Leaf senders — deliver to an external destination
Writes colorized, formatted lines to stdout / stderr. Includes optional ISO timestamps, level tags, and a configurable prefix.
Appends formatted log lines to a file. Supports line-count-based rotation — when the limit is reached, the file is archived and a new one starts.
Sends transactional email via the Resend API. Requires a Resend account and API key. No SMTP configuration needed.
Sends email via any SMTP server. Supports TLS (port 465), STARTTLS (port 587), and plain (unauthenticated relay). Compatible with Gmail, Outlook, Postfix, and others.
Posts colour-coded message cards to a Teams channel via an incoming webhook. Card colour follows the message severity level (red = error, orange = warning, blue = info, gray = debug).
Routing senders — intercept, filter, and dispatch
Fans a single message out to multiple target senders simultaneously. All targets receive the same unmodified message in parallel. If one target fails the rest are unaffected.
Evaluates an ordered list of regex rules against each message. First match wins — the rule can drop the message or forward it to a specific sender. Messages that match no rule follow the default action.
A visual pipeline designer in the Kwirth UI. Combine tee and regex nodes into a tree-based routing graph. No JSON editing needed — the designer generates the config.
Gates messages by time-of-day and day-of-week. Define windows (e.g. Mon–Fri 09:00–18:00) that forward to a specific sender, and silence everything outside. Timezone-aware.
Because senders receive the full ISenderAccess facade at startup, a sender can delegate to other senders. This is how tee, regex, composite, and timed work: they each intercept a message and decide which downstream sender (if any) should receive it.
You can chain them: a timed sender can forward to a regex sender, which can forward to a tee sender, which fans out to email-smtp and file.
Open the menu drawer and choose Manage Senders (requires cluster-scope access key). From there you can:
.tgz uploadSenders with a custom UI (composite, timed) open a dedicated editor instead of the generic config form when you expand their card.
import { ISender, ISenderAccess, ISenderConfig, ISenderFieldDef, ISenderMessage }
from '@kwirthmagnify/kwirth-common-back'
export class SlackSender implements ISender {
readonly id = 'slack'
private configs = new Map<string, { name: string; webhookUrl: string }>()
getConfigSchema(): ISenderFieldDef[] {
return [
{ name: 'name', label: 'Name', required: true },
{ name: 'webhookUrl', label: 'Webhook URL', required: true },
]
}
async send(configName: string, message: ISenderMessage): Promise {
const cfg = this.configs.get(configName)
if (!cfg) throw new Error(`config '${configName}' not found`)
await fetch(cfg.webhookUrl, {
method: 'POST',
body: JSON.stringify({ text: message.body }),
headers: { 'Content-Type': 'application/json' }
})
}
addConfig(c: ISenderConfig) { this.configs.set(c.name, c as any) }
removeConfig(n: string) { this.configs.delete(n) }
hasConfig(n: string) { return this.configs.has(n) }
getConfigNames() { return [...this.configs.keys()] }
async startSender(_: ISenderAccess): Promise {}
async stopSender(): Promise {}
}
export default SlackSender Full ISender interface reference, config schema docs, and packaging guide in the Kwirth docs.
Sender docs → Browse sender sources