bvandeusenandClaude Opus 5.5 58bfc7c361
CI / lint (push) Successful in 2s
CI / integration (push) Failing after 45s
CI / unit (push) Successful in 1m7s
CI / publish (push) Skipped
feat(alerts): one evaluation path; host-agent ingest fires rules (#5060)
#5060 "One evaluation path: record_metrics batch API; host-agent ingest
evaluates rules".

- record_metrics(session, rows) writes metric rows and evaluates the rules
  that watch them, reading rules, states and maintenance windows once per
  batch. Each rule sees its key's samples oldest first, so an agent catching
  up after an outage counts consecutive checks as they happened.
- Host-agent ingest now goes through it. Before, it wrote plugin_metrics
  directly and no host_agent rule could ever fire. The catalog gains the
  per-mount disk_used_pct ("<host>:<mount>").
- MetricBatch gathers a transaction's samples; the docker ingest, traefik,
  unifi, snmp, monitor and self-monitor writers record one batch each instead
  of a rule query per sample.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 12:53:40 -04:00

Steward

A self-hosted network monitoring and infrastructure management hub for home servers. Steward gives you a single pane of glass over your hosts, services, and automation — with live-updating dashboards, alerting, and Ansible integration.


What It Does

  • Ping monitoring — TCP/ICMP probes with live latency history and configurable thresholds
  • DNS monitoring — resolution checks with optional expected-IP validation
  • Ansible — browse playbooks, trigger runs, stream output live
  • Alerting — threshold-based rules against any monitored metric, with email and webhook (Discord-compatible) notifications
  • Plugins — extend with additional data sources; Traefik metrics included out of the box
  • Dashboard widgets — all monitors and plugins contribute live-updating widgets

No JavaScript framework. No build step. No external workers or message brokers.


Quick Start — Docker

cp .env.example .env
# Set STEWARD_DATABASE_URL in .env

docker compose up -d

Open http://localhost:5000. On first run you'll be prompted to create the admin account.

This compose file builds the image locally and bind-mounts the source for live editing. To run a persistent instance off the published CI image instead, see Remote dev instance below.


Remote dev instance

For a long-lived instance on a remote server that tracks the dev CI line, use compose.deploy.yml. It pulls the published image (git.fabledsword.com/bvandeusen/steward:dev) rather than building, runs no source bind-mounts, and persists data in named volumes.

One-time prerequisite — registry push token (on the Git host). CI publishes the image, so the FabledSteward repo needs a push credential. The injected GITHUB_TOKEN lacks write:package, so create a Forgejo token scoped read:package + write:package and add it as the repo Actions secret REGISTRY_TOKEN. Until this exists, the publish CI job 401s and no :dev image is produced.

Standup (on the server):

# 1. Authenticate to the registry (read:package token is enough to pull)
docker login git.fabledsword.com -u <user>

# 2. Configure secrets
cp .env.example .env
# Set POSTGRES_PASSWORD. Leave STEWARD_SECRET_KEY unset to let the app
# generate + persist one on the /data volume.

# 3. Pull + boot
docker compose -f compose.deploy.yml up -d

Open http://<server>:5000 and create the admin account on first run.

Update to the latest dev build:

docker compose -f compose.deploy.yml pull
docker compose -f compose.deploy.yml up -d

Every push to dev that goes CI-green publishes a fresh :dev (and an immutable :<commit-sha> for rollback). To pin a specific commit, change the image: tag in compose.deploy.yml to :<sha>.


Quick Start — Bare Metal

Requires Python 3.11+ and PostgreSQL.

python -m venv .venv && source .venv/bin/activate
pip install -e .

cp config.example.yaml config.yaml
# Edit config.yaml — set database.url at minimum

steward --host 0.0.0.0 --port 5000

ICMP ping requires CAP_NET_RAW or the setuid ping binary. TCP mode (the default) needs no elevated privileges.


Configuration

Only two things must be set to run the app:

What How
Database URL STEWARD_DATABASE_URL env var or database.url in config.yaml
Secret key Auto-generated on first run and saved to /data/secret.key

Everything else — SMTP, webhooks, monitor intervals, Ansible sources, plugin settings — is configured through the web UI at /settings/.


Plugins

Drop a directory into plugins/ and enable it via the Settings UI. The Traefik plugin is included:

plugins/
└── traefik/      ← included; enable in Settings

See docs/plugins/ for a full plugin development guide.


Documentation

Document Contents
docs/architecture.md How the app works: startup sequence, routing, scheduler, DB pattern
docs/core/configuration.md Bootstrap config, DB-backed settings, all setting keys and defaults
docs/core/monitors.md Ping and DNS monitors: probe logic, metrics emitted, data models
docs/core/alerting.md Alert rules, state machine, notification channels, template variables
docs/core/ansible.md Playbook sources, run lifecycle, SSE streaming
docs/plugins/overview.md Plugin system: how loading works, plugin.yaml schema, required exports
docs/plugins/writing-a-plugin.md Step-by-step plugin development guide
docs/plugins/traefik.md Traefik plugin: config, metrics, alert rule examples
docs/reference/code-map.md Where every key function, model, and route lives in the codebase
S
Description
No description provided
Readme
6.1 MiB
Languages
Python 69.8%
HTML 29.8%
Jinja 0.2%
Dockerfile 0.1%