niacin 1.1 — live policy reload and a calmer menu bar
niacin 1.1 is out. The headline change is for IT: managed policy now takes effect the moment your MDM pushes it, with no restart and no logout. The rest of the release is small comforts — a countdown next to the menu bar icon, an About window, a Settings window that actually opens where you’re looking, and proper unified logging when you need to figure out what the app is doing.
Live policy reload
In 1.0, applying a new managed configuration meant restarting niacin (or, more often, telling a user to). That works, but it’s the kind of friction that makes IT teams quietly stop using a tool. 1.1 watches /Library/Managed Preferences/com.oldsalt.niacin.plist directly and reacts to changes in milliseconds.
The implementation has a few sharp corners worth calling out:
- Bypasses cfprefsd. The managed-preferences daemon aggressively caches that directory and isn’t expected to re-read on direct edits — the managed domain is designed for ingestion via
mdmclientandprofiles. niacin now reads the plist directly withNSDictionary(contentsOfFile:), so a JAMF push, a profile install, or adefaults writefrom your runbook all surface immediately. - kqueue with a polling safety net. A
DispatchSourceFileSystemObjectwatcher gives near-instant reaction. A 30-second mod-date poll covers filesystems where kqueue events may not deliver. Atomic-replace deployments (cp,defaults write, JAMF) trigger.renameor.delete, and niacin re-installs the watch on the new inode. - Per-user plist precedence. A user-level managed plist takes precedence over the system-wide one, matching the standard managed-domain resolution order.
- Loud parse errors. Malformed XML now logs a clear error rather than silently falling back to the previous policy. If you typo a key, you’ll see it.
Smarter handling of running sessions
If a stay-awake session is active when policy changes, niacin no longer kills it reflexively. The session is only deactivated when the new policy is genuinely incompatible:
- The master kill switch was flipped off.
- The session is indefinite and the new policy disallows it.
- The remaining time exceeds a newly imposed
maxDurationSeconds.
Compatible changes — for example, locking down which durations appear in the menu while the user is mid-session — leave caffeinate running. Policy also re-evaluates on app focus and on wake from sleep, so a Mac that comes back from a closed lid catches up immediately.
Menu bar countdown and tooltip
During a timed session, the menu bar icon now sits next to a compact countdown: 45m, 1h 23m, 30s. Indefinite sessions show an infinity symbol. Hovering reveals a status tooltip — Inactive, Keeping you awake, or 5m remaining — so you can check on a long backup without opening the menu.
About window
There’s now a proper About window with the app icon, current version, tagline, and links to the website and GitHub repo. It also credits KeepingYouAwake, whose menu bar UX shaped the 1.0 design.
Settings opens where you’re looking
SwiftUI’s SettingsLink has a habit of opening the window on the wrong Space or behind whatever you’re currently working in. niacin now uses openSettings with an explicit NSApp.activate, so the window comes to the front on your active screen every time.
Unified logging
For IT teams troubleshooting a deployment, niacin now emits structured logs under the com.oldsalt.niacin subsystem with four categories:
policy— reload events, deactivation reasonspolicy-watcher— kqueue events with symbolic names ([write,extend],[rename],[delete]) instead of raw bitmasksmanaged-prefs— plist parse results and errorscaffeinate— subprocess lifecycle
Stream them with:
log stream --predicate 'subsystem == "com.oldsalt.niacin"' --info
Or filter by category in Console.app.
Polish
- The display name is now “Niacin” everywhere a user sees it (the Settings window title, About, the App Store listing). Bundle IDs and file paths are unchanged.
- A proper app icon set, with all sizes from 16×16 to 1024×1024.
LSApplicationCategoryTypeis now wired at the target level, so the App Store places niacin under Utilities correctly.ManagedPreferences.isManagedreads the plist files directly rather than viaCFPreferences, which would occasionally return false positives by searching the user domain.
Upgrading
The Mac App Store update will roll out as soon as Apple’s review completes. If you’re running from a signed release on GitHub, replace the app and relaunch — managed policy carries over, and the new watcher picks up your existing plist on first launch. There are no breaking changes to the MDM key surface.
Source and release notes are on GitHub at github.com/just-an-oldsalt/niacin. Bug reports and MDM key requests welcome on the issue tracker or via niacin@dort.zone.