| English (default) | README.md |
| Русский | docs/README.ru.md |
| Deutsch | docs/README.de.md |
| Français | docs/README.fr.md |
| 中文 | docs/README.zh.md |
| 日本語 | docs/README.ja.md |
A lightweight macOS menu bar utility that keeps your Mac awake — an open-source alternative to Amphetamine, built with Avalonia + .NET 10.
Live in the menu bar (no Dock icon), start a session for a fixed duration or indefinitely, and optionally keep the Mac awake even with the lid closed or the display asleep.
| Layer | Technology |
|---|---|
| UI | Avalonia 12, FluentTheme |
| Runtime | .NET 10 (LTS) |
| MVVM | CommunityToolkit.Mvvm (source generators) |
| Power management | IOKit IOPMAssertion* (system/display sleep) + IOKit clamshell state + pmset disablesleep via a scoped, passwordless sudoers helper |
| Native interop | Direct objc_msgSend calls (menu bar–only "Accessory" activation policy) |
| Packaging | hdiutil-built DMG, GitHub Actions |
- Menu bar–only app: no Dock icon, no application menu — everything lives in the tray icon
- Tray menu groups: Timer, Modifiers, Language, then Start/Stop Session
- Start / stop a keep-awake session from the tray; picking a timer duration starts the session
- Built-in durations: Indefinitely, 5 / 15 / 30 minutes, 1 / 2 / 5 hours, plus a remembered custom duration
- Live countdown next to Active while a timed session is running
- Dynamic pill tray icon: black when idle, green for timed sessions, red for indefinite; closed-lid and display modifiers change the fill and side bars
- Allow closed lid — keeps the Mac fully awake with the lid closed, on both AC and battery power
- Keep display awake — in addition to preventing system sleep, also prevents the display from sleeping
- UI localization for major languages (selectable from the tray)
- Settings persist across launches (
~/Library/Application Support/AmphetamineNet/settings.json) - Built-in workaround for the macOS 26
Avalonia.Nativerender-timer crash (CVDisplayLinkCreateWithActiveCGDisplays, error-6661) via a small interpose dylib
macOS normally forces sleep when the lid is closed, unless a clamshell-mode override is active (the same mechanism used when an external display is connected). AmphetamineNet combines two pieces to keep the Mac awake with the lid closed, on battery as well as on AC power:
- IOKit clamshell assertion (
kPMSetClamshellSleepStateonIOPMrootDomain) — works while the app is running. pmset -a disablesleep 1— the setting IOKit'sSleepDisabledflag actually depends on. Running this without a password prompt every time requires a one-timesudogrant.
On first use of "Allow closed lid", AmphetamineNet asks for administrator credentials once,
via a native macOS prompt, to install a narrowly scoped sudoers.d rule
(/etc/sudoers.d/amphetamine-net) that allows only:
/usr/bin/pmset -a disablesleep 1
/usr/bin/pmset -a disablesleep 0
for the current user, without a password. From then on, sessions with a closed lid start and stop without any further prompts. A background heartbeat re-applies the clamshell assertion every 30 seconds in case the OS resets it.
- macOS 12 (Monterey) or later — this app does not run on other platforms, and the Avalonia window shows an explanatory message if launched elsewhere
- .NET 10 SDK (for building from source)
- Xcode Command Line Tools (
clang) — needed once, to build the small CVDisplayLink interpose dylib
git clone https://github.com/RASLK/AmphetamineNet.git
cd AmphetamineNet
dotnet restorerun-macos.sh builds the interpose dylib if missing, builds the app, and runs it with the
environment variables the macOS 26 render-timer workaround needs:
./run-macos.sh # Debug build
./run-macos.sh Release # Release buildLogs are written to /tmp/amphetamine-net-run.log (runtime) and /tmp/amphetamine-net.log
(in-app diagnostics).
dotnet run -c ReleaseThis also works, but on macOS 26+ the app self-relaunches once with
DYLD_INSERT_LIBRARIES set to the CVDisplayLink fix dylib (built automatically by the
BuildCvDisplayLinkFix MSBuild target the first time you build on macOS).
dotnet publish AmphetamineNet.csproj -c Release -r osx-arm64 -o publish/osx-arm64 # Apple Silicon
dotnet publish AmphetamineNet.csproj -c Release -r osx-x64 -o publish/osx-x64 # Intelclang -dynamiclib -o Native/libcvdisplaylink_fix.dylib Native/cvdisplaylink_fix.c \
-framework CoreVideo -framework CoreGraphics -install_name @rpath/libcvdisplaylink_fix.dylib
dotnet publish AmphetamineNet.csproj -c Release -r osx-arm64 -o publish/osx-arm64 -p:UseAppHost=true
packaging/macos/create-app-dmg.sh \
publish/osx-arm64/AmphetamineNet 1.0.0 dist/AmphetamineNet-1.0.0-macos-arm64.dmg \
Assets/tray.png publish/osx-arm64/libcvdisplaylink_fix.dylib| Workflow | Purpose |
|---|---|
ci.yml |
Builds the project on every push/PR to main (macOS runner) |
macos-pack.yml |
Publishes osx-arm64 and osx-x64, builds a DMG + .sha256sum for each — runnable manually or from release.yml |
release.yml |
On a vX.Y.Z tag → runs macos-pack.yml → publishes a GitHub Release with both DMGs |
git tag v1.0.0
git push origin v1.0.0macOS note: DMGs are unsigned (no Apple Developer ID). On first launch, right-click the app → Open to bypass Gatekeeper's unidentified-developer warning.
AmphetamineNet/
App.axaml(.cs) # Avalonia lifecycle, tray icon + menu, window activation policy
Program.cs # entry point, macOS 26 CVDisplayLink relaunch workaround
Services/
AppSettings.cs # JSON-persisted user settings
Localization.cs # tray UI strings for major languages
TrayIconPainter.cs # dynamic pill tray icon (SkiaSharp)
MacKeepAwakeService.cs # IOPM assertions, clamshell state, pmset orchestration
PowerProtect.cs # one-time passwordless sudoers installation for pmset
Native/
IoKitNative.cs # IOKit / CoreFoundation P/Invoke declarations
MacAppActivation.cs # Accessory ↔ Regular NSApplication activation policy switching
cvdisplaylink_fix.c # interpose dylib source for the macOS 26 render-timer crash
ViewModels/ # MVVM view models (CommunityToolkit.Mvvm)
Views/ # custom duration prompt
MIT © Ruslan Khairulin