-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration
/etc/afanctl/afanctl.toml is typed TOML with exactly five honoured keys
(src/config.rs). Unknown keys produce a warning (not an error) and are
ignored. Every error names the key and the fix.
Pristine copy: packaging/afanctl.toml.default → installed to both
/etc/afanctl/afanctl.toml (first install) and
/usr/share/afanctl/afanctl.toml.default. Pacman backup= protects user
edits across upgrades.
[thresholds] # °C, integers
high = 66 # ramp starts here; low is derived: high - 3
max = 86 # full speed from here; guard: max <= 95 (Tjmax 100 - 5)
[curve]
min_rpm = 1200 # clamped to >= fan1_min at load
max_rpm = 6200 # clamped to <= fan1_max at load
[poll]
interval_s = 1 # 1..=12; must stay well below the unit's WatchdogSec=15
# (two watchdog pings per poll — start and end of each
# one; starving it crash-loops)| Dotted key | Type | Default | Meaning |
|---|---|---|---|
thresholds.high |
integer °C | 66 |
Ramp starts here. low is derived: high − 3 (= 63 by default). |
thresholds.max |
integer °C | 86 |
Full speed from here. Guard: max ≤ 95 (Tjmax 100 − 5). |
curve.min_rpm |
u32 rpm |
1200 |
Curve floor. Clamped up to fan1_min at load. |
curve.max_rpm |
u32 rpm |
6200 |
Curve ceiling (deliberately below the 7200 hardware max). Clamped down to fan1_max at load. |
poll.interval_s |
u64 s |
1 |
Poll period. Range 1..=12 (see watchdog coupling below). |
Safety tunables are compiled-in constants, not config: verify tolerance, sensor-loss polls, overshoot polls, slew rate, write-fail fallback, retry counts, settle windows (full table in Control Policy and Hardware Interface).
-
Missing file → falls back to the built-in defaults above (provenance
source: "defaults"instatus --json). - Present file → must contain all five keys. A missing key is refused (not defaulted) with the key name and the fix — a typo can never silently select a different curve. The daemon exits nonzero.
- Present-but-broken → refuse to start (exit nonzero, loud error naming the key and the fix). Never runs on defaults after a bad edit.
-
Unknown keys → warning strings, sorted, e.g.
unknown config key 'thresholds.low' ignored; the file still loads (forward-compatible). -
Load-time RPM clamping against hardware:
min_rpm.max(fan1_min),max_rpm.min(fan1_max). If the band empties after clamping (clamped_min >= clamped_max), the config is refused undercurve.min_rpmwith key+fix.
| Rule | Rejects | Error key reported |
|---|---|---|
thresholds.max outside 0..=95
|
max > 95 or max < 0
|
thresholds.max |
thresholds.high outside 0..=95
|
any value outside the band | thresholds.high |
high >= max |
equal (the H3 landmine) or inverted thresholds | thresholds.high |
curve.min_rpm >= curve.max_rpm |
empty rpm band | curve.min_rpm |
poll.interval_s < 1 |
0 |
poll.interval_s |
poll.interval_s > 12 |
13+ (would starve the watchdog) |
poll.interval_s |
| Non-integer value | float, string, bool where an integer belongs | respective key |
| Missing key / missing section (file present) | absent key or absent [section]
|
respective key / section name |
| Band empties after hw clamp | clamped_min >= clamped_max |
curve.min_rpm |
Temperatures that "can't reproduce a real temperature" (outside 0..=95 °C)
are a config error by construction. high == max (mbpfan's UB trigger) is
rejected outright.
The bound is the compiled-in constant config::MAX_INTERVAL_S = 12,
mirroring packaging/afanctl.service (WATCHDOG_UNIT_SEC = 15,
WatchdogSec=15). Rationale: the watchdog pings ride the poll — two per
poll, at its start and end — and the F19 settle windows block up to ≈2.7 s
inside a failing write; a poll period without ≥3 s of watchdog headroom
crash-loops the daemon (hang → SIGABRT → L2 → restart, noisily but safely).
12 is legal, 14 is rejected (pinned by test).
-
status/status --jsonalways show where the config came from:source: /etc/afanctl/afanctl.toml(or your--configpath) vssource: "defaults". - The daemon's startup evidence INFO line in the journal names the effective mode, L2 armed state, watchdog notify path, state-file path, config source path, and hardware band.
-
doctor'sconfig validationcheck reports valid / invalid (key+fix) / unknown-key WARNs; an invalid config FAILs the wholedoctorrun (exit 1).
The empirical verdict: at t_eff ≤ 64 °C ours ≈ the SMC within ±20 rpm (both
~1190–1220); during short coretemp spikes (66–85 °C) ours ramps toward the
linear target at the 750 rpm/poll slew while the SMC stayed near 1.2k rpm (its
own sensors did not register those spikes). Ours is more responsive to
coretemp and verifiable, not quieter. Knobs if quiet is the goal: raise
[thresholds].high (start ramping later) and/or lower [curve].max_rpm.
Re-verify with doctor --compare N after any change, then
sudo systemctl restart afanctl (no in-process reload — restart is the
reload, by design).
- Home
- Installation
- CLI Reference
- Configuration
- Control Policy
- Supervisor and Run Modes
- Safety Model
- Hardware Interface
- Diagnostics (doctor)
- Plugin Surface (omafan)
- JSON Schemas
- systemd and Packaging
- Development and Testing
- Architecture and Internals
- Troubleshooting and FAQ
- Background and Verification
- afanctl-vs-mbpfan