Skip to content

Releases: fexxdev/Mystral

v1.1.1

Choose a tag to compare

@fexxdev fexxdev released this 09 Jun 07:52

Quieter sleep (#2)

Fans no longer keep spinning at the last forced speed while the Mac is asleep. On system sleep the helper hands fan control back to macOS auto mode, so the firmware idles the fans during sleep; on wake your fan curve re-engages automatically.

No action needed — the in-app updater will offer this automatically.

Closes #2.

v1.1.0

Choose a tag to compare

@fexxdev fexxdev released this 01 Jun 08:16

⚡ Power monitoring

Mystral now reads and visualizes power draw — no extra permissions, no root.

Menu bar

  • New display modes: Icon + Power and Power Only (total system watts).

Live dropdown

  • Total system power plus a CPU / GPU breakdown.

Dashboard

  • New Power section between the temperature cards and the chart:
    • Total system watts with a stacked CPU / GPU / Other bar.
    • A Power — Last 5 Minutes chart (Total / CPU / GPU).

Total power comes from the SMC (PSTR); CPU/GPU come from the IOReport
"Energy Model". Other = total − CPU − GPU (RAM, display, SSD, etc.), since
the two sources measure different things.


Install: open the DMG, drag Mystral.app to Applications. First launch:
right-click → Open to bypass Gatekeeper (unsigned build).

v1.0.15

Choose a tag to compare

@fexxdev fexxdev released this 29 May 14:24

The fan helper now never stops — and only asks for your password once

Previously the background SMC helper was periodically terminated by macOS (launchd housekeeping reaped it every ~20–40 minutes), causing it to relaunch and prompt for your password again each time.

What changed

  • The helper is now installed as a launchd LaunchDaemon (KeepAlive). macOS keeps it alive indefinitely, it survives reboot, and you are asked for your password only once — at first install, never again.
  • Manual restarts and post-update binary reloads are now prompt-free.

Install

  1. Drag Mystral.app to Applications.
  2. Right-click → Open the first time (unsigned build, bypasses Gatekeeper).
  3. Enter your password once to install the fan helper. That's it.

Uninstall the helper

sudo launchctl bootout system/com.fexxdev.Mystral.helper
sudo rm /Library/LaunchDaemons/com.fexxdev.Mystral.helper.plist

Verified: the helper ran for a full hour on the same process with zero deaths and zero password prompts.

v1.0.14

Choose a tag to compare

@fexxdev fexxdev released this 25 May 09:27

Fixes

  • Fixed helper restart getting permanently stuck: Aligned staleness detection thresholds between the health check and restart logic (was 60s vs 120s, causing a dead zone where the app knew the helper was down but wouldn't restart it)
  • Added 12-hour automatic retry: After exhausting 3 restart attempts, the app now retries every 12 hours instead of giving up permanently
  • Added "Restart Helper" button: The error banner now includes a manual restart button instead of the misleading "Attempting to restart..." text

v1.0.13

Choose a tag to compare

@fexxdev fexxdev released this 20 May 15:30

What's Changed

Bug Fixes

  • Fix helper crash on macOS Tahoe: The SMC helper was crashing before reaching Swift code because the LLVM profiling runtime tried to write default.profraw to / (read-only root cwd). Fixed by setting LLVM_PROFILE_FILE=/dev/null and changing cwd to /tmp in the launch script.
  • Fix missing Sensors empty state icon: thermometer.slash doesn't exist as an SF Symbol — replaced with exclamationmark.triangle.

Improvements

  • Unified empty state styling: Dashboard, Sensors, and Fans views now use consistent vertical centering with ContentUnavailableView.
  • Better helper launch diagnostics: Launch script now logs binary path, UID, cwd, PID, and alive/dead status to /tmp/mystral-helper-launch.log.

Full Changelog: v1.0.12...v1.0.13

v1.0.12

Choose a tag to compare

@fexxdev fexxdev released this 19 May 14:42

In-App Auto-Updater

Updates can now be downloaded and installed directly from within the app — no more opening GitHub in the browser.

How it works

  1. Go to Settings → Updates → Check Now
  2. If an update is available, click Install Update
  3. The app downloads the DMG with a progress bar, installs, and relaunches automatically

Details

  • Real-time download progress with cancel support
  • Automatic DMG mount, version validation, and app replacement
  • Rollback if installation fails
  • Elevates via admin prompt only if the install directory requires it
  • Removes macOS quarantine attributes for seamless launch

v1.0.11

Choose a tag to compare

@fexxdev fexxdev released this 19 May 14:02

Fix: Prevent unnecessary password prompts during sleep/wake

The SMC helper was being killed and relaunched (triggering a password prompt) whenever its data file became temporarily stale — e.g. during display sleep, power nap, or system wake transitions.

Changes

  • Raise checkHelper staleness threshold from 10s to 120s for alive processes with matching version
  • Add PID liveness check in SMCProxyService before declaring helper dead
  • Tolerate up to 60s of stale data if helper process is still alive
  • Ignore SIGHUP in helper process to prevent unexpected termination
  • Launch helper with nohup for additional signal isolation
  • Observe screensDidWakeNotification for display wake grace periods

Mystral 1.0.10

Choose a tag to compare

@fexxdev fexxdev released this 17 May 18:00

What's New

Accurate Chip Detection

  • Fixed wrong chip identification — Mystral now reads the chip name directly from the OS (machdep.cpu.brand_string sysctl) instead of guessing from SMC sensor counts. All Apple Silicon chips (M1, M1 Pro, M1 Max, M2, M3, M4, etc.) are now correctly identified.
  • Robust CPU core counting — CPU core sensor detection is now prefix-based, so new or unmapped sensor keys are always counted correctly.
  • Improved diagnostic export — SMC export now includes hw.model identifier and sysctl chip name, making it easier to debug hardware-specific issues.

Full Changelog

v1.0.9...v1.0.10

Mystral 1.0.9

Choose a tag to compare

@fexxdev fexxdev released this 16 May 22:17

What's New

Helper Reliability & Wake Safety

  • Activity assertion — the SMC helper now holds a latencyCritical activity assertion so macOS won't throttle or kill it during display sleep.
  • Timer leeway tightened — helper GCD timer leeway reduced to 100ms (was unlimited), preventing stale data during idle.
  • 15-second wake grace period — proxy and fan controller now ignore stale-data alerts for 15s after system wake, eliminating false "helper down" banners.
  • Launch verification — helper launch now polls for the PID file to confirm the process actually started, instead of trusting NSAppleScript blindly.
  • Capped retries — max 3 consecutive launch failures before giving up, stopping infinite admin password prompt loops.

Cache & State Fixes

  • Wake-safe cachesnotifyWake() now clears Ftst unlock state and SMCKit key-info cache after system sleep, so stale firmware state doesn't persist.
  • FanController wake handler — invalidates caches for both proxy and direct SMC modes on wake.
  • Power source monitor — uses .commonModes so profile auto-switching reacts to AC/battery changes even during modal dialogs.

Cleanup

  • Removed dead reUnlockAfterWake code path, replaced with cleaner notifyWake() flow.

Full Changelog

v1.0.7...v1.0.9

Mystral 1.0.7

Choose a tag to compare

@fexxdev fexxdev released this 06 May 17:41

What's New

Helper Reliability

  • Fixed helper crash — the root-privileged SMC helper could crash on Debug/coverage builds due to LLVM profiling writing to a read-only filesystem (/). The helper now sets its working directory to /tmp.
  • Staleness detection — the app now detects when the helper stops responding (stale data file >10 seconds) and reports it immediately instead of silently serving cached readings.
  • Auto-restart — after 3 consecutive helper failures, the app automatically re-launches the helper with a 30-second cooldown between attempts.
  • Dashboard warning banner — a visible red banner appears when the helper is unresponsive, so you know fan control is paused.

Hardening & Safety

  • Command input validation — the helper now validates all incoming commands (fan index 0–7, percentage 0–100, mode 0–1) and rejects out-of-range values with a log entry.
  • CurvePoint clamping — temperature (0–120°C) and fan percentage (0–100%) are clamped on init, decode, and edit to prevent invalid curve data.
  • Profile name sanitization — empty or whitespace-only profile names are replaced with "Untitled Profile".
  • Division-by-zero guard — fan curve interpolation now handles overlapping temperature points safely.
  • Write gating — the app stops queuing SMC commands when the helper is known to be dead, avoiding stale command buildup.

Performance & Correctness

  • SMC key info cachereadKeyInfo results are cached, reducing repeated IOKit calls during each polling tick.
  • Refresh cooldownSMCProxyService skips redundant file I/O within 0.5 seconds, avoiding double-reads when sensors and fans are fetched in the same tick.
  • Stable chart IDs — history chart data points use deterministic IDs instead of random UUIDs, eliminating SwiftUI diff thrashing.
  • Fan slider debounce — manual fan override slider now debounces at 200ms to avoid flooding the helper with rapid commands.
  • Menu bar timer sync — the menu bar update timer now uses the configured polling interval instead of a hardcoded 2 seconds.

Fixes

  • Memory leak fixProfileAutoSwitcher now properly balances passRetained/release for the IOKit power source callback.
  • Helper kill moved off main thread — stale helper termination + relaunch now runs in a Task.detached instead of blocking the main thread with usleep.
  • Monotonic restart cooldown — helper restart timing uses ProcessInfo.systemUptime instead of wall-clock Date, immune to clock changes.
  • Fan-stuck detection — threshold changed from == 0 RPM to < 100 RPM for more reliable stuck-fan alerts.
  • Sensor registry consistencyAlertManager and StressTester now use SensorRegistry.cpuCoreSensors instead of hardcoded hasPrefix("Tp").
  • Settings reset — "Reset All Settings" now resets all keys (smoothing, deadband, aggressive override) and syncs in-memory state.
  • Pre-release version parsing — update checker handles version strings like 1.0.0-beta gracefully.
  • Profile loading errors — corrupt profile JSON now logs the error instead of silently failing.
  • Release build optimizations — Release config now uses -O, whole-module compilation, and dSYM generation.

Full Changelog

v1.0.6...v1.0.7