A small native macOS menu-bar fan controller aimed at keeping an Apple-silicon MacBook Pro chassis cooler in a warm room.
- Apple Silicon Mac with fans (primary target: M3 Max MacBook Pro)
- macOS 13 or later
- Xcode / Command Line Tools with Swift
- Local/admin installation
This package is source-only and intentionally does not include a pre-signed binary. Manual SMC fan control is disabled on all production hardware until a model-specific mode/target sequence has been validated. In particular, manual control is disabled on Mac14,9 after the real M2 Pro failure documented below. M3 Max manual control is not validated.
- Shows the current control temperature in the menu bar (
58°C). - Shows CPU/GPU peaks, hottest discovered SMC sensor, fan RPM and current mode.
- Runs the cooling policy in a root launchd daemon, so closing the menu UI does not stop automatic cooling.
- Optional Cool Surface curve is tuned for a warm 24-33°C room:
| Temperature | Cooling level* |
|---|---|
| 58°C | 25% |
| 65°C | 40% |
| 70°C | 60% |
| 75°C | 80% |
| 80°C | 100% |
* Temperature is the 20-second moving average of the warmer CPU/GPU sensor-group average. Cooling level is interpolated between each fan's reported minimum and maximum RPM, not a percentage of raw max RPM.
- macOS thermal state
fairforces at least 72% cooling. - macOS thermal state
seriousorcriticalforces 100% cooling. - Any discovered 95°C hotspot forces 100% cooling immediately.
- Below 48°C for 30 seconds, fan control is returned to macOS.
- Sensor/control failures return fan authority to macOS where possible.
- The launchd daemon uses
KeepAliveso it is restarted if it exits. - Only one CoolerMBP process may write SMC keys at a time. The daemon holds an OS file lock; the standalone recovery tool uses the same lock.
xcode-select --install # only if you do not already have command-line tools
cd CoolerMBP
./install.shThe installer builds locally, requests a controlled shutdown of an existing daemon, waits for it to unload and release the SMC writer lock, and verifies Apple Automatic before replacing installed files. It enables the LaunchDaemon before bootstrapping it once; RunAtLoad starts it. A bounded readiness check verifies service load, process state, XPC ping, requested/persisted Apple Automatic, fan modes, recovery state, and Ftst before reporting success. It does not use launchctl kickstart -k. On failure it returns nonzero, reports the current service/hardware state, and retains a metadata snapshot under /var/tmp/CoolerMBP-install-*.
For read-only inventory and Apple Automatic verification on the M2 Pro and M3 Max, see the hardware validation runbook. These procedures are separate from manual fan-control validation. The diagnostic collector is scripts/collect-hardware-diagnostics.sh.
On a real Mac14,9 Apple M2 Pro running macOS 27.0.1, sudo coolermbpctl max received an accepted F0Md mode=1 write, but the immediate readback did not confirm Manual. Fan 0 later reported Manual with a 0 RPM target; Fan 1 remained Apple Automatic. Normal Auto recovery refused because ownership could not be verified. After unloading the daemon and verifying writer-lock availability, the operator explicitly ran unknown-fan recovery; it succeeded and both fans returned to Apple Automatic.
Manual control is disabled on Mac14,9. M2 Pro Phase 4 must not be repeated. M3 Max manual control is not yet validated. Read-only inventory and Apple Automatic verification are separate from manual fan-control validation. Cool Surface may be accepted at low temperature while the fans stay automatic; that is not evidence of validated manual cooling. No stress testing is authorized.
- Apple Automatic — no manual override; macOS controls the fans.
- Cool Surface — proactive temperature-driven policy above. A low-temperature request may be accepted while fans remain Apple Automatic; this does not validate manual cooling.
- Maximum — a manual-control request. Production hardware currently fails closed; it does not enable fan writes.
The lower-level SMC transition implementation is covered by deterministic mocks, but production eligibility currently denies manual control on every hardware model. No model has evidence supporting a safe mode/target write sequence, and no unvalidated model is implicitly allowed. The experimental Ftst fallback remains guarded and is not reached by an ineligible manual request. Ftst is never written when unavailable, unreadable, already active with unknown ownership, or when recovery metadata cannot be stored. Ftst ownership metadata is separate from fan-control ownership metadata.
If a future reviewed build enables manual control for a validated model, the daemon records attempted fan transitions before writes and retains recovery metadata until Apple Automatic is verified. On restart or an ordinary Auto request, the daemon restores fans only when the recorded transition matches the observed mode keys and targets. Missing, invalid, or mismatched records leave manual fans untouched and set recovery required. This record is evidence of CoolerMBP's transition, not an Apple-provided process identity: another controller that writes identical values cannot be distinguished by AppleSMC.
| Key | Value/type | When written | Verification and restoration |
|---|---|---|---|
F%dmd (preferred) or F%dMd (fallback only when lowercase is confirmed absent) |
1 / 0 (ui8) |
The low-level transition path supports these values for deterministic tests and recovery. Production manual-mode requests are currently denied on all models; mode 0 remains available to verified recovery. |
Manual mode is read back with a bounded delay in the low-level path. Apple Automatic restoration has bounded stabilization verification. |
F%dTg |
Calculated target RPM (flt) |
Low-level manual transition path only; the production eligibility gate currently prevents target writes. | Target must be within the reported fan limits and read back within 100 RPM. |
Ftst |
1 / 0 (ui8) |
The 1 fallback remains unreachable through production manual requests while hardware eligibility is denied. 0 is written only to release an owned flag or from explicit unknown-ownership recovery. |
Read back the exact value. Ownership lives at /Library/Application Support/CoolerMBP/recovery.json and is cleared only after verified release. |
F%dAc, F%dMn, and F%dMx are read only. CoolerMBP does not write Ftst on direct-mode success and never writes mode 3.
SMC mode values reported by community reverse engineering are 0 (automatic), 1 (manual target), and 3 (system-managed/mitigation state). These meanings are not an Apple specification. The research notes report different behavior across tested M1, M4, and M5 systems. A separate MacFanControl project reports M3 Pro tests, while the research notes leave M3 applicability open; neither establishes behavior on this M3 Max or the M2 Pro. No hardware support claim is made for either machine until manually verified.
sudo coolermbpctl status
sudo coolermbpctl ping # Test XPC connectivity without waiting for sensor polling.
sudo coolermbpctl cool
sudo coolermbpctl max
sudo coolermbpctl autocool may persist a policy request when no manual cooling is required; do not treat that as validated manual control. max is currently denied by the production hardware eligibility gate. Do not use it for testing on the M2 Pro or M3 Max.
Try the daemon-supervised restore first. The helper verifies daemon status and direct SMC state before reporting success. If the daemon request fails, it requests a controlled shutdown, waits up to 20 seconds for the service to unload and the writer lock to become available, then runs and verifies the standalone restore. If the daemon request succeeds but verification fails, it stops with an error rather than issuing another restoration request. The fallback enables and bootstraps the service once, then waits for XPC and verifies the daemon and direct hardware state:
sudo /usr/local/sbin/coolermbp-recoverThe standalone tool is read-only except for explicit recovery commands. Do not use coolermbpctl max to validate hardware: production manual control is disabled until model-specific evidence and review exist.
On failure, coolermbp-recover returns nonzero and reports launchd state, daemon status, direct SMC status, metadata files, and recent logs. It preserves a copy of the original recovery metadata and a failure report under /var/tmp/CoolerMBP-recovery-*; inspect these files before follow-up. It does not disable the service permanently and never invokes unknown-ownership recovery automatically. A successful launchctl bootout requests service shutdown, but does not prove application-level cleanup; actual fan and Ftst readback is required.
Ordinary auto refuses manual fan states without a matching CoolerMBP transition record. If the daemon is running, the CLI and app report the explicit recovery command. After stopping the daemon and competing fan-control tools, confirming the writer lock is released, and deciding to override unknown fan ownership, run:
sudo /usr/local/sbin/coolermbpsmc recover-unknown-fansUnknown Ftst ownership remains a separate condition. After stopping other fan-control tools and deciding to return control to macOS, run:
sudo /usr/local/sbin/coolermbpsmc recover-unknown-ftstThe recover-unknown-ftst command authorizes releasing unknown Ftst only when fan ownership is already known. If both fan and Ftst ownership are unknown, the combined administrative override is sudo /usr/local/sbin/coolermbpsmc recover-unknown. Each command verifies Apple Automatic before reporting success. These commands can overwrite another fan controller's settings; use them only after deciding to return control to macOS. After explicit recovery, rerun sudo /usr/local/sbin/coolermbp-recover to verify the state and restart the daemon.
To inspect raw status:
sudo /usr/local/sbin/coolermbpsmc statusIf the daemon itself needs inspection:
sudo launchctl print system/com.superstring.CoolerMBP.daemon
tail -100 /var/log/CoolerMBP.log./uninstall.shThe uninstaller asks the daemon to restore Apple Automatic, unloads it, verifies via the independent recovery tool, and only then removes recovery metadata. If verification fails, removal stops and the metadata remains.
This app writes undocumented Apple SMC keys. Apple does not provide a supported public API for manual fan control on Apple Silicon. The implementation therefore depends on reverse-engineered interoperability behavior and can stop working after firmware/macOS changes.
Manual control can alter interactions with macOS thermal management. Ftst findings are experimental and hardware-specific. CoolerMBP does not write CPU frequency, voltage, power-limit, clock, or AppleCLPC settings, and it does not directly control P-core frequency. The reported performance changes do not establish that CoolerMBP or Ftst caused CPU performance degradation.
The code deliberately:
- never requests an RPM below the fan's hardware-reported minimum;
- refuses manual control if fan min/max limits are invalid;
- forces maximum cooling at an 80°C representative temperature, a 95°C hotspot, or
serious/criticalthermal state; - restores Apple control and latches Apple Automatic if temperature acquisition/control fails;
- verifies mode/target writes and checks that fan RPM responds;
- accepts daemon connections only from the installed, root-owned app and root-only CLI;
- provides daemon-first recovery with a bounded, lock-checked standalone fallback through
sudo coolermbp-recover. - records when it enabled
Ftst, restores only an owned override, and reports active unknown ownership as recovery required; - reconciles fan modes and owned
Ftststate at daemon startup before honoring any saved cooling mode; after a crash, restart begins in Apple Automatic; - provides read-only sensor inspection through
coolermbpsmc status.
Even with these safeguards, manual SMC fan control can interfere with Apple's thermal management. Test it while monitoring temperatures before relying on it unattended.
If status reports active Ftst with unknown ownership, do not force Ftst=0 blindly. The ordinary recovery command blocks before any SMC write. Stop competing fan tools, decide that returning control to macOS is appropriate, then use sudo /usr/local/sbin/coolermbpsmc recover-unknown-ftst. If recovery cannot verify the result, the helper leaves the daemon disabled and retains recovery metadata. SIGKILL, crashes, sleep, and power loss can interrupt transitions; the next daemon start attempts reconciliation.
For diagnostic logs, collect sudo coolermbpctl status, sudo coolermbpsmc status, and sudo tail -100 /var/log/CoolerMBP.log. For an external CPU/thermal sample, run sudo powermetrics --samplers cpu_power,thermal in a separate terminal. Compare repeated workloads using application throughput and completion time as primary measures; frequency and utilization alone do not identify effective work or prove a thermal cause. Record model, macOS build, power mode, workload, ambient conditions, and whether CoolerMBP was enabled.
build.sh ad-hoc signs binaries for local use. Before distributing a compiled build to other Macs, use your own Developer ID signing and notarization workflow.
The SMC interface and reported Ftst behavior are based on experimental reverse-engineering research, including macos-smc-fan research notes. Treat those observations as hardware-specific, not an Apple specification. See THIRD_PARTY_NOTICES.md.