feat: pos system event-trigger — state-based threshold monitors (eventer)

Each line of event.env is an independent rule: ["msg" if ] <check> <op> <thr>.
Check runs on every pass; first numeric output compared float-safe; op is the
rightmost 'op threshold' pair so checks with their own >/< parse fine. Alerts
once on false->true + one recovery message on true->false (no repeats while
the condition holds); per-rule state keyed by rule-line hash in
~/.local/share/linux_post_install/eventer/state/.

Subcommands: run (timer entrypoint), config (interactive add/remove/edit with
check-validation), list (rules + live values), enable [interval] (systemd
user timer pos-event-trigger.timer; 5m..weekly or OnCalendar; graceful without
a user manager, loginctl enable-linger attempt), disable, status. --dry-run
honors the DEV.md convention. Alerts via lib/notify.sh (Telegram default).

New: bin/pos-system-event-trigger, lib/eventer-lib.sh, config/event.env
template (no-clobber via postinstall), install.sh lib install, INTERACTIVE_CMDS
entry. Docs: POS.md system row, HOWTO.md index, howto/event-trigger.md.
make gen && make check green; functional tests cover trigger/recovery/no-repeat,
float+unit parsing, editor add/remove/edit + validation + dry-run, timer
enable/disable/status, dispatcher routing.
This commit is contained in:
Your Name
2026-08-09 17:09:59 +00:00
parent 2e57634877
commit 030ec0b456
12 changed files with 651 additions and 17 deletions
+19 -15
View File
@@ -10,19 +10,19 @@
<!-- GEN:START docmap -->
| ## 1. Project Overview | 2843 |
| ## 2. Directory Structure | 44183 |
| ## 3. Installation Flow | 184236 |
| ## 4. The `pos` CLI System | 237300 |
| ## 5. Shared Library — `lib/common.sh` | 301332 |
| ## 6. Docker Compose / ScaleTail | 333375 |
| ## 7. Optional Apps (`apps/`) | 376405 |
| ## 8. Entertainment Module | 406419 |
| ## 9. Systemd Services | 420432 |
| ## 10. Configuration Files | 433459 |
| ## 11. Coding Conventions | 460492 |
| ## 12. Development Workflow | 493545 |
| ## 13. Key File Quick Reference | 546595 |
| ## 14. Common Tasks for Agents | 596621 |
| ## 2. Directory Structure | 44184 |
| ## 3. Installation Flow | 185237 |
| ## 4. The `pos` CLI System | 238302 |
| ## 5. Shared Library — `lib/common.sh` | 303334 |
| ## 6. Docker Compose / ScaleTail | 335377 |
| ## 7. Optional Apps (`apps/`) | 378407 |
| ## 8. Entertainment Module | 408421 |
| ## 9. Systemd Services | 422434 |
| ## 10. Configuration Files | 435461 |
| ## 11. Coding Conventions | 462494 |
| ## 12. Development Workflow | 495547 |
| ## 13. Key File Quick Reference | 548599 |
| ## 14. Common Tasks for Agents | 600625 |
<!-- GEN:END docmap -->
## 1. Project Overview
@@ -79,6 +79,7 @@ Linux_post_install/
│ ├── pos-network-scan # Parallel ping sweep of CIDR
│ ├── pos-ssh-load-keys # Load all SSH keys into the agent
│ ├── pos-system-backup # Encrypted (AES-256) folder snapshots (tar + gpg)
│ ├── pos-system-event-trigger # State-based rule monitors; alerts via notify when a check crosses a threshold
│ ├── pos-system-firewall # Interactive UFW management
│ ├── pos-system-health # Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL
│ ├── pos-system-nfs-client # Mount NFS shares (ephemeral or persistent systemd mount units)
@@ -274,6 +275,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
| network | scan | `pos-network-scan` | Parallel ping sweep of CIDR |
| ssh | load-keys | `pos-ssh-load-keys` | Load all SSH keys into the agent |
| system | backup | `pos-system-backup` | Encrypted (AES-256) folder snapshots (tar + gpg) |
| system | event-trigger | `pos-system-event-trigger` | State-based rule monitors; alerts via notify when a check crosses a threshold |
| system | firewall | `pos-system-firewall` | Interactive UFW management |
| system | health | `pos-system-health` | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL |
| system | nfs-client | `pos-system-nfs-client` | Mount NFS shares (ephemeral or persistent systemd mount units) |
@@ -554,12 +556,13 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `lib/flags.sh` | 60 | Feature flag store (set/clear/is_set/value/list/status) |
| `lib/notify.sh` | 76 | Multi-platform alerting (`notify_send`) — opt-in source, silent-fails |
| `lib/entertainment-lib.sh` | 350 | Entertainment module lib (ENABLED parsing, scheduler sync) |
| `lib/eventer-lib.sh` | 312 | Eventer lib (rule parsing, float compare, per-rule state, user-timer sync) |
| `bin/flag-reader` | 58 | Inspect flags (list/status/`--raw`) |
| `bin/flag-set` | 21 | Set a flag (optionally with a value) |
| `bin/flag-clear` | 21 | Unset a flag |
| `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) |
<!-- GEN:START filetable -->
| `bin/pos` | 277 | CLI dispatcher with smart arg matching + logging + category help |
| `bin/pos` | 280 | CLI dispatcher with smart arg matching + logging + category help |
| `bin/pos-ai-gemini` | 311 | Chat with Google Gemini (ask, chat, models, sessions) |
| `bin/pos-communication-telegram-listener` | 563 | Telegram bot listener: map /command → bash, run them on chat messages |
| `bin/pos-communication-telegram-sender` | 221 | Send Telegram messages/files/links/stickers via Bot API (send, test) |
@@ -581,13 +584,14 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/pos-network-scan` | 271 | Parallel ping sweep of CIDR |
| `bin/pos-ssh-load-keys` | 31 | Load all SSH keys into the agent |
| `bin/pos-system-backup` | 126 | Encrypted (AES-256) folder snapshots (tar + gpg) |
| `bin/pos-system-event-trigger` | 212 | State-based rule monitors; alerts via notify when a check crosses a threshold |
| `bin/pos-system-firewall` | 291 | Interactive UFW management |
| `bin/pos-system-health` | 209 | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL |
| `bin/pos-system-nfs-client` | 138 | Mount NFS shares (ephemeral or persistent systemd mount units) |
| `bin/pos-system-nfs-server` | 134 | Manage the NFS kernel server (status, share/unshare exports, enable/disable) |
| `bin/pos-tree` | 110 | Show the pos CLI command tree: categories, commands, and subcommands |
| `bin/pos-usb-server` | 218 | USB Redirector server control (--ls, --share; prompts when args omitted) |
| `completions/pos.bash` | 284 | Dynamic bash completion |
| `completions/pos.bash` | 286 | Dynamic bash completion |
<!-- GEN:END filetable -->
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
+1
View File
@@ -14,6 +14,7 @@ authoritative one-line reference (every command + flag), see
| `pos docker` | Compose services, container dashboards, disposable VMs | [docker](howto/docker.md) |
| `pos media` | Download audio/video via yt-dlp | [media](howto/media.md) |
| `pos system` | Backups, firewall, health dashboard | [system](howto/system.md) |
| `pos system event-trigger` | Threshold-rule monitors that alert on crossing | [event-trigger](howto/event-trigger.md) |
| `pos ssh` | Load keys into the agent | [ssh](howto/ssh.md) |
| `pos usb` | Share USB devices over the network | [usb](howto/usb.md) |
| `pos communication` | Send Telegram messages/files/alerts | [communication](howto/communication.md) |
+1
View File
@@ -187,6 +187,7 @@ The standalone `vbox` command still works and forwards to `pos docker vbox` (see
| `pos system health [--send] [--markdown]` | `bin/pos-system-health` | Host health dashboard: disk per mount, RAM/swap, failed systemd units, backup age, fail2ban, docker containers. Exits 1 if any check FAILs | `--send`/`--markdown` are notification-only: they send the summary via `lib/notify.sh` to every platform in `NOTIFY_PLATFORM` and do NOT print the dashboard (so wrappers like the Telegram listener don't echo it back — pair with the listener's `@quiet` marker). `HEALTH_BACKUP_MAX_AGE_DAYS` (default 2) and `BACKUP_SERVICE_ROOTS` come from `~/.config/linux_post_install/system.env`; `--help` shows the effective values. Platform list from `~/.config/linux_post_install/notify.env` |
| `pos system nfs-server <cmd>` | `bin/pos-system-nfs-server` | Manage the NFS kernel server: `status`, `share <path> [client]`, `unshare <path>`, `list`, `reload`, `enable`, `disable` | Requires `nfs-kernel-server` (added to `preinstall.sh` PACKAGES). Exports live in `/etc/exports`; `share` is idempotent (replaces any existing line for the path) and runs `exportfs -ra`. Default client `*(rw,sync,no_subtree_check)` — the tool warns you to restrict it; help prints Tailscale CGNAT (`100.64.0.0/10`), WireGuard (`10.10.0.0/24`) and LAN examples. Mutating commands announce via `lib/notify.sh` |
| `pos system nfs-client <cmd>` | `bin/pos-system-nfs-client` | Mount and manage NFS shares: `mount <server:export> <local-dir>`, `unmount <local-dir>`, `list`, `persist <server:export> <local-dir>`, `unpersist <local-dir>` | Requires `nfs-common` (added to `preinstall.sh` PACKAGES). `persist` writes a systemd `.mount` unit (`systemd-escape --path --suffix=mount`) with `After=network-online.target` / `Wants=network-online.target` — mounts only once all interfaces are up, no fstab edits to break boot — then `daemon-reload` + `enable --now`. `unpersist` stops/disables/removes the unit. `mount`/`persist` announce via `lib/notify.sh` |
| `pos system event-trigger <cmd>` | `bin/pos-system-event-trigger` | State-based rule monitors: `run`, `config`, `list`, `enable [interval]`, `disable`, `status`. Each line of `~/.config/linux_post_install/event.env` is an independent rule `["msg" if ] <check-command> <op> <threshold>` (op `> < >= <= == !=`, unit suffix ok: `60c`, `80%`); the check command's first numeric output is compared float-safe. Alerts once on false→true, plus one recovery message on true→false — no repeats while the condition holds, via `lib/notify.sh` | `config` is an interactive editor that validates rules by test-running the check; `run` is what the systemd user timer (`pos-event-trigger.timer` + oneshot `.service`, interval set at `enable`) executes; supports `--dry-run`; rules are arbitrary shell commands (chmod 600, same trust model as the Telegram map); template `config/event.env` auto-installed no-clobber by postinstall |
`systemd/pos-health.service` + `systemd/pos-health.timer` run `pos system health --send --markdown` daily at 08:00 as the installing user. `postinstall.sh` enables the timer automatically once `~/.config/linux_post_install/telegram.env` exists — re-run postinstall after configuring a notify platform to pick it up. The service also loads `system.env` + `notify.env` via `EnvironmentFile=`.
+75
View File
@@ -0,0 +1,75 @@
# How-To: `pos system event-trigger`
State-based threshold monitors: each line of `event.env` is an independent rule;
when a rule's check crosses its threshold you get one alert (plus one recovery
message when it clears). Alerts go through `lib/notify.sh` — Telegram by
default, `NOTIFY_PLATFORM` for more.
```bash
pos system event-trigger config # interactive rule editor
pos system event-trigger list # rules + live check values
pos system event-trigger enable 5m # evaluate every 5 minutes via systemd
pos system event-trigger status # timer + rule count
pos system event-trigger disable # stop monitoring
pos system event-trigger run --dry-run # preview what would fire
```
---
## Rule format
One rule per line in `~/.config/linux_post_install/event.env` (chmod 600):
```
["<message>" if ] <check-command> <op> <threshold>
```
| Part | Meaning |
|------|---------|
| `"<message>" if` | Optional custom alert text (quote-stripped); without it the message is auto-composed |
| `<check-command>` | Any shell command; its **first numeric output** is the value (pipes/args fine) |
| `<op>` | `>` `<` `>=` `<=` `==` `!=` |
| `<threshold>` | Number with optional unit suffix — `60c`, `80%`, `10g` all work |
The operator is detected as the rightmost `op threshold` pair in the line, so
check commands containing their own `>`/`<` (redirection, awk) don't confuse it.
Examples:
```
# event.env
"CPU too hot" if sensors -u | grep -m1 temp1_input | awk '{print $2}' > 60c
"Disk nearly full" if df -P / | awk 'NR==2{print $5+0}' > 80%
"Load high" if uptime | sed 's/.*load average: //; s/,.*//' >= 4
```
Behavior:
- The check runs on every pass. Non-numeric/empty output, or an unparseable
line → the rule is skipped with a warning (other rules still run).
- Alerts fire **once** when the condition turns true, and once more when it
recovers — a hot CPU for two hours is one message, not twenty.
- State is tracked per rule in `~/.local/share/linux_post_install/eventer/state/`
(keyed by a hash of the rule line — editing a rule resets its state).
## Scheduling
`enable [interval]` installs a systemd **user timer** (`pos-event-trigger.timer`
+ oneshot `.service` that runs `pos system event-trigger run`). Intervals:
`5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly`, or a raw `OnCalendar=…`.
Requires a reachable user systemd manager; run
`sudo loginctl enable-linger $USER` once so timers fire without login (the tool
tries this and warns if it can't).
## Alerting
`run` sends via `lib/notify.sh notify_send`, which delivers to every platform in
`NOTIFY_PLATFORM` (default `telegram`; comma-separated = fan out). Adding a
Matrix/Synapse sender later needs no changes here — see DOC/DEV.md → Alerting.
---
## Related
- Reference: [DOC/POS.md → system](../POS.md#system)
- Notifications: [communication](howto/communication.md) / `lib/notify.sh`