← Back to home

Kwirth Senders

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.

How senders work

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.

Channel / Provider
senders.send('email-smtp', 'alerts', { body: '...' })
SenderManager
consolestdout / stderr
filerotating log file
email-smtpSMTP inbox
teamsMS Teams channel (webhook)
teefans out to multiple senders
regexroutes / drops based on pattern rules
compositefull visual routing pipeline
timedtime-window gating (business hours, etc.)

Built-in senders

Leaf senders — deliver to an external destination

🖥️
console

Console

Writes colorized, formatted lines to stdout / stderr. Includes optional ISO timestamps, level tags, and a configurable prefix.

prefixtimestampslevels
📄
file

File

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.

filePathtimestampslevelsmaxLines
✉️
email-resend

Email — Resend

Sends transactional email via the Resend API. Requires a Resend account and API key. No SMTP configuration needed.

apiKeyfromtosubject
📧
email-smtp

Email — SMTP

Sends email via any SMTP server. Supports TLS (port 465), STARTTLS (port 587), and plain (unauthenticated relay). Compatible with Gmail, Outlook, Postfix, and others.

hostportencryptionuserpassfromto
💬
teams

Microsoft Teams

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).

webhookUrltitle

Routing senders — intercept, filter, and dispatch

🔀
tee

Tee

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.

targets[]
🧵
regex

Regex Filter

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.

rules[]defaultAction
🗺️
composite

Composite Pipeline

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.

pipeline tree
🎨 Visual editor
🕔
timed

Timed Gate

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.

rules[]timezonedefaultAction
🎨 Rule editor UI

The dispatcher pattern

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.

Managing senders from the UI

Open the menu drawer and choose Manage Senders (requires cluster-scope access key). From there you can:

Senders with a custom UI (composite, timed) open a dedicated editor instead of the generic config form when you expand their card.

Build your own sender

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

Ready to build a sender?

Full ISender interface reference, config schema docs, and packaging guide in the Kwirth docs.

Sender docs → Browse sender sources