Releasing Task Monki
Date: 2026-08-26
Task Monki publishes one stable GitHub Release for all supported desktop
platforms. The first stable release is 0.2.0 with tag v0.2.0.
The release contains these user installers and update files:
| Platform | User installer | Update files |
|---|---|---|
| macOS 14 or newer, Apple silicon | Task-Monki-<version>-mac-arm64.dmg |
ZIP, ZIP blockmap, and latest-mac.yml |
| Windows 10 or newer, x64 | Task-Monki-<version>-win-x64.exe |
installer blockmap and latest.yml |
| x64 Linux with AppImage support | Task-Monki-<version>-linux-x86_64.AppImage |
latest-linux.yml |
Each platform also has a SHA-256 checksum file. GitHub Releases is the single download and update source. There is no update redirect or separate feed.
Trust model#
The macOS app uses this Developer ID identity:
Developer ID Application: rojhat toptamus (ZD35XP4V7D)
The workflow enables Hardened Runtime. It signs the app, submits the DMG to Apple, and records the submission ID before waiting. After Apple accepts the submission, the workflow staples and validates the DMG and app. It then creates the ZIP from the final stapled app. Gatekeeper checks run on the DMG and mounted app.
Windows releases are temporarily unsigned. The first install can show SmartScreen or Unknown Publisher. A user can select More info, then Run anyway. The updater does not require an Authenticode publisher while this policy is active. The workflow still checks the installer structure, version, x64 architecture, update metadata, blockmap, SHA-256 value, and final release asset set.
This is a deliberate temporary trade-off. An attacker who can replace release files would not need a Windows signing key. Protected tags, immutable GitHub Releases, draft-first publishing, metadata hashes, and public download checks reduce that risk. They do not provide publisher identity. Do not add a Windows certificate, signing secret, Azure signing service, or Authenticode gate for the initial release.
When Windows signing is added, keep the same NSIS updater. Configure the
publisher identity, sign the installer and application, remove
win.verifyUpdateCodeSignature: false, and add an Authenticode check. Do these
changes together.
The Linux AppImage is unsigned. Its update metadata and SHA-512 download digest detect corrupted or replaced update bytes. The GitHub Release checks protect the published asset set. They do not provide a Linux publisher signature.
Release controls#
The protected release environment holds only the macOS secrets:
MACOS_CERTIFICATE_P12MACOS_CERTIFICATE_PASSWORDAPPLE_API_KEY_P8APPLE_API_KEY_IDAPPLE_API_ISSUER_ID
Do not copy these values to repository secrets, workflow inputs, logs, or local files. The macOS job creates a temporary keychain and temporary API-key file. It removes the certificate input immediately after import. It removes the API key immediately after the Apple wait and log request. A final cleanup step also restores the original keychain search list and removes all temporary credential files.
The release environment must:
- require approval from
@RojhatToptamus; - accept only
mainand tags that matchv*; - prevent administrator approval bypass.
The repository must protect v* tags from update or deletion. Keep GitHub
immutable releases enabled. Protect main with code-owner review and dismiss
approvals after new commits. Allow only GitHub-owned Actions and require full
commit SHAs.
The workflow grants read-only repository access by default. Only the tag-only
publish job receives contents: write. Windows and Linux jobs do not use the
release environment or any signing secret.
Version rules#
package.json owns the version. A release version must have the stable
MAJOR.MINOR.PATCH form. package-lock.json must contain the same version. The
tag must be v plus that exact version.
The release commit must be on main. The workflow rejects a mismatched tag or
an existing GitHub Release. The GitHub Actions run number becomes the macOS
CFBundleVersion. The package version becomes the user-visible version on all
platforms.
Local checks#
Before release preparation is merged, run:
npm ci
npm run verify
npx vitest run scripts/verify-release-artifacts.test.mjs
git diff --checkThese checks need no release credential. Local builds must not contact Apple.
Complete dry run#
Run a manual dry run from main before the first release. Also run one after a
release-pipeline change.
- Open the Trusted desktop release workflow.
- Run it on
main. - Check the source commit in the pending
releasedeployment. - Approve the macOS job.
The dry run uses the same validation, build, signing, notarization, packaging, and internal verification jobs as production. It builds all three platforms. It does not create a tag, commit, branch, GitHub Release, or public asset.
Accept the dry run only when:
- the source and repository quality gates pass;
- macOS reports one accepted Apple submission ID;
- the DMG and mounted app pass signing, timestamp, Hardened Runtime, stapler, Gatekeeper, entitlement, architecture, and packaged-runtime checks;
- the final macOS ZIP contains the stapled signed app;
- the Windows NSIS and Linux AppImage checks pass;
- each fresh verifier downloads one exact internal artifact set;
- all checksums and update-metadata digests match;
- the publish and public-verification jobs are skipped.
Internal release artifacts expire after one day. The Apple notarization log expires after seven days.
Production release#
- Set the stable version in
package.jsonandpackage-lock.json. - Update release notes and run the local checks.
- Merge the release preparation to
main. - Confirm that all required checks pass on the intended commit.
- Create and push the matching tag.
For the first stable release:
git switch main
git pull --ff-only
git tag v0.2.0
git show --no-patch --oneline v0.2.0
git push origin v0.2.0Check the pending environment deployment before approval. The workflow creates a draft Release only after all internal checks pass. It uploads the complete asset set, downloads it again, verifies it, publishes the draft, and marks it as the latest release. Only then can installed apps discover the update.
Accept production only when:
- every dry-run acceptance condition also passes in the tag run;
- the tag still resolves to the validated
maincommit; - the Release contains exactly the twelve expected assets;
- GitHub reports the published Release as immutable;
- fresh public verifiers download without authentication;
- public checksums, metadata, architecture, and asset integrity pass;
- the public macOS DMG and ZIP pass the complete Apple trust checks.
Failure handling#
The macOS job records the Apple submission ID before it waits. If Apple rejects
the submission, download the notarization-log-* artifact. Inspect its issue
paths.
Do not replace a verified artifact. Do not move or force-push a release tag. If source or artifact bytes are wrong, fix the source and use a new version.
Do not manually add a release asset after publication. The public verifier requires the exact asset set. If publishing leaves a draft Release, inspect it, delete only that draft, and rerun the complete tag workflow. Do not delete or move the tag. A failure after publication needs investigation before a new release.
Independent public checks#
Download SHA256SUMS-<platform>.txt and the platform files from the matching
GitHub Release. Check the SHA-256 values before installation.
On macOS, also run:
version=0.2.0
dmg="Task-Monki-${version}-mac-arm64.dmg"
curl --fail --location --proto '=https' --tlsv1.2 \
--output "$dmg" \
"https://github.com/RojhatToptamus/task-monki/releases/download/v${version}/${dmg}"
hdiutil verify "$dmg"
xcrun stapler validate "$dmg"
spctl --assess --type open --context context:primary-signature --verbose=4 "$dmg"The automated public checks also verify the update metadata and the downloaded public installer on its target operating system.