# How-To: `pos system` Host care: encrypted backups, firewall, health dashboard, and uninstall. Tools: `backup`, `firewall`, `health`, `uninstall`. | Tool | What it does | |------|--------------| | `pos system health` | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker) | | `pos system backup` | gpg-encrypted (AES-256) folder snapshots | | `pos system firewall` | Interactive UFW ("UFW POWER") management | | `pos system uninstall` | Safe, interactive uninstaller for the pos toolkit | --- ## `pos system health` — host health dashboard ```bash pos system health # console report; exits 1 if any check FAILs ``` Checks: disk per mount (>90% = FAIL), RAM/swap, failed systemd units, backup age, fail2ban, docker containers. Header shows hostname, uptime, load, public IP. Health is a **console-only reporter — it never sends notifications**; deliver its output with a wrapper or a scheduled job (below). `--help` prints the **effective** config values (env > `system.env` > default), e.g.: ``` Environment (effective values): HEALTH_BACKUP_MAX_AGE_DAYS 2 BACKUP_SERVICE_ROOTS /srv $HOME/srv ``` (`$HOME` is resolved at runtime — on this host that is `/srv /home/unknown/srv`.) ### Configuration ```bash # ~/.config/linux_post_install/system.env (comment-only defaults — uncomment to override) BACKUP_SERVICE_ROOTS=/srv $HOME/srv # roots for backup-age check + backup --service BACKUP_USB_ROOT=/mnt/usb # optional: copy finished backups to /backups/ (auto-detects a mounted USB when unset) HEALTH_BACKUP_MAX_AGE_DAYS=3 # WARN if newest backup older (default 2) ``` ### Daily digest (automated) Run the health report on a timer with a scheduled job (no systemd unit needed): ```bash pos system schedule config # add a job: INTERVAL=daily, NOTIFY=always, # COMMAND=pos system health systemctl --user list-timers | grep pos-schedule pos system schedule run # run once now ``` The `NOTIFY=always` policy sends the job's full output — i.e. the dashboard — as the alert. The old `pos-health.{service,timer}` systemd units are gone — a legacy install may still have them failed/leftover; disable and remove them: ```bash sudo systemctl disable --now pos-health.timer pos-health.service 2>/dev/null sudo rm -f /etc/systemd/system/pos-health.{service,timer} && sudo systemctl daemon-reload ``` **Recipes:** - Watch the backup age without email: add the daily digest job; if the backup check turns WARN you'll see it in the morning report. - Exit code in a cron/scheduled check: `pos system health >/dev/null 2>&1 || notify_send "health FAIL"`. **Troubleshooting:** - `[WARN] fail2ban installed but not running` → expected unless you have it active; start it (`sudo systemctl enable --now fail2ban`) or ignore. - `[FAIL] services: nbd-server.service …` → a failed unit; inspect with `systemctl status `. --- ## `pos system backup` — encrypted folder snapshots ```bash pos system backup # encrypt to ./_.tar.gz.gpg pos system backup --no-encrypt # plain ./_.tar.gz, no password pos system backup --service # pick a folder from /srv + ~/srv ``` Uses `sudo tar` + gpg AES-256. The password is prompted **twice and never stored**; the artifact is `chmod 600`. On success (and on failure, via ERR trap) a `notify_send` alert is sent. **Skip encryption** with `--no-encrypt` (or `BACKUP_ENCRYPT=0` in `system.env`): the archive stays a plain `.tar.gz`, no password is prompted, and the file is still `chmod 600` + USB-copy verified. This is the **headless/cron-safe** mode — the encrypted path prompts for a password, so under cron it needs `--no-encrypt` with a fixed folder (`pos system backup ~/Documents --no-encrypt`). `--service` lists folders under the roots in `BACKUP_SERVICE_ROOTS` (default `/srv $HOME/srv`; override via `system.env` or env) and lets you pick. ### Copy to a USB stick After the archive verifies, connected USB storage is **detected** (so a stick plugged in while the backup was running is found — if none is mounted you get one chance to plug one in and re-check) and you're asked whether to copy the backup there. The copy lands in `/backups/` and is **verified 100%** (sha256 source vs copy) before any success is announced: ```bash pos system backup ~/Documents # ... after the archive verifies: # [!] No USB storage detected # Plug a USB drive in now and press Enter to re-check (or 's' to skip): # [+] Copying to /media/you/USB-DISK/backups/docs_2026-08-13.tar.gz.gpg ... # OK Transfer verified 100% (sha256 match): .../backups/docs_2026-08-13.tar.gz.gpg ``` Detection reads `lsblk` and treats a device as USB when `TRAN == usb` (the per-device deciding signal). Removable-but-not-USB slots (e.g. a SATA card reader) are skipped. If a device shows no `TRAN` at all, `lsblk`'s answer is cross-checked against `/dev/disk/by-id/usb-*` symlinks and `lsusb` before it is offered. **Unmounted stick?** If the USB stick is plugged in but only shows as `sdax` with no mountpoint (common on CLI boxes with no automounter), you're offered a **mount first**, then it copies there: ```bash # [!] Found USB storage not mounted: /dev/sda1 (7.5G, DataTraveler) # Mount it at /media/usb-sda1 (world-writable) so the backup can go there? [y/N] # (y) OK Mounted /dev/sda1 at /media/usb-sda1 # [+] Copying to /media/usb-sda1/backups/docs_2026-08-13.tar.gz.gpg ... # OK Transfer verified 100% (sha256 match): .../backups/docs_2026-08-13.tar.gz.gpg ``` The mount mirrors `usb-automount` (`/media/