v1.3.4 - Notification Permission Told Honestly
🚀 Notihub v1.3.4
Release Title: v1.3.4 - Notification Permission Told Honestly
🎯 What this fixes
Setup said "✓ Allowed" whether or not macOS had actually allowed anything. If you ever saw the permission step confirm itself while notifications never arrived, that checkmark was the bug — not a symptom of one.
🔍 The bug
The permission step showed a test notification and then waited 2.5 seconds:
// src/main.js — the old ofa:test-notification handler
setTimeout(() => {
if (!cfg.notificationsOk) patchConfig({ notificationsOk: true });
reply({ ok: true, fallback: true });
}, 2500);Silence was treated as success. When macOS refuses, it doesn't reply — no banner appears and, often, no error event fires either. Nothing came back, the timer expired, and the app wrote down "allowed".
Three different situations produced that identical checkmark:
- Permission genuinely denied — macOS drops the banner silently.
- The system authorization alert was still on screen. 2.5 s routinely expired before you had clicked anything, so the app declared success on a dialog you hadn't answered yet.
- macOS explicitly said no.
patchConfigsat outside the handler'srepliedguard, so even after a realfailedevent the timer still flippednotificationsOktotrue2.5 s later. The UI showed "macOS refused" for a moment, and every visit after that showed "✓ Allowed".
Once set, nothing ever unset it. The one place that learned the truth — a real notification failing — only wrote to console.error:
// src/main.js — the old notify()
n.on('failed', (_e, err) => {
dbg(...); console.error(...); // and that was all
});So turning notifications off in System Settings left the app claiming permission indefinitely.
🛠️ How it works now
The rule is that only macOS gets to say yes, and a timer expiring is not macOS saying anything. The probe is a small state machine in the new src/permissions.js with four outcomes instead of one boolean:
| Status | Meaning | Written to disk |
|---|---|---|
granted |
a banner actually rendered | true |
denied |
macOS refused | false |
pending |
asked, still waiting | — |
unverified |
the wait elapsed with no verdict | nothing |
unverified is the state that used to be reported as success. It now says so plainly, in amber, next to a button that opens System Settings — because "we heard nothing" is genuinely not the same as "you're all set".
Three consequences:
- The wait is 9 s, not 2.5 s, and it no longer concludes anything. It only changes the wording, and the probe stays open behind it — answer the macOS alert at 20 seconds and you still get a correct
granted. - Real notifications keep the verdict honest. Every delivery reports back: one that shows confirms permission, one that fails revokes it. Turn notifications off next month and the app notices on the next message instead of insisting it has permission.
- The stored flag is never written from a guess. Only a real
showorfailedmoves it.
The setup window never blocks
Pressing Allow is fire-and-forget. Nothing is disabled, nothing is modal, and Continue and Skip for now stay clickable in every one of the five states — including while a probe is in flight. A probe that never resolves leaves a warning behind and nothing else.
🔒 Also fixed: web permissions could answer two different ways
Only setPermissionRequestHandler was installed. Chromium checks a permission before it requests one, and those go through separate handlers — so the unset check handler fell through to Electron's permissive default. navigator.permissions.query({name: 'geolocation'}) reported granted from that default, while the request handler stood ready to deny it. Sites got a yes, called the API, and got a no.
Both handlers now share one predicate, so they cannot disagree. Camera, microphone, screen capture, location, clipboard read, MIDI, USB, serial, HID and file-system access are all denied — this is a notification relay and none of it needs that hardware.
clipboard-sanitized-write is now explicitly allowed. Chromium checks it without ever requesting it, so it had been permitted implicitly by that same default; adding a check handler without listing it would have quietly broken every "Copy link" button in Slack, Messenger and Gmail. It permits writing sanitized content only, never reading.
Smaller fix: the global shortcut registered at startup ignored its return value, so a hotkey another app already owned failed silently. It now reports the conflict.
✅ How this was verified
npm test: 80 reported checks, including a new 26-check permission suite. It covers the exact failure shape —start → failed → timeoutmust staydenied, and a timeout must not write the stored flag whether it was previouslytrueorfalse. Two of the checks readsrc/main.jsitself, because the web-permission bug was an omission: no unit test of a module can notice a handler that was never installed.- A real notification through the packaged app, confirming
notification SHOWNreaches the new code path and that an already-correct stored value is left alone rather than rewritten on every message. - All five UI states driven through the real
setup.html, checking the button, the status line, the hint, and — in every state — that Continue and Skip are still usable.
📦 Download Assets & Checksums
| Asset File | Format | SHA-256 Checksum |
|---|---|---|
notihub-v1.3.4-mac.dmg |
Native macOS DMG Installer | 1fea8163383193fe8bc71b260a11c42364a38291f4b96215b2ff41210efd4af9 |
notihub-v1.3.4-mac.zip |
Portable Zip Archive | db7073a231ca649060e434368014a4b017f4f6a546928a64d5fb8880d03541b0 |
These match what GitHub publishes as the digest field on each asset — the same value src/updater.js verifies the download against before installing.
📥 Installation Options
Method 1: Homebrew (Recommended)
brew tap thuong180702/notihub
brew install --cask notihubMethod 2: npm (CLI companion)
npm install -g @thuong180702/notihubMethod 3: DMG Installer
- Download
notihub-v1.3.4-mac.dmgbelow. - Double click to open the DMG, then drag
notihub.appintoApplications.
Method 4: Direct Zip Download
- Download
notihub-v1.3.4-mac.zip. - Unzip and move
notihub.appinto/Applications.
⬆️ Upgrading
Use Settings → Download & Install. No bundle identifier or data-directory change, so existing logins stay intact.
After upgrading, check the permission row in Settings. If it now shows amber or red where it used to show a checkmark, nothing broke in this release — that is the real state of the permission, finally being reported. Press Try again, or use Open notification settings to turn it on.
⚠️ Gatekeeper Notice
Notihub is signed ad-hoc, not notarized by Apple, so macOS will refuse a manual install with "notihub is damaged and can't be opened". This isn't corruption — it's Gatekeeper rejecting an unnotarized app. Clear the quarantine flag once:
xattr -dr com.apple.quarantine /Applications/notihub.appUpdates Notihub installs itself are never quarantined, so this is only needed for a fresh manual install.