Jobs, audit & notifications¶
What this is¶
Three related systems — do not confuse them:
| System | Purpose | Think of it as… |
|---|---|---|
| Jobs | Queue + live progress of work units | What is running right now? |
| Audit | Immutable history (who / what / when / client IP / snippet) | Who did that last Tuesday? |
| Notifications | Dismissible inbox (updates pending, failed backup, …) | What still needs my eyes? |
Open notifications via the bell icon (no separate Alerts nav link).
Why they exist¶
Long SSH work must not block the browser (jobs). Homelab and multi-operator setups need accountability (audit). Noise should not bury failures (notifications + optional Web Push). Together they replace “did that cron fire?” shell archaeology.
End-to-end: follow one backup through all three¶
- Start a backup from a server Backups page.
- Jobs → row goes
pending→running→success/failed; open detail for log tail. - Audit → filter that server / backup actions; see request → queued → running → complete phases and client IP.
- If it failed earlier, a notification may open; on success, related open alerts can auto-resolve (optional push:
Resolved: …). - Dismiss the inbox item when you have acted.
Jobs¶
Where: nav Jobs (/jobs) · compact panel on each server detail.
Job types (examples)¶
| Type | Typical trigger | Runner |
|---|---|---|
backup | Manual or backup cron | Celery |
os_patch / container_patch | Manual or apply schedule | Web background |
os_update_check / container_update_check | Manual or check schedule | Web background |
docker_stack_check / docker_stack_deploy | Stack ⋯ Check updates / Deploy | Web background |
docker_stack_stop / _start / _restart | Project ⋯ Stop/Start/Restart all | Web background |
service_migrate | Docker Move to another host… (flag PIHERDER_SERVICE_MIGRATE) | Web background (not Celery). Do not recycle web mid-job. JobHold stays until Close. Copy / dest-up fail: Start source stack. Celery Move is a later candidate (v1.5) |
template_deploy / template_redeploy | Catalog template confirm / Save & redeploy | Web background |
template_drift_check | Deployment Check drift (live log) | Web background |
retention | Per-server backup file retention | As configured |
stale_data_cleanup | Opt-in Jobs / Audit / nmap-run purge | Scheduler or Settings → Run now |
nmap_discover / nmap_inventory / nmap_detailed / nmap_host_deep | LAN Discovery scans | celery-worker-nmap (-Q nmap) |
nmap_vuln_db_update | Download / refresh vuln pack | nmap worker |
herder_backup | PiHerder self-backup | As configured |
Statuses: pending → running → success / failed.
Web restart: OS/container patch and other web-process jobs (not Celery backups / nmap) that are still pending or running are failed on startup. They cannot still be executing after uvicorn exits; leaving them blocked new exclusive work on that host. Bulk Upgrade OS runs on a shared patch thread pool (several hosts at once), not one sequential BackgroundTasks chain.
Exclusive jobs (one per type per host)¶
These types do not stack on the same server while already pending or running:
os_patch,container_patchos_update_check,container_update_check- Stack lifecycle + template deploy/redeploy (shared stack mutation lane on the host)
service_migrate— exclusive with backup and stack mutation on both source and desttemplate_drift_check(one drift job at a time per host; not a stack write)
A second start reuses the existing job (UI follows it; REST 409 with already_active / existing job). Backups use a separate rule: per-host Redis mutex + Celery (see Multi-worker). Move a service also refuses a migrate while either host is busy.
Why exclusive: two apt upgrades at once on one host is a recipe for lock conflicts and opaque failure.
Fleet Jobs UI¶
- Ops hero at the top: dual-line pulse (running / queue / ok / fail + type chips) and app timezone caption
- Filters: server, status, type, date range with 7d / 30d / 90d / Clear presets, per-page 10 / 20 / 50 / 100 (cookie
ph_per_pageshared with Servers / Docker / discovery) - Date presets use the Settings timezone calendar day (not the browser’s local midnight)
- Active only — pending + running
- Row → detail modal (summary, log tail, scheduled flag)
- Cancel works from list and modal (where applicable)
- Link to Audit for historical trail
Live progress¶
JobHold / progress modals poll status and log lines for OS/container patch, nmap scans (including vuln DB update), and similar work. If a job was already active, the modal notes that and tracks the existing job_id. When a job finishes, the modal stays open with a done banner (no forced full-page reload) so you can read the log.
Stale data cleanup is configured under Settings → General — separate from per-server backup retention jobs.
Bulk fleet queue¶
From the Servers list, multi-select hosts and queue the same job type across eligible servers (feature flags apply). See Updates & patching — Bulk actions.
Audit¶
Where: nav Audit (/audit). Also from:
- Server detail footer: All logs, Backup logs, Docker logs, OS audit logs (filtered by host)
- Notifications → Audit log
- Jobs panel links
Actors may be:
- Session user (display name + email)
- API token name + id (automation)
- system / scheduler for cron jobs
Client IP (v0.5.0): every request-driven audit row stores the source IP.
| Path | What you see |
|---|---|
Via Caddy (:8888 / :8443) | True browser/API client (X-Forwarded-For / X-Real-IP overwritten by Caddy) |
Direct app :8000 | TCP peer (often a Docker network IP) — production should not expose this |
| Scheduler / cron only | — (no HTTP request) |
| Job finish (Celery etc.) | IP snapshotted when the job was queued |
Also audited with IP: login / login failed / 2FA, and API token create/update/rotate/revoke. Free-text search matches IPs. Detail modal shows IP.
Filter by user, server, token, action, status, date range (same 7d / 30d / 90d presets as Jobs — app timezone), or free-text (includes IP). Per-page is the same 10 / 20 / 50 / 100 cookie as Jobs.
The Audit page uses the same ops-hero pattern: status bars, top action-type chips (as filter links), and the active timezone in the subtitle. On dense filter layouts the pulse can be collapsed (Hide pulse) so the filter row stays primary; detail rows open a branded detail modal (summary + snippet).
Console command transcripts (v1.3)¶
When Settings → Console → Command audit is on, each finished web shell can attach a command timeline (Fernet-encrypted, not in Audit search). Open the ssh_console_close event: operator+ can expand the timeline, open /audit/console/{id}, or download .txt. Viewers see counts only (“transcript hidden”). Demo never stores bodies. Retention drops the ciphertext and keeps the fact that a transcript existed. See Web SSH console.
Backup lifecycle events¶
Each backup job writes append-only phases:
| Phase | Action | Meaning |
|---|---|---|
| request | backup_request | User / schedule / bulk asked for a backup |
| queued | backup_queued | Waiting for a Celery worker |
| running | backup_running | Worker started rsync |
| complete | backup | Terminal success or failure |
Completed backups show a summary line with source count and total size (e.g. 2 sources · 1.5 MB), duration, and a detail modal with per-source sizes. Incomplete/running noise can be hidden with Hide incomplete runs.
Timezone display¶
All event times are stored in UTC and rendered in the app timezone from Settings → timezone (e.g. Africa/Johannesburg / SAST). The Audit header shows the active zone. Jobs and Notifications use the same rule.
Notifications¶
- Bell icon → open / dismiss
- Deep links into the relevant server or page
- Optional Web Push for new open notifications — PWA & Web Push
- When an alert auto-resolves (e.g. backup succeeds, updates clear), a push may fire as
Resolved: …using the same type preference as the original alert - Dismiss is idempotent if already closed
Why an inbox separate from Audit: Audit is forever; the inbox is a short “todo” list for open problems.
Job, nmap-run, and console-open history is aggregated on Reports (backups, OS patches, LAN live, Docker deploys, console sessions).
API¶
Automation can list/trigger jobs with Bearer tokens — API tokens.