Files
homelab-monitor/CLAUDE.md
T
jhodgkin 9891751d37
CI / web (push) Successful in 17s
CI / api (push) Successful in 22s
docs: update CLAUDE.md for device discovery
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 20:50:50 -06:00

4.0 KiB

Homelab Monitor — Project Memory

Unified health dashboard for the homelab (infra docs: jhodgkin/homelab repo, docs/infrastructure.md). One login instead of logging into Proxmox, Zabbix, OMV, and every service separately.

How to resume work in a new session

  1. List open issues: GET /api/v1/repos/jhodgkin/homelab-monitor/issues?state=open&limit=30 on git.jerodrigged.com (same pattern as the main homelab backlog, see global ~/.claude/CLAUDE.md). Milestone v1-dashboard is the current focus; everything else is backlog.
  2. Pick an issue, comment that you're starting it, do the work, comment what you did, close it.
  3. Commit and push after every meaningful step — don't let work sit uncommitted.

Architecture

  • apps/api — Fastify + TypeScript. Polls collectors on an interval (POLL_INTERVAL_SECONDS, default 30s), writes snapshots to SQLite (apps/api/src/db), serves /api/*.
  • apps/web — React + Vite + TypeScript. Polls /api/hosts every 15s, renders host/container cards.
  • config/hosts.yaml — declares what to monitor (no secrets, committed). Credentials live in .env (gitignored) — see .env.example for the full list.
  • Collectors implement the Collector interface (apps/api/src/collectors/types.ts) and are registered in apps/api/src/index.ts. Adding a new data source = new collector + config entry, no other wiring.
    • ProxmoxCollector (done): one API call to pve returns CPU/mem/disk and PSI pressure (pressurecpusome, pressurememoryfull) for the host + every LXC. Covers ~22 of ~24 machines.
    • SshHostCollector (done): covers .180 (omv) and .171 (ripper), the bare-metal boxes Proxmox can't see. Dedicated SSH key with a forced command= in the remote authorized_keys — the server always runs a fixed read-only script regardless of what's exec'd, so a leaked key is still contained. See docs/ssh-collector-key-setup.md, including the gotcha that diskPaths in hosts.yaml must be kept in sync by hand with the remote script's DISK_<label>= lines.
    • Everything else (Zabbix alerts) is filed as backlog issues, not yet implemented.
  • Auth: local (bcrypt + signed session cookie) is live. AUTH_MODE=oidc is a stubbed config value only — Authentik wiring is issue #12, not implemented.
  • Device discovery (done, separate from the collector system above — it's inventory, not per-host metrics): scripts/discover-devices.sh runs via systemd timer on the CT122 host, not in Docker (real ARP entries live in the host's network namespace, not Docker's bridge network). API reads the resulting JSON each poll cycle. Full reasoning in docs/device-discovery.md.

Infrastructure this project owns

  • Gitea repo: jhodgkin/homelab-monitor
  • Proxmox API token: monitor@pve!dashboard, role PVEAuditor (read-only), created via pveum on pve. Secret lives only in CT122's .env.
  • Deployment: CT122 (homelab-monitor, 192.168.1.103), unprivileged LXC on pve, Docker + Compose. SSH alias homelab-monitor in ~/.ssh/config (root, key-based). Not yet exposed externally — see issue #13 (needs NPM proxy host + Cloudflare tunnel route).
  • SSH collector key: monitor_ed25519 keypair lives only at /opt/homelab-monitor/ssh/ on CT122 (gitignored, not in the repo). Public key installed on omv and ripper with a forced command — see docs/ssh-collector-key-setup.md.
  • Vaultwarden: dashboard admin credentials saved as "Homelab Monitor (dashboard admin)".

Known gaps / explicitly deferred (see issues for detail)

  • No historical charts yet — only the latest snapshot is shown (24h retention in SQLite unused by the UI so far).
  • No Zabbix/Graylog integration yet — this dashboard doesn't duplicate their alerting, just complements it later.
  • Public exposure and Authentik OIDC both need either credentials from the user or manual dashboard steps (Cloudflare Zero Trust, NPM admin UI, Authentik admin) — flagged in the relevant issues, not blocking.