Architecture¶
flowchart TB
Browser["Browser (HTMX + Alpine)"] -->|HTTPS| Caddy
Caddy --> FastAPI["FastAPI (web)"]
subgraph Core["Docker Compose (supported)"]
FastAPI --> DB[(PostgreSQL)]
FastAPI --> Scheduler["APScheduler"]
FastAPI --> Celery["Celery worker(s)"]
end
Scheduler -->|backup cron| Celery
Scheduler -->|patch/check cron| FastAPI
Celery -->|reads/writes| DB
Celery -->|SSH + rsync| PiFleet["Remote fleet"]
FastAPI -->|SSH · apt · docker| PiFleet
FastAPI -->|DB reads for UI| DB
FastAPI -.->|Job.details progress| Celery Job execution paths¶
| Work | Runs on | Concurrency rule |
|---|---|---|
| Backups | Celery | Parallel across hosts; one backup per host (Redis mutex) |
| OS/container patch & update checks | Web (BackgroundTasks / thread pools) | One active job of that type per host |
| Bulk fleet actions | Web → same enqueue paths | Feature-flag skip + exclusive rules |
Key modules (pointers)¶
| Concern | Location |
|---|---|
| Roles / middleware | app/security/auth.py |
| Password policy | app/services/password_policy.py |
| Jobs / progress / exclusive types | app/services/jobs/ (service.py; package preserves patch.object surface) |
| Docker unused cleanup HTML | app/services/docker_unused_html.py |
| Per-server backup lock | app/services/server_job_lock.py |
| Scheduler | app/services/scheduler.py |
| Backup | app/services/backup.py (+ progress, profiles) |
| Docker inventory | app/services/docker_inventory.py |
| Templates | app/services/service_templates/ |
| Integrations (domain) | app/services/integrations/ |
| Integrations (HTTP) | integrations.py + integrations_common / _kuma / _grafana / _pihole / _npm |
| Templates (HTTP) | templates_common + templates_svc (catalog) + templates_deploy |
| Auth (HTTP) | auth.py + auth_users.py (admin users) |
| Network maps | app/services/dns_fabric/ (core, mesh_physical, mesh_logical) · app/routers/dns.py |
| Ops-hero pulse helpers | app/services/ops_pulse.py |
| Push | app/services/push.py |
| API tokens | app/services/api_tokens.py, app/routers/api_v1.py |
| Herder backup | app/services/herder_backup.py |
| Metrics | app/services/metrics.py |
| Bulk server actions | app/routers/servers.py (POST /servers/bulk) |
| Server SSH / patch sub-routers | server_ssh.py, server_patch.py, server_common.py (mounted under /servers) |
| Docker UI | server_docker.py + server_docker_compose.py (editor/versions) |
| Theme / map / ops CSS | themes.css, fabric.css, ops.css, ops-auth.css, ops-pages.css |
| Map client | app/static/js/fabric-mesh.js (PiHerderFabric.refreshLayout on orient) |
| App timezone display | app/services/app_settings.describe_timezone · Settings General card |
| Large templates | Prefer partials/ — e.g. server_detail_*_modals, docker_modals, settings_{tab} |
Frontend stack¶
- Server-rendered Jinja2 + HTMX fragments + Alpine for small widgets
- Vendored Tailwind / HTMX / Alpine (no runtime CDN)
- Progressive enhancement vanilla JS for Network maps, job hold, push, compose editor
- Shared ops-hero grid contract (
ops.css): full main content width; desktop title left · viz right (≥768px); mobile viz under title - Mobile orientation reflow in
base.html; service rows stack actions on narrow viewports - Auth pages (login/register + force-password / 2FA) use shared
auth-stagechrome - Empty DB → first register is admin; no default password user; then registration closes
- Session cookies set
SecurewhenPIHERDER_PUBLIC_URLishttps://(orCOOKIE_SECURE=true)
Design principles¶
- Privileged actions audited (incl. client IP)
- Secrets encrypted at rest; decrypt only in memory for jobs
- Offline/air-gapped ready once built (vendored assets)
- External/dangerous actions opt-in: preview → confirm → audit
- One exclusive OS/container job type per host (no silent double SSH)
- Thin routers where practical; domain logic in
app/services/ - Compose-first deployment; DB-first operational settings
- Integrations optional — core fleet never depends on Kuma/Grafana/NPM/Pi-hole