enodia-sentinal/Makefile
Luna c00fff224c Add read-only web dashboard and phone push notifications
Both zero-dependency (stdlib http.server + urllib), consistent with the
project's no-dependency-tree stance for a security daemon.

Web dashboard (enodia_sentinel/web.py + static/dashboard.html):
- read-only JSON API over the log dir: /api/status, /api/alerts,
  /api/alerts/<id>, /api/events
- bearer-token auth (constant-time; header or ?token=), required on non-
  loopback binds, auto-generated + persisted (0600) when unset
- binds the host's Tailscale IP by default (auto-detected), reachable from the
  tailnet but not the LAN/internet
- self-contained dark SPA: severity cards, live alert list, full snapshot
  viewer; 10s auto-refresh
- path-traversal-safe alert lookup; `enodia-sentinel web` subcommand; daemon
  now writes a pidfile so the dashboard can show live status
- hardened enodia-sentinel-web.service (read-only, no caps)

Phone push (enodia_sentinel/notify/):
- pluggable backends — ntfy, Pushover, generic webhook — each separating a pure
  build() (unit-tested, no network) from send()
- a backend turns on when its config keys are set; pushes gated by
  notify_min_severity; severity → per-service priority/tags
- fired from snapshot.capture on worker threads, errors swallowed
- desktop notify-send retained

Tests: +16 (9 web incl. a real-server 401/200 auth test, 7 notify request-build
cases). 55/55 pass. Live end-to-end verified: daemon → alert → API → page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 16:18:00 -07:00

68 lines
2.3 KiB
Makefile

PREFIX ?= /usr/local
BINDIR := $(PREFIX)/bin
LIBDIR := $(PREFIX)/lib/enodia-sentinel
SYSTEMDDIR := /etc/systemd/system
CONFDIR := /etc
LOGDIR := /var/log/enodia-sentinel
.PHONY: install uninstall enable disable status logs check baseline drill test clean
# Installs the stdlib-only Python package as a plain directory + launcher
# wrapper (no pip, no virtualenv, no site-packages). Zero runtime deps.
install:
install -d $(DESTDIR)$(LIBDIR)
cp -r enodia_sentinel $(DESTDIR)$(LIBDIR)/
install -Dm755 packaging/enodia-sentinel.wrapper $(DESTDIR)$(BINDIR)/enodia-sentinel
install -Dm755 src/sentinel-redteam $(DESTDIR)$(BINDIR)/sentinel-redteam
install -Dm644 systemd/enodia-sentinel.service $(DESTDIR)$(SYSTEMDDIR)/enodia-sentinel.service
install -Dm644 systemd/enodia-sentinel-web.service $(DESTDIR)$(SYSTEMDDIR)/enodia-sentinel-web.service
@if [ ! -e "$(DESTDIR)$(CONFDIR)/enodia-sentinel.toml" ]; then \
install -Dm644 config/enodia-sentinel.toml $(DESTDIR)$(CONFDIR)/enodia-sentinel.toml; \
echo "Installed default config at $(CONFDIR)/enodia-sentinel.toml"; \
else \
install -Dm644 config/enodia-sentinel.toml $(DESTDIR)$(CONFDIR)/enodia-sentinel.toml.new; \
echo "Existing config preserved; new template at $(CONFDIR)/enodia-sentinel.toml.new"; \
fi
install -dm750 $(DESTDIR)$(LOGDIR)
@echo "Installed. Run 'sudo make enable' to start the service."
uninstall:
rm -rf $(DESTDIR)$(LIBDIR)
rm -f $(DESTDIR)$(BINDIR)/enodia-sentinel
rm -f $(DESTDIR)$(BINDIR)/sentinel-redteam
rm -f $(DESTDIR)$(SYSTEMDDIR)/enodia-sentinel.service
rm -f $(DESTDIR)$(SYSTEMDDIR)/enodia-sentinel-web.service
rm -f $(DESTDIR)$(CONFDIR)/enodia-sentinel.toml.new
@echo "Uninstalled. Config and logs preserved."
enable:
systemctl daemon-reload
systemctl enable --now enodia-sentinel.service
disable:
systemctl disable --now enodia-sentinel.service
status:
systemctl status enodia-sentinel.service --no-pager
logs:
journalctl -u enodia-sentinel.service -f
# Dev targets — run from the repo without installing.
test:
python3 -m unittest discover -s tests -v
check:
python3 -m enodia_sentinel.cli check
baseline:
python3 -m enodia_sentinel.cli baseline
web:
python3 -m enodia_sentinel.cli web
drill:
./src/sentinel-redteam
clean:
find . -type d -name __pycache__ -prune -exec rm -rf {} +