Daemons are headless backend workers that run continuously inside Kwirth without requiring a user session. Same processing power as plugins — no browser tab needed.
A Kwirth daemon is a backend-only extension that runs autonomously alongside the Kwirth core. Unlike plugins, daemons have no frontend component and no WebSocket session — they start when Kwirth starts and keep running regardless of who is connected.
Daemons are ideal for continuous background processing: log filtering, alerting, data forwarding, or any task that should run 24/7 without user interaction.
| Plugin | Daemon | |
|---|---|---|
| UI | Yes — React tab in the front end | None — headless |
| Triggered by | User opening a tab | Kwirth startup / API call |
| Session | Tied to a WebSocket connection | Independent, survives reconnects |
| Use case | Interactive exploration | Continuous background processing |
| Providers | Yes | Yes |
| Senders | Yes | Yes |
| Storage | Yes | Yes |
Headless version of the Censor plugin. Watches selected containers, batches log lines, calls an LLM to learn noise regex patterns, and applies them continuously — forwarding only meaningful lines to the configured sender. Runs without any browser tab open.
AvailableAny continuous background task: log archival, metric aggregation, business-event correlation, compliance checks. The daemon interface gives you full access to cluster state, providers, senders, and storage.
Build your ownEvery daemon must implement the IDaemon interface from @kwirthmagnify/kwirth-common-back:
"censor")A daemon is a single CJS bundle compiled with esbuild. Project layout mirrors the plugin and provider structure:
daemons/my-daemon/
src/
back/index.ts ← implements IDaemon, exports as default
build.mjs ← esbuild script (node platform, CJS format)
package.json ← includes id, name, version, descriptionMinimal scaffold:
import { IDaemon, IBackDaemonObject, IDaemonInstanceConfig, BackDaemonData }
from '@kwirthmagnify/kwirth-common-back'
export class MyDaemon implements IDaemon {
readonly daemonId = 'my-daemon'
constructor(private clusterInfo: unknown, private bdo: IBackDaemonObject) {}
getDaemonData(): BackDaemonData {
return { id: 'my-daemon' }
}
async startDaemon(): Promise {
this.bdo.logInfo?.('[my-daemon] started')
// subscribe to providers, load config from storage, etc.
}
containsInstance(id: string): boolean { return false }
containsAsset(id: string, ns: string, pod: string, c: string): boolean { return false }
async addObject(cfg: IDaemonInstanceConfig, ns: string, pod: string, c: string): Promise {
// start watching this container
return true
}
async deleteObject(cfg: IDaemonInstanceConfig, ns: string, pod: string, c: string): Promise {
return true
}
async processCommand(instanceId: string, command: string, data?: unknown): Promise {
return null
}
}
export default MyDaemon Register it for local development in kwirth-dev.json:
{
"daemons": {
"my-daemon": "../daemons/my-daemon/dist"
}
}Kwirth watches dist/back.js and hot-reloads the daemon whenever it changes.
Full IDaemon interface reference, lifecycle documentation, and the Censor daemon source are in the Kwirth docs.
Daemon docs → Censor daemon source