Skip to content

docs(code-signing): add SignPath Foundation code-signing policy page#3366

Merged
max-sixty merged 4 commits into
mainfrom
feat/signpath-code-signing-28724277808
Jul 9, 2026
Merged

docs(code-signing): add SignPath Foundation code-signing policy page#3366
max-sixty merged 4 commits into
mainfrom
feat/signpath-code-signing-28724277808

Conversation

@worktrunk-bot

@worktrunk-bot worktrunk-bot commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Addresses the first, gating step of option #1 (SignPath Foundation) from #3355 — the fix for the Trojan:Win32/Wacatac.B!ml false positive on the unsigned Windows binary.

What this PR does

Adds a code signing policy page at worktrunk.dev/code-signing/. A published policy page is a hard eligibility requirement for the SignPath Foundation free OSS code-signing program, so it's the natural thing to land first — the application can't proceed without it. The page documents:

  • Certificate provenance — the required attribution ("Free code signing provided by SignPath.io, certificate by SignPath Foundation") and the HSM-held key.
  • What's signedgit-wt.exe in the Windows release archive / winget; nothing else.
  • The build+sign pipeline — tag-triggered, cargo-dist on GitHub runners, SignPath action for signing, all reproducible from public source.
  • Project roles (Author / Reviewer / Approver), per-release manual approval, and the no-telemetry privacy stance SignPath asks projects to state.

The generated skill mirror (skills/worktrunk/reference/code-signing.md) and docs/static/llms.txt entry are produced by test_docs_are_in_sync — not hand-edited.

Roster check: the roles table lists @max-sixty as Author/Reviewer/Approver. Adjust if anyone else should hold signing authority before this is submitted to SignPath.

What this PR deliberately leaves out

The release.yaml signing job and the SignPath account are a follow-up, for two reasons:

  1. The concrete slugs don't exist yet. The SignPath action needs organization-id, project-slug, and signing-policy-slug — all issued after the project is registered and approved.
  2. cargo-dist doesn't sign natively, and the wiring is checksum-sensitive. cargo-dist generates a per-artifact .sha256 in the local build job, alongside the .zip. Signing the binary afterward means the signed .zip no longer matches its checksum, so a correct job has to sign → re-zip → regenerate the .sha256 → overwrite the artifact before the host job uploads it. I don't want to ship that against a live release pipeline without being able to run it end-to-end (it only runs on a version-tag push), so it belongs in a follow-up gated behind the real SignPath config.

I've laid out the exact maintainer steps and the proposed job shape in a comment on #3355.

Verification

  • cargo test --test integration test_docs_are_in_sync — green (regenerates + validates the skill mirror, llms.txt, and Zola link transformation).
  • zola build + lychee link check run in CI (check-docs); neither tool is installed in the tend sandbox.

Closes nothing on its own — #3355 stays open until signing is live.

Follow-up commits on this branch

  • docs/static/code-signing.md companion symlinkskills/worktrunk/reference/code-signing.md, matching every existing page, so the worktrunk.dev/code-signing.md entry in llms.txt resolves once deployed. Also swaps a dead opensource.axo.dev/cargo-dist link for the repo's axodotdev.github.io/cargo-dist convention.
  • .config/lychee.toml exclusion for the self-referential worktrunk.dev/<page>.md links in llms.txt. Those point at the live site, so a brand-new page's entry always 404s in the link check until the site is deployed — a bootstrap gap every new-docs-page PR hits. Their existence is already guaranteed by test_docs_are_in_sync, and prose/template links use the trailing-slash form (worktrunk.dev/faq/), so the exclusion is scoped to the llms.txt index entries only.

