Skip to content

Installation

Prakhar Yadav edited this page Sep 16, 2026 · 3 revisions

Installation

How to install, enable, upgrade, and uninstall afanctl — one command from the release, or from a checkout. packaging/PKGBUILD builds your working tree; packaging/aur/PKGBUILD builds a released source tarball and is the recipe for the AUR (complete, but unpublished — the AUR has closed registration to new maintainers). Both install the same five files, and afanctl.service, the polkit rule 49-afanctl.rules and afanctl.toml.default live in packaging/ and travel inside the release tarball.

Prerequisites

Requirement Detail
Machine Pre-T2 Intel Mac with applesmc + coretemp (tested: MacBook Pro 2017 A1708, MacBookPro14,1). No pwm* attributes needed — applesmc does not expose them.
OS Arch Linux (Omarchy) with systemd as PID1, kernel 7.2.3-arch1-3 at verification time.
Build tool extra/rust 1:1.98.1 (cargo 1.98.1, rustc 1.98.1), makepkg (base-devel).
Privileges makepkg -si and systemctl enable/start, doctor --roundtrip, selftest-panic, and any mode-changing verb against the live daemon need root (sudo/pkexec). Plain status reads work as a normal user against fixtures; against live /sys the L2-related doctor checks need root (see Diagnostics (doctor)).
Distribution A tagged GitHub release (v0.1.0) plus install.sh at releases/latest/download/ for the one-command install. No AUR package yet — registration there is closed to new maintainers. The taken neighbours are fanctl/macfanctl/smctl.

Install (one command)

curl -fsSL https://github.com/yadav-prakhar/afanctl/releases/latest/download/install.sh | sudo bash
sudo systemctl enable --now afanctl

That fetches the package attached to the latest release, checks its SHA-256 against that release's SHA256SUMS, and installs it with pacman -U. Read it before you pipe it — the script is install.sh in the repository root, and ... | bash -s -- --dry-run resolves, downloads and verifies without installing (--version 0.1.0 pins a release).

Re-run the same command to upgrade; sudo pacman -R afanctl removes it.

Build from source (no install)

cd packaging && makepkg

This builds the release binary (cargo build --release --locked) and stages the package without touching the system. Artifacts (*.pkg.tar.zst) are untracked build outputs.

Install from source

cd packaging && makepkg -si

What package() installs (see packaging/PKGBUILD):

Path Purpose Mode
/usr/bin/afanctl the binary 755
/usr/lib/systemd/system/afanctl.service the systemd unit (systemd and Packaging) 644
/etc/afanctl/afanctl.toml active config (from afanctl.toml.default on first install) 644, pacman backup=-protected
/usr/share/afanctl/afanctl.toml.default pristine default copy 644
/usr/share/polkit-1/rules.d/49-afanctl.rules optional passwordless pkexec rule (wheel only) 644

backup=('etc/afanctl/afanctl.toml') means pacman preserves your edits across upgrades (kills the "missing≡0 / silently reset config" class).

Enable and start (safe by construction)

The unit installs disabled, and its ExecStart is /usr/bin/afanctl daemon --mode observe — so even after enabling, the daemon writes nothing until you opt in:

sudo systemctl enable --now afanctl
systemctl status afanctl
afanctl status
sudo afanctl doctor

Expected healthy state after install: mode: observe, fan1_manual = 0, fan ~1200 rpm on the SMC curve, doctor all PASS (see Diagnostics (doctor)).

The reinstall-then-restart discipline (non-negotiable)

After EVERY package reinstall, restart the unit and confirm the loaded build. A fresh install without a restart silently keeps testing the old binary — this cost one hardware round during the build.

sudo systemctl restart afanctl
sudo afanctl doctor            # includes the stale-binary check: FAIL/WARN if
                               # the running daemon predates the installed binary

doctor's running daemon matches installed binary check detects "unit older than the installed binary" (it compares daemon start time against the binary mtime, with a /proc/<pid>/exe/usr/bin/afanctl fallback). It does not detect "package older than repo HEAD" — the restart discipline covers that gap.

Upgrading

  1. Re-run the install command (from a checkout: cd packaging && makepkg -si).
  2. Restart: sudo systemctl restart afanctl.
  3. Verify: sudo afanctl doctor (all PASS), afanctl status (mode/config as expected). Your /etc/afanctl/afanctl.toml edits survive via backup=.
  4. If you edited the unit's ReadWritePaths or ExecStart --mode, re-apply with sudo systemctl daemon-reload after reinstall.

Opting into control

sudo afanctl curve            # continuous curve control (writes cmd.json; daemon applies it)
sudo afanctl observe          # release back to firmware (always safe)
sudo afanctl hold 3000        # fixed 3000 rpm (clamped to fan1_min..fan1_max)

/run is tmpfs: after every reboot the machine is back on the SMC curve in observe, regardless of what was commanded before. There is deliberately no persisted mode across reboots (PRD §12).

Uninstall / disable

sudo systemctl disable --now afanctl   # stop + disable; startup reconcile already restored AUTO
sudo pacman -R afanctl                 # remove package; backup config handling per pacman policy
cat /sys/devices/platform/applesmc.768/fan1_manual   # expect 0 (firmware owns the fan)

Verifying the install

  • afanctl --versionafanctl 0.1.0.
  • afanctl -h → usage text (see CLI Reference).
  • afanctl status → per-sensor temps, t_eff, mode, fan rpm/min/max, config provenance, recent errors.
  • sudo afanctl doctor → 10 PASS lines, exit 0. Non-root run FAILs the write-mode checks by design (see Diagnostics (doctor) "Root required").
  • systemctl status afanctlactive (running), READY + watchdog armed in the journal.

Troubleshooting install issues

Symptom Cause / fix
makepkg fails on missing rust Install extra/rust (sudo pacman -S rust), verify cargo --version → 1.98.x.
systemctl enable survives but fan still manual after reboot Reboot always lands in observe; if fan1_manual reads 1, a foreign supervisor (e.g. mbpfan) or a stale cmd.json re-applied curvedoctor's daemon-mode check names it. The daemon's startup reconcile unconditionally restores AUTO out of any Manual owner first.
doctor WARN unit not installed Expected until makepkg -si installs the unit; WARN never blocks (exit stays 0).
doctor WARN running daemon predates the installed binary You reinstalled without restarting — sudo systemctl restart afanctl.
Config error on first start A present-but-broken /etc/afanctl/afanctl.toml refuses to start (never defaults silently). Every error names the key and the fix — see Configuration.

Clone this wiki locally