Skip to content

fix(worker): verify the release binary before replacing the running one - #441

Open
KillerX wants to merge 1 commit into
fix/activity-counter-on-activity-workerfrom
fix/verify-selfupdate-checksum
Open

fix(worker): verify the release binary before replacing the running one#441
KillerX wants to merge 1 commit into
fix/activity-counter-on-activity-workerfrom
fix/verify-selfupdate-checksum

Conversation

@KillerX

@KillerX KillerX commented Aug 12, 2026

Copy link
Copy Markdown
Member

2/n of a stack. Base: fix/activity-counter-on-activity-worker (#440).

The worker polled GitHub every five minutes and installed whatever it found, with no selfupdate.Validator: no checksum, no signature, no size check. A truncated download, a proxy that served the wrong bytes, or a partially uploaded release asset all became the running binary on the whole fleet within minutes.

publish.yml now writes <binary>.sha256 next to each matrix artifact and attaches it to the release, and the worker builds its updater with selfupdate.SHAValidator. That makes the update fail closed: if the checksum asset is missing or does not match, nothing is installed and the error is logged. Both changes have to ship together — the first release built after this carries the file the new binary demands.

What this does not do is authenticate the release. The hash is produced by the same job, in the same workflow, and attached to the same release, so anyone who can write the binary can write the hash. Closing that needs a signature checked against a key the pipeline cannot mint — an ECDSA keypair with the public half compiled into the worker and the private half a repository secret. That needs a key generated out of band, so it is left as a follow-up rather than done halfway here. Happy to do it as the next branch if you generate the keypair.

Tests pin the one thing that silently breaks: that the name publish.yml uploads is the name the validator looks for, and that the two-column output of sha256sum is a form the validator accepts — BINARY_NAME contains a slash, so the second column is a path.

🤖 Generated with Claude Code

The worker polled GitHub every five minutes and installed whatever it found,
with no selfupdate.Validator: no checksum, no signature, no size check. A
truncated download, a proxy that served the wrong bytes, or a partially
uploaded release asset all became the running binary on the whole fleet within
minutes.

publish.yml now writes <binary>.sha256 next to each matrix artifact and
attaches it to the release, and the worker builds its updater with
selfupdate.SHAValidator. That makes the update fail closed: if the checksum
asset is missing or does not match, nothing is installed and the error is
logged. Both changes have to ship together — the first release built after
this carries the file the new binary demands.

What this does not do is authenticate the release. The hash is produced by the
same job, in the same workflow, and attached to the same release, so anyone who
can write the binary can write the hash. Closing that needs a signature checked
against a key the pipeline cannot mint — an ECDSA keypair with the public half
compiled into the worker and the private half a repository secret. That needs a
key generated out of band, so it is left as a follow-up rather than done
halfway here.

Tests pin the one thing that silently breaks: that the name publish.yml uploads
is the name the validator looks for, and that the two-column output of
sha256sum is a form the validator accepts — BINARY_NAME contains a slash, so
the second column is a path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant