Add afanctl: applesmc fan supervisor for pre-T2 Intel Macs (A1708) - #476
Open
yadav-prakhar wants to merge 1 commit into
Open
yadav-prakhar wants to merge 1 commit into
yadav-prakhar wants to merge 1 commit into
Conversation
afanctl is a safety-first single-fan supervisor for Macs whose fan is driven by the Apple SMC through applesmc (no pwm* attributes, so fancontrol cannot be used): it reads coretemp and drives fan1_manual/fan1_output, and every failure path returns the fan to the firmware. Verified on MacBookPro14,1 (A1708) with the full hardware acceptance gate passed. The recipe follows the repository's owned-recipe + upstream watch model: the source tarball is the tagged GitHub release, and .omarchy/package.json carries a github watch with a version pattern so bin/sync-upstream tracks releases on its own. The unit installs disabled and the daemon starts in observe mode, so installing changes nothing until the user enables it. Checked locally with helpers/upstream-watch.py (status: skipped, already current) and by building the recipe with makepkg (binary + unit + default config + polkit rule + license).
yadav-prakhar
added a commit
to yadav-prakhar/afanctl
that referenced
this pull request
Sep 16, 2026
The Omarchy Package Repository owns its recipes and tracks releases through their own upstream watch, so the submission is omacom/omarchy-pkgs#476 rather than an AUR import. Also spells out the AUR account step (register, verify the email, then paste the pubkey) and the AUR host key fingerprint to check.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
afanctlsupervises the single fan of pre-T2 Intel Macs — machines whose fan is driven by the Apple SMC throughapplesmcand which expose nopwm*attributes, sofancontrol/pwmconfigcannot drive it and the firmware curve is the only stock option.It reads
coretemp, drivesfan1_manual/fan1_output, and returns the fan to the firmware on every failure path. Upstream: https://github.com/yadav-prakhar/afanctl (tagv0.1.0), which is also where it is distributed — a tagged GitHub release with a one-command install (releases/latest/download/install.shresolves the package through the release'sSHA256SUMS, verifies its SHA-256 and runspacman -U). Verified onMacBookPro14,1(MacBook Pro A1708) — the same machine class the repository already shipsomarchy-brightness-display-appleand the other Apple hardware leaves for.Packaging notes
source: local, built from the tagged GitHub tarball (${url}/archive/refs/tags/v${pkgver}.tar.gz);.omarchy/package.jsondeclares agithubupstream watch with a version pattern, sosync-upstreamfollows upstream releases on its own.PKGBUILD. The watch resolvesv0.1.0today.arch=('x86_64')—applesmcandcoretempare Intel-only; there is no aarch64 build to invent.depends=('glibc' 'gcc-libs'):readelf -don the built binary listslibc.so.6andlibgcc_s.so.1and nothing else.makedepends=('cargo')(extra/rustprovides it)./etc/afanctl/afanctl.toml(backup=, so user edits survive upgrades), a pristine default copy under/usr/share/afanctl/, an optional polkit rule allowing passwordlesspkexec afanctl status|observe|curve|hold <rpm>forwheelusers in local active sessions, and the license.ExecStartisdaemon --mode observe: installing changes nothing until the user enables it, and even then the daemon writes nothing until they ask forcurveorhold./runis tmpfs, so every reboot lands back on the firmware curve.Safety
It writes to
/syson machines that get hot, so the failure policy is the design: a per-poll verify/re-assert with a 3-strike ladder that hands the fan back to AUTO and latches monitor-only; a pre-opened fd plus panic/signal handlers that restore AUTO in a singlewrite(2)(proven by a hiddenselftest-panicprobe); and systemdType=notify+WatchdogSec=15+Restart=always+ sandboxing (ProtectSystem=strict,ReadWritePathspinned to the applesmc platform directory).afanctl doctoris read-only except a confirm-flagged 2-second roundtrip; a broken config refuses to start rather than silently running on defaults.Validation
python helpers/upstream-watch.py check pkgbuilds/afanctl→{"status": "skipped", "current": "0.1.0", "reason": "already current"};python tests/upstream-watch.py→ 22 tests OK on this checkout.makepkginpkgbuilds/afanctlbuildsafanctl 0.1.0-1from the tagged tarball; the package carries the binary, unit, config, default copy, polkit rule and license.doctor, a write roundtrip, the deliberate-panic death path, a 1 h+ observe soak (0.05 % CPU, 2.4 MB peak RSS),SIGKILLrescue in curve mode, a 600-sampledoctor --comparetable, polkithold, and the reboot test — after four adversarial review rounds. 170 tests and four quality gates; av*tag builds and publishes the release artifacts in CI..gitattributesexport-ignores the upstream AUR recipe directory so the tarball contains nothing self-referential.Known limits
status --jsonand the/run/afanctl/cmd.jsoncontrol channel instead.holdcan keep the fan quiet under load. The overshoot guard, the clamp tofan1_min..fan1_maxand both fallback layers stay active, anddoctorwarns while a hold is active.coretemp, not quieter:doctor --compare 600measured a mean Δ of 191 rpm against the SMC curve, with ours louder on 392 of 600 samples (the SMC's own die sensors read 10–15 °C hotter and miss shortcoretempspikes). This is a verifiability and responsiveness argument, not a noise one.