braid monitor
Note
Experimental π§ͺ
This command is experimental: the idea or implementation is still uncertain and may be removed, replaced, or overhauled before braid v1.0.
Checks btrfs device error stats, missing devices, and SMART alerts. Designed to be run automatically by a systemd timer (every 5 minutes by default). Exits with a status code that drives the alert pipeline.
When to use it
You normally donβt run this by hand β the braid-monitor.timer systemd unit runs it automatically. Use it directly when debugging the alert system or testing your monitoring setup.
Basic example
sudo braid monitor
No output on success. Check the exit code:
sudo braid monitor; echo $?
Exit codes
| Code | Meaning |
|---|---|
| 0 | Healthy, pool is offline, or another braid command holds the pool lock (cycle skipped, re-evaluated on the next timer tick) |
| 1 | Critical alert active β a disk-health problem; the beeper fires |
| 2 | Pre-monitor setup error (e.g. pool-lock I/O, config load failure) |
| 3 | Warning-only alert active β a proactive capacity (ENOSPC) risk; notifies via alertCommand, no beep |
What triggers an alert
Alerts have two severities. The audible beep is reserved for Critical; a Warning-only cycle takes the non-beeping advisory path.
Critical (exit 1, beeps):
- btrfs device errors β any device in the pool has read, write, flush, corruption, or generation errors above the acknowledged baseline, including errors discovered during scrub.
- Missing device β btrfs reports a device as missing or a pool device has a null underlying path.
- SMART alert β smartd has written a SMART alert flag (via the braid smartd notifier).
- Scrub failed β the scheduled maintenance scrub failed to run or complete.
braid-scrub.serviceβsonFailurewrites ascrub-failedflag and starts the beeper; the next monitor cycle latches aScrubFailedcause. This covers scrub execution failure only β corruption found by a scrub still alerts as btrfs device errors above. - Computation error β a probe, parse, btrfs device stats call, mountinfo read, acked-stats baseline load, acked-stats save during self-heal, or alert-latch load/quarantine failed. Monitor fails closed: it latches a
ComputationErrorcause so the beeper fires andbraid statusshows the detail.
Warning (exit 3, no beep β notifies via alertCommand and braid status):
- ENOSPC risk β the pool is one disk-loss away from being unable to allocate the RAID1 chunk pairs needed to restore redundancy. This is the same shared predicate
braid statusandbraid doctorreport, now evaluated proactively each monitor cycle. Acknowledge to snooze the reminder (default 7 days), not resolve it: if the pool is still at risk when the interval elapses, monitor reminds again (andbraid statusshows the advisory the whole time); ack again to re-snooze. It re-arms immediately when the risk clears. Best-effort: if thebtrfs device usageprobe fails, only this check is skipped β it never masks a Critical alert in the same cycle, and never escalates to a beep.
Flags
None. Monitor has no flags β it reads from the braid config and state files.
What happens under the hood
- Checks if the pool is mounted. If not, exits 0 (nothing to monitor).
- Runs
btrfs device statson the pool mount point. - Loads the acknowledged-stats baseline (
acked-stats.json) from a previousbraid ack. If the file is unreadable or unparseable, monitor fails closed β it latches aComputationErrorrather than firing every acknowledged cause against an empty baseline. - Self-heals stale ack state before computing alerts: prunes baseline entries for devices no longer in the pool, and clears the missing-acked flag for any device that was acknowledged missing but is now present again. If the baseline changed, the updated
acked-stats.jsonis written immediately; a write failure (e.g. EROFS, ENOSPC) is itself a fail-closedComputationError. - Computes alert causes against the reconciled baseline: btrfs device errors above the baseline, missing/null-underlying devices, the smartd alert flag, and the scrub-failed flag.
- Best-effort ENOSPC check: probes
btrfs device usageand raises anEnospcRiskWarning when the pool is one disk-loss from RAID1 chunk-pair exhaustion. A matchingenospc-ack.jsonsnooze marker suppresses it until its reminder deadline elapses, after which it re-fires every cycle until a re-ack; the marker is dropped (re-armed) when the risk clears. A probe or parse failure skips only this check. - Merges the causes into the alert latch (
alert-latch.json). The latch is sticky: once an alert fires, it stays active untilbraid ackclears it.
Alert pipeline
braid monitor --writes--> alert-latch.json --> braid status / braid tui (display)
(timer, every 5m) --exit 1--> braid-alert.service (latched orchestrator + alertCommand)
--wants--> braid-beep.service (backoff beep loop)
--exit 3--> braid-alert-advisory.service (alertCommand only, no beep)
smartd --start--> braid-alert.service
--writes--> smartd-alert --> next braid monitor cycle (latches SmartdAlert)
braid-scrub.service --onFailure--> braid-scrub-failed.service
--start--> braid-alert.service
--writes--> scrub-failed --> next braid monitor cycle (latches ScrubFailed)
On exit 1, the braid-monitor.service wrapper starts braid-alert.service, a latched orchestrator that runs any alertCommand and pulls in braid-beep.service when beeping is enabled. On exit 3 it starts braid-alert-advisory.service, which runs only alertCommand (no beep). After that, two things stay active until you braid ack, each held by a different mechanism:
- The latch and exit 1 β held by monitor. Each cycle it writes the live causes to
alert-latch.json, merging them into the existing latch, and re-exits 1 while any cause remains.braid statusand the TUI read the same file for display. - The beep β held by
braid-beep.service, not the read-back.braid-alert.serviceremains active as aRemainAfterExitlatch, while the beep loop stays active in the bound service. The wrapperβs per-cyclesystemctl startis a no-op and a skipped cycle (offline or lock-contended exit 0) does not silence it. Neither service readsalert-latch.jsonor thesmartd-alertflag.
smartd is a second, independent trigger: on a SMART fault it starts braid-alert.service directly and writes the smartd-alert flag, which the next monitor cycle latches as a SmartdAlert cause.
A failed scheduled scrub is a third, independent trigger with the same shape: braid-scrub.serviceβs onFailure runs braid-scrub-failed.service, which starts braid-alert.service directly and writes the scrub-failed flag, which the next monitor cycle latches as a ScrubFailed cause. A deliberate cancel (lock/suspend/shutdown) and a corruption-found scrub (btrfs exit 3) are both successes, so neither fires this path. This whole path exists only when braid.monitor is enabled.
The beep stops only when braid ack clears the latch and runs systemctl stop braid-alert.service; that stop cascades to braid-beep.service through BindsTo. The same ack also stops braid-alert-advisory.service, so a Warning-tier advisory is silenced too.
Related commands
- ack β acknowledge alerts and silence the beeper
- doctor β one-time diagnostic; pass
--beepto test the alert beep - status β shows active alerts in the status output