Run it anywhere

The daemon

moonwayd on its own — every flag, headless fleet watching, webhook alerts, and where the journal lives.

Everything Moonway computes happens in one process: moonwayd opens watch streams, keeps the cache, runs the engines, writes the journal, and serves a token-guarded API on 127.0.0.1. The desktop app manages a daemon for you — but the binary stands alone, and running it yourself unlocks the shapes the app doesn't need: a fleet watcher on a server, alerts into a webhook, custom rules, Postgres.

Headless fleet watching

moonwayd --watch staging.example,prod-a.example --db sqlite:$HOME/.moonway/fleet.db --webhook https://ntfy.example/oncall

--watch starts watching the named kubeconfig contexts from boot — no UI, no clicks. Paired with --db the journal records around the clock, and --webhook turns critical Triage findings into pushes. The Fleet page in the app shows every watched cluster's health at a glance.

Webhook alerts

--webhook POSTs JSON on the same edge-triggered discipline the desktop notifications use: once when a critical finding appears, once when it resolves, silence while a flapping condition bounces inside the 30-minute dedupe window.

{
  "cluster": "prod-a.example",
  "item": {
    "id": "crash|payments/api-7f9c…",
    "sev": "crit",
    "cat": "crashloop",
    "kind": "Pod",
    "ns": "payments",
    "name": "api-7f9c…",
    "title": "CrashLoopBackOff",
    "count": 4
  },
  "at": 1754700000,
  "resolved": false
}

ntfy, a Slack webhook, or twenty lines of your own HTTP handler all work — anything that accepts a POST.

Flags

FlagDefaultWhat it does
--port5171Port to listen on; 0 picks a random one
--host127.0.0.1Bind address — 0.0.0.0 only in-cluster
--token$MOONWAY_TOKEN, else randomAuth token; a random one is printed at boot
--token-fileRead the token from a file, creating it with a random value if missing
--dbin-memoryPersistence: sqlite:<path> or postgres://…; empty means the journal dies with the process
--retention-days14Journal retention; 0 disables pruning
--watchComma-separated contexts to watch from boot (fleet mode)
--webhookPOST critical alerts to this URL
--rulesDirectory of custom CEL rules (*.yaml)
--watch-scopeallGeneric-resource watching: all (eager) or lazy (on-demand, idle-reaped)
--in-clusteroffUse the pod service account instead of a kubeconfig
--uioffServe the embedded web UI (implied by --in-cluster)
--logstderrWrite the log to a file, size-capped and rotated
--log-max-mb16Size cap for --log, per generation; two generations kept

Two flags deserve a second look. --watch-scope lazy defers watching custom-resource types until something asks for them and reaps idle watches — the right trade on clusters with hundreds of CRDs. And --db defaults to memory when you run the daemon by hand: if you want the journal to outlive the process — you do — say so with sqlite: or postgres://.

The token

Every request carries the token; without it the API answers nothing. Set it explicitly, or let the daemon mint one — it prints MOONWAYD_TOKEN <value> at boot. --token-file is how the login-item daemon and the desktop app agree on a credential without a channel: first writer creates it, both read it.