Repository navigation
Self Updater Guide
TrayIconGuard updates itself from a JSON feed hosted in its own Git repository: the program
fetches the feed over HTTPS, compares versions, and — only after the user confirms —
downloads the new executable, verifies its SHA-256 against the feed's pin, replaces itself
through a rename chain and relaunches. This page documents the contract (Updater_SKILL.MD
in the repository), the security properties, the failure matrix, an end-to-end test recipe,
and one provenance caveat every release engineer should know about the 8.4.0 publication.
- What the program does at update time
- The feed file
- Publication checklist
- Security properties
- Failure matrix
- End-to-end test on a loopback feed
- Provenance caveat: the 8.4.0 publication (resolved)
- See also
- Resolve the feed URL:
settings.updates.feedUrlif set, else the built-in defaulthttps://raw.githubusercontent.com/paulmann/windows-show-all-tray-icons/main/update/stable.json. The allow-list acceptshttps://URLs plus plain HTTP only for127.0.0.1/localhost(local QA servers). -
GETthe feed with WinHTTP (user agentTrayIconGuard/<version>; resolve/connect 8 s, send 10 s, receive 60 s timeouts). - Parse and validate:
version,urland a 64-hexsha256are mandatory; missing or malformed fields fail the check withfeed missing version/url/sha256(64 hex). - Compare
versionwith the running version (4-component numeric compare,vprefix tolerated). Not newer → “up to date”. - On confirmation (GUI) download
urlto%TEMP%\TrayIconGuard-update.exe. - SHA-256 the download with BCrypt; mismatch → delete the file and report Update rejected: checksum mismatch.
- Apply: rename the running exe to
TrayIconGuard.exe.old(renaming a running image is permitted), move the new file into place, schedule.oldfor deletion at reboot (MOVEFILE_DELAY_UNTIL_REBOOT), relaunch and exit. A failed move rolls the.oldfile back into place. - The opt-in automatic check (About toggle) runs at most once per 24 h, shows a balloon only, and never downloads or applies anything by itself.
The legacy path — if the feed cannot be fetched or parsed — probes the GitHub API
releases/latest endpoint and compares tag names; it can tell you that something newer
exists but cannot pin hashes, so applying from that path is not offered.
update/stable.json at the repository root (served through raw.githubusercontent.com);
update/beta.json for a beta channel, selected only via the feedUrl override:
{
"schema": 1,
"channel": "stable",
"version": "8.6.3",
"released": "2026-10-16",
"minBuild": 15063,
"url": "https://github.com/paulmann/windows-show-all-tray-icons/releases/download/v8.6.3/TrayIconGuard.exe",
"sha256": "10ea493785629761be0430baff3288f474b47d3720201c2197a1b6fa5bc5fc03",
"zipUrl": "https://github.com/paulmann/windows-show-all-tray-icons/releases/download/v8.6.3/TrayIconGuard-8.6.3-win-x64.zip",
"zipSha256": "4fadcc253d259974728583202b5e0c5fd3b220375cc3e077b6734713e9028436",
"notes": "One paragraph shown in About → update panel."
}Rules from the contract: versions must strictly increase; sha256 must match the
byte-identical served executable (compute it on the final stripped — and signed, if you
sign — binary); binaries belong in release assets, not raw git URLs; published feeds are
never rewritten, a new version is published instead.
From Updater_SKILL.MD §3, annotated with what the 8.4.0 release actually did (see the
caveat below):
-
./scripts/build.sh— gates: zero warnings + manifest well-formedness. - Bump the version in one place —
src/core/version.h— then mirror toresources/resources.rc,resources/app.manifest,src/core/installer.cpp(DisplayVersion) andscripts/package.sh(VER). -
./scripts/package.sh→ zip +SHA256SUMS. - Write
update/stable.jsonwith the new hashes and notes. - Commit, tag
v<ver>, push; create the GitHub release with the three assets named exactly as the feed URLs. - Verify:
--check-updateprintsnewer=yesbefore andnewer=noafter updating. - Optional loopback E2E (below).
- HTTPS-only feeds and downloads; loopback HTTP solely for QA.
- Mandatory SHA-256 pin; no apply without an exact match.
- No silent auto-apply: a confirmation dialog always precedes download+apply in the GUI;
the CLI verb
--update-nowis an explicit operator action. - No telemetry: the only network requests are the feed GET and the binary GET.
- Rename-chain apply with rollback on move failure; old image deleted at reboot.
Important
Understand the trust boundary: the SHA-256 pin proves the download matches the feed, and TLS proves both came from the feed's host — it does not authenticate the publisher beyond that, because there is no code signature and no signed manifest. Anyone who can publish to the feed URL can publish a matching hash for their own binary. For fleets, the documented answer is code-signing the executable and re-hashing the feed afterwards.
| Symptom | Cause | Action |
|---|---|---|
update check failed: HTTP 404 |
feed path/branch wrong | check update/stable.json exists on the default branch |
sha256 mismatch (feed=… file=…) |
exe re-uploaded or re-signed after the feed was written | republish the feed with the hash of the served bytes |
only https (or loopback http for tests) feeds are allowed |
non-loopback http feed URL | use https |
cannot rename running exe (…) |
AV/EDR holding the file | retry; add an exclusion for the folder |
| update applied but old UI still running | relaunch blocked | start manually; .old disappears at reboot |
bad feed JSON: … |
malformed feed | fix JSON; the client also falls back to the releases probe for the check |
# serve a folder containing stable.json (loopback url) + the candidate exe
python3 -m http.server 8000
# point one installation at it (settings.json): "updates": { "feedUrl": "http://127.0.0.1:8000/stable.json" }
TrayIconGuard.exe --update-now # downloads, verifies, applies, relaunches
TrayIconGuard.exe --check-update # now prints newer=noThe repository's v8.3.0 development log records this exact loop verified under Wine
(download → sha → rename → .old → relaunch, exit 0).
The published v8.4.0 assets do not match what the tagged source would produce:
- the
v8.4.0tag changed onlyupdate/stable.json(plus script execute bits) relative tov8.3.0;src/core/version.hat the tag still reads 8.3.0; - the released executable's version resource says 8.4.0.0 and its
--versionprints 8.4.0, so it was built from a tree whose version bump was never pushed; - its version resource copyright still says MIT although the repository license changed to PolyForm Noncommercial at 8.3.0;
- the feed's release notes advertise features (all-users install scope, About License/Wiki buttons) that exist neither in the published source nor in the released binary's strings.
Practical consequence for operators: updating an 8.3.0 installation to the feed's 8.4.0 is
safe and terminates (the delivered binary reports 8.4.0, so the next check says
newer=no); but a build produced from the public tag reports 8.3.0 and would keep offering
the same update. Release engineers should treat checklist step 2 as mandatory and commit
the bump with the tag. Full drift table: Changelog and Versioning.
Resolution (2026-10-16). The gap was closed: the missing 8.4.0 sources were restored and
published as v8.4.1, and 8.6.0 and everything after it followed from the repository itself. Since 8.6.0 the CI
release job uploads the assets and runs scripts/make_feed.py, which pins
update/stable.json from the bytes the release actually serves — so the feed can no longer
disagree with the assets. The obsolete 8.3.0/8.4.0/8.4.1/8.6.0/8.6.1 releases and tags were removed
on 2026-10-16; v8.6.3 is the only 8.x release, and the legacy v6.2 / v7.0.3 releases remain as
history for the script editions (Legacy Script Editions).
- Security and Privacy — the wider threat model
-
Command-Line Interface —
--check-update,--update-now - Changelog and Versioning — feed history and version drift
- Installation and Deployment — signing guidance for fleets
TrayIconGuard · feed 8.6.3 / source tree 8.6.3 (see Changelog and Versioning) · PolyForm Noncommercial 1.0.0 · Repository · Report an issue · Home · Glossary · FAQ · Troubleshooting
Every claim in this wiki is traced to a source line at commit 124e849 or reproduced by
executing the program (build + Wine battery, 2026-10-16); measured discrepancies between
documentation, source and shipped binaries are documented, not smoothed over.
Home Getting Started Features Overview Comparison with Alternatives
Installation and Deployment Configuration Reference Command-Line Interface
How Tray Visibility Works Legacy Script Editions The Guard Watcher
GUI Pages Guide Backups and Snapshots Localization and Themes Self-Updater Guide Security and Privacy Troubleshooting
Architecture Roadmap and Ideas Independent-Audits Testing and QA Building from Source Changelog and Versioning FAQ Glossary
8.6.3 feed / 8.6.3 source · PolyForm Noncommercial 1.0.0