Publishes a code-signing policy at worktrunk.dev/code-signing/, the gating
eligibility requirement for the SignPath Foundation free OSS code-signing
program (#3355). Documents the certificate provenance, the tag-triggered
build+sign pipeline, project roles, per-release manual approval, and the
no-telemetry privacy stance.

The release.yaml signing job and the SignPath account setup are a follow-up
that depends on the maintainer registering the project (see the issue thread).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add docs/static/code-signing.md → skills/worktrunk/reference/code-signing.md
  symlink so the worktrunk.dev/code-signing.md companion URL in llms.txt
  resolves once deployed (matches every other page's companion).
- Replace dead opensource.axo.dev/cargo-dist/ link with the repo's own
  convention (axodotdev.github.io/cargo-dist, per release.yaml).

Co-Authored-By: Claude <noreply@anthropic.com>

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing worth settling before this goes to SignPath: the "What is signed" section names only git-wt.exe, but the x86_64-pc-windows-msvc archive (and thus the winget package) ships both wt.exe and git-wt.exe. If the follow-up signing job covers only git-wt.exe, the primary wt.exe stays unsigned and the #3355 false positive persists for anyone who runs wt. Inline note with the detail. Otherwise the page reads accurately — the file/workflow links, the SignPath attribution wording, and the @/faq.md internal-link syntax all check out.

Comment thread docs/content/code-signing.md Outdated
New docs pages 404 in the link check until the site deploys, because
llms.txt points at the live worktrunk.dev. Their existence is already
guaranteed by test_docs_are_in_sync; exclude the .md-suffixed index
entries (prose uses the trailing-slash form) so a new-page PR is green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@max-sixty

Copy link
Copy Markdown
Owner

yes wt and git-wt

should we add worktrunk-bot to the signers too?

The x86_64-pc-windows-msvc archive ships both binaries, so the policy's
"What is signed" list and "Why signing matters" intro now name both;
signing only git-wt.exe would leave the primary wt.exe unsigned and the
#3355 false positive live for anyone invoking wt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@worktrunk-bot

Copy link
Copy Markdown
Collaborator Author

Done — pushed cbd0e93 covering both wt.exe and git-wt.exe in the "What is signed" list and the "Why signing matters" intro. Mirror + llms.txt regenerated; test_docs_are_in_sync green.

should we add worktrunk-bot to the signers too?

My take: no — at least not to Approver, and probably not to any SignPath role.

The three roles map to SignPath's trust model, and the Approver role is specifically the human gate this policy is built around — the page states "signing is never fully automated" and "every signing request requires manual approval by an Approver." A bot account authorizing signing requests would quietly turn that into full automation, which is the one property SignPath's Foundation program asks OSS projects not to give up. It also can't meaningfully satisfy the "MFA on GitHub and SignPath accounts" line the roles table commits to.

Author/Reviewer are more defensible in principle (the bot does open PRs and review), but SignPath issues these roles to people who vouch for what gets released, and listing a non-human alongside you muddies who's actually accountable for a signed artifact. I'd keep the roster human — you as Author/Reviewer/Approver — and treat worktrunk-bot as a contributor that lands work through the reviewed pipeline, not as a signer of it.

If you'd rather list it anyway (e.g. as a co-Author for transparency), that's a reasonable maintainer call — just say the word and I'll add a row. I'd still leave Approver human-only.

@max-sixty max-sixty merged commit 29fa13d into main Jul 9, 2026
36 checks passed
@max-sixty max-sixty deleted the feat/signpath-code-signing-28724277808 branch July 9, 2026 19:13
@max-sixty

Copy link
Copy Markdown
Owner

The three roles map to SignPath's trust model, and the Approver role is specifically the human gate this policy is built around — the page states "signing is never fully automated" and "every signing request requires manual approval by an Approver." A bot account authorizing signing requests would quietly turn that into full automation, which is the one property SignPath's Foundation program asks OSS projects not to give up. It also can't meaningfully satisfy the "MFA on GitHub and SignPath accounts" line the roles table commits to.

But then I have to manually click around each release? How does this work?

@worktrunk-bot

Copy link
Copy Markdown
Collaborator Author

Good question — and the honest answer is: yes, it's one click per release, but it's genuinely one click, not "clicking around." SignPath's flow is designed to make that click cheap:

  1. Your tagged release workflow submits wt.exe / git-wt.exe to SignPath via the signing-request action. The signing step blocks waiting for approval.
  2. SignPath emails you (the Approver) that a request is pending — the email names the project, signing policy, and artifact.
  3. You open the request in the web console, see the build's origin metadata (repo, the vX.Y.Z tag/commit, the exact Actions run), and click Approve.
  4. The workflow unblocks, gets the signed binary back, and finishes the release.

So per release it's: one email → glance → one button. Releases here aren't frequent, so in practice that's a few clicks a month.

The part that makes this safe and low-effort

The reason it isn't a forensic chore is trusted build systems / origin verification. SignPath receives the build's origin directly from GitHub (repo, ref, workflow, commit) — provided by GitHub, not the build script, so it can't be forged. A signing policy can then require that a request came from a version-tag push on max-sixty/worktrunk before it's even eligible for signing. That's what does the "was this really built from the tagged commit?" check automatically — your click is just the final go/no-go, not manual verification.

If the click ever annoys you, there's a dial

Approval is configured per signing policy, so you have a spectrum:

  • Manual approval (what the policy page commits to now) — one click per release; keeps a human gate, which is the property the Foundation program values.
  • Origin-verified auto-approval — no click at all, but SignPath still hard-enforces "only tagged builds from this repo get signed" via that unforgeable origin metadata. Safer than fully-open automation, but it does drop the human from the loop.

My recommendation: start with manual approval. It's one click, it costs you almost nothing at this release cadence, and it's the exact thing that lets the policy page truthfully say "signing is never fully automated" — which is what keeps worktrunk-bot (or any CI) from ever being able to sign on its own. If a click-per-release ever becomes friction, origin-verified auto-approval is the middle ground you can move to later without giving up the "only real tagged builds" guarantee — and that's a one-line policy change on SignPath's side, not a code change here.

Sources: Signing Code · Trusted Build Systems (GitHub) · Foundation OSS terms

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.

3 participants