Download installers from the GitHub Releases page. All builds are produced by GitHub Actions from tagged commits (widget-v*) in the Focusdoro source repository and published here.
The Focusdoro Widget is a desktop Pomodoro companion that mirrors timer state from the Focusdoro web app. The widget's bundle identifier is com.focusdoro.widget — this value is load-bearing for keyring entries, macOS Login Items, notification permissions, and Windows MSI upgrade behavior. Do not install another .app or installer with the same identifier from a different source.
Apple Silicon (M1/M2/M3/M4) only. Intel Mac users are out of scope for v3.0. (Intel + Rosetta is not supported because the widget's Hardened Runtime + JIT entitlement combination interacts poorly with Rosetta translation.)
Steps:
- Download
Focusdoro_Widget_<version>_aarch64.dmgfrom the release page. - Double-click the
.dmgto mount it. - Drag the
Focusdoro Widget.appto/Applications. - Eject the disk image and launch the app from Applications (Spotlight: type "Focusdoro Widget").
The .app is code-signed with a Developer ID Application certificate and notarized by Apple, so first-launch should succeed without any Gatekeeper warning. If Gatekeeper does hesitate (rare — typically only when the stapled notarization ticket fails to download), right-click the app → Open → confirm.
Each release publishes a SHA256SUMS.txt file alongside the installers. To verify the downloaded .dmg:
shasum -a 256 Focusdoro_Widget_<version>_aarch64.dmgCompare the output against the matching line in SHA256SUMS.txt. If they do not match, do not mount the .dmg — re-download or report the discrepancy.
The bundle identifier com.focusdoro.widget is what Keychain (Phase 36 OAuth/refresh token storage), tauri-plugin-autostart Login Items (Phase 39 launch-at-login), notification permissions (TCC), and the focusdoro:// deep-link scheme all key off. Do not install another .app with the same identifier from a different source — doing so will overwrite the canonical bundle and may invalidate prior keyring/permission state.
Steps:
- Download
Focusdoro_Widget_<version>_x64_en-US.msifrom the release page. - Double-click the
.msito start the installer. - Click through the WiX installer prompts. Default install location is
C:\Program Files\Focusdoro Widget\.
When Windows shows "Windows protected your PC", click "More info" → "Run anyway".
This is expected: v3.0 ships unsigned (the .msi has no Authenticode code-signing certificate attached), so SmartScreen has no publisher reputation to evaluate. v3.1 will add code signing once the EV/OV certificate procurement is decided; SmartScreen will stop warning at that point.
Known limitation: v3.0 ships unsigned by design; SmartScreen reputation requires an EV certificate which is deferred to v3.1 (tracked as WDIST-05 in .planning/REQUIREMENTS.md). Until then, users who prefer to verify before running can use the SHA256 procedure below.
Each release publishes a SHA256SUMS.txt file alongside the installers. To verify the downloaded .msi matches the published hash, open PowerShell and run:
Get-FileHash .\Focusdoro_Widget_<version>_x64_en-US.msi -Algorithm SHA256Compare the output against the matching line in SHA256SUMS.txt. If they do not match, do not run the installer — re-download or report the discrepancy.
If upgrading from v1.0.0 to v1.1.0+ leaves you with two entries in Add/Remove Programs ("Focusdoro Widget" listed twice with different versions), uninstall the older one manually. This is a one-time WiX UpgradeCode-stability issue resolved by v1.1.0+; subsequent upgrades will replace in place. The widget's WiX UpgradeCode UUID is pinned (see apps/widget/src-tauri/tauri.conf.json bundle.windows.wix.upgradeCode) and MUST NOT change in any future release — changing it breaks in-place upgrades for every existing Windows user.
Two formats are published per release: a .deb package for Debian/Ubuntu-family distributions, and an .AppImage for everything else.
wget https://github.com/code-with-ahsan/focusdoro-widget/releases/download/widget-v<version>/Focusdoro_Widget_<version>_amd64.deb
sudo dpkg -i Focusdoro_Widget_<version>_amd64.debIf dpkg reports missing dependencies (typically libayatana-appindicator3-1 on a fresh system without the universe/contrib repos enabled), run:
sudo apt --fix-broken installThis will pull the missing runtime libraries from your distribution's package archive and complete the install.
Supported distributions (verified to ship libayatana-appindicator3-1 in their default archives):
- Ubuntu 22.04 LTS (Jammy Jellyfish)
- Ubuntu 24.04 LTS (Noble Numbat)
- Debian 12 (Bookworm)
- Linux Mint 21+
- Pop!_OS 22.04+
Older or unsupported distributions — Ubuntu 20.04 (Focal Fossa) and Debian 11 (Bullseye) ship the older libappindicator3-1 (note: no -ayatana- prefix) instead of libayatana-appindicator3-1. The .deb's Depends: declaration will fail there. Use the .AppImage instead.
wget https://github.com/code-with-ahsan/focusdoro-widget/releases/download/widget-v<version>/Focusdoro_Widget_<version>_amd64.AppImage
chmod +x Focusdoro_Widget_<version>_amd64.AppImage
./Focusdoro_Widget_<version>_amd64.AppImageNo install is required — the AppImage is a self-contained, double-clickable bundle (once the executable bit is set). It runs on any glibc-2.31+ distribution with FUSE available.
Each release publishes a SHA256SUMS.txt file alongside the installers. To verify the downloaded .deb or .AppImage:
sha256sum Focusdoro_Widget_<version>_amd64.deb
sha256sum Focusdoro_Widget_<version>_amd64.AppImageCompare each output against the matching line in SHA256SUMS.txt. If they do not match, do not install — re-download or report the discrepancy.
After the first CI-signed .dmg is published, run these two smokes against the signed bundle. They cannot be exercised against dev binaries because dev binaries lack bundle identity — tauri-plugin-autostart Login Item registration and macOS TCC permission attribution both key off the signed bundle's notarized identity, not the unsigned dev binary's path.
These are the Phase 39 deferred smokes (D-15 reboot + D-09 tccutil) folded into Phase 40 per D-22.
- Install the signed
.appto/Applications(drag from the mounted.dmg). - Launch the app and sign in.
- Open the settings panel and enable the "Launch at login" toggle.
- Reboot the Mac.
- After login completes, verify that:
- The tray icon appears in the menu bar within ~5 seconds of login.
- The login window does NOT appear (the autostart path must hide the login window per the Phase 39 D-15 contract).
If the login window appears on the autostart path, the bundled tauri-plugin-autostart is not invoking the silent-launch flag correctly against the signed bundle — capture as a Phase 40 blocker.
-
Open Terminal.app and run:
tccutil reset Notifications com.focusdoro.widget
-
Relaunch the Focusdoro Widget app.
-
In the settings panel, toggle Notifications OFF, then back ON.
-
Verify that the macOS notification permission prompt fires.
-
Click Allow.
-
Verify that the cached permission state inside the app refreshes (no app restart required — the next test notification fires immediately).
If the prompt does not fire after the tccutil reset, TCC is failing to attribute the permission request to the signed bundle's com.focusdoro.widget identity — capture as a Phase 40 blocker.
Both smokes are captured pass/fail in the Plan 05 verification checkpoint task. Cross-reference: .planning/phases/40-distribution-signing-ci/40-05-PLAN.md.
This section is operational documentation for the maintainer. End users do not need to read it.
com.focusdoro.widget MUST NOT change. Changing it invalidates:
- Keyring entries (Phase 36 WAUTH-03 — Google OAuth refresh tokens, magic-link state)
- macOS Login Items (Phase 39 D-12..D-15 —
tauri-plugin-autostartregistration) - TCC permissions (Phase 39 D-09..D-11 — notification grants)
focusdoro://deep-link scheme registration (Phase 36 D-08)- Windows MSI UpgradeCode derivation (relative-to-identifier, used as a stable upgrade key)
This is the single most important invariant in the codebase. Phase-checker grep gates assert it on every tauri.conf.json diff.
The UUID in apps/widget/src-tauri/tauri.conf.json bundle.windows.wix.upgradeCode MUST NOT change after v1.0.0 ships. Current value (set in Phase 40 Plan 01): 429BA08E-7BB9-4280-821D-72125C60E290.
Changing this UUID will:
- Break in-place upgrades for every existing Windows user. The new
.msiwill install as a parallel product instead of upgrading; the user will see two Add/Remove Programs entries and have to uninstall the older one manually. - Reset all Windows user state that the WiX installer migrates (Start Menu shortcuts, file associations if any).
The UpgradeCode is load-bearing forever. Do not regenerate it for any reason.
To rotate the App Store Connect API key (.p8):
-
Revoke the existing key. In App Store Connect → Users and Access → Integrations → Team Keys, locate the existing key and click Revoke.
-
Generate a new key. Click Generate API Key, name it (e.g.
focusdoro-ci-2026Q2), grant Developer role (sufficient for notarization), and click Generate. Download the.p8file. You can only download the.p8ONCE — it cannot be re-downloaded. Save it securely outside the repo. -
Base64-encode the .p8 locally. On macOS:
base64 -i AuthKey_<NEW_KEY_ID>.p8 | pbcopy
On Linux:
base64 -w 0 AuthKey_<NEW_KEY_ID>.p8
(The
-w 0flag prevents line wrapping; the resulting base64 is a single line ready to paste into a GitHub Secret.) -
Update GitHub Secrets. In the repo's Settings → Secrets and variables → Actions:
- Update
APPLE_API_KEYto the new Key ID string (10-character alphanumeric shown next to the new key, e.g.A1B2C3D4E5). - Update
APPLE_API_KEY_BASE64to the base64 contents you copied above. - Leave
APPLE_API_ISSUERunchanged (it is per-team, not per-key).
- Update
-
Push the next
widget-v*tag. CI will use the new key automatically — no code change required.
If the previous .p8 file is still on disk, securely delete it (rm -P AuthKey_<OLD>.p8 on macOS, shred -u AuthKey_<OLD>.p8 on Linux).
Per CONTEXT.md D-18 ERRATA + ERRATA-2:
APPLE_CERTIFICATE— base64 of the Developer ID Application.p12APPLE_CERTIFICATE_PASSWORD— password for the.p12APPLE_SIGNING_IDENTITY— full identity string, e.g.Developer ID Application: <Name> (TEAMID)APPLE_API_KEY— App Store Connect API Key ID string (10-character alphanumeric)APPLE_API_ISSUER— App Store Connect Issuer UUIDAPPLE_API_KEY_BASE64— base64 of theAuthKey_<KEYID>.p8file contentsGOOGLE_DESKTOP_OAUTH_CLIENT_ID— Phase 36 OAuth client ID (build-time inject)GOOGLE_DESKTOP_OAUTH_CLIENT_SECRET— Phase 36 OAuth client secret (build-time inject)FIREBASE_WEB_API_KEY— Phase 36 magic-link Web API key (build-time inject)FIREBASE_DATABASE_URL— Phase 37 RTDB base URL (build-time inject viaoption_env!inapps/widget/src-tauri/src/timer/rtdb.rs). Per D-18 ERRATA-2.
Optional 10+1: APPLE_TEAM_ID (belt-and-suspenders). tauri-action infers it from the APPLE_SIGNING_IDENTITY suffix (TEAMID) when absent; the explicit secret avoids parsing assumptions but its absence is fine.
All 10 mandatory secrets are verified by the Plan 04 pre-flight checkpoint task.
Last revised: 2026-06-04 (Phase 40 Plan 03).