Add CLI identity-token acquisition for keyless skill push - #6390
Merged
Merged
Conversation
samuv
requested review from
ChrisJBurns,
JAORMX,
amirejaz,
aponcedeleonch,
jhrozek,
rdimitrov,
reyortiz3 and
tgrunnagle
as code owners
August 19, 2026 16:29
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6390 +/- ##
==========================================
+ Coverage 77.75% 77.77% +0.02%
==========================================
Files 750 754 +4
Lines 72601 72672 +71
==========================================
+ Hits 56448 56519 +71
Misses 16148 16148
Partials 5 5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
JAORMX
previously approved these changes
Aug 19, 2026
29 tasks
Keyless push signing (#6307) is meant to be the default, not a flag to opt into: a bare `thv skill push` should sign automatically in CI and after a browser sign-in locally, never silently unsigned. Add --identity-token (raw token or a file path, cosign parity) and a pkg/skills/identitytoken acquisition ladder: an explicit flag value, then a GitHub Actions ambient OIDC token, then a TTY-gated interactive browser sign-in against the public-good Sigstore OAuth instance. Exhausting the ladder fails with an actionable, unsigned-safe error before any push request is made. Signed-off-by: Samuele Verzi <samu@stacklok.com>
samuv
force-pushed
the
skills-keyless/03-cli-token-acquisition
branch
from
August 20, 2026 13:36
83b471b to
381dd18
Compare
10 tasks
jhrozek
approved these changes
Aug 21, 2026
15 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #6385. #6307 (keyless push signing) makes keyless the default
signing path, not something to opt into per push — but until now nothing
gave
thv skill pusha way to actually obtain an identity token, soPushOptions.IdentityToken(added in #6385) had no way to become non-empty.--identity-tokentothv skill push: accepts a raw OIDC token or apath to a file containing one (cosign parity with
--key).pkg/skills/identitytokenpackage implements the acquisition ladderwhen the flag is absent and neither
--keynor--no-signwas given:ACTIONS_ID_TOKEN_REQUEST_URL/_TOKEN, scoped to thesigstoreaudience) — the CI case.browser sign-in against the public-good Sigstore OAuth instance
(
oauth2.sigstore.dev, viasigstore/sigstore/pkg/oauthflow— alreadya direct dependency).
signing choices. Never silently unsigned.
--identity-tokenis always resolved and forwarded, evenalongside
--key— the conflict is the server's to reject(
skillsvc.validateSigningInputs, from Plumb an identity token through skill push for keyless signing #6385), never something theclient silently arbitrates.
acquisition never leaves an unsigned artifact published.
docs/arch/12-skills-system.md: documents the three signing choices, theCLI-acquires/server-signs split, and the ladder, in the same PR that ships
the UX.
Consequence worth flagging explicitly: after this merges, a bare
thv skill pushwith no flags at all stops being a hard 400 and startssigning keylessly — in CI from the ambient token, or after a browser prompt
locally. That's the intended fix for the CI breakage from #6139 (the
workflow file itself is repaired in the next PR in this stack).
Type of change
Test plan
task test)task lint-fix)task testpasses forpkg/skills/identitytoken(new, thorough table-drivencoverage:
Resolve's file/literal/error disambiguation,Ambient'senv-var-absent/success/error-response cases via
httptest,Acquire's fullprecedence table including the regression case where
--key+--identity-tokenare both set, andInteractive's wiring viaoauthflow.StaticTokenGetterwith a structurally-valid unverified JWTfixture — no real OAuth exchange). One unrelated failure appeared in
pkg/transport/proxy/streamablefrom a stray leftover process holding ahardcoded test port in this local environment (same pre-existing flake noted
on the earlier PRs in this stack, untouched package).
Manual: built binary, ran
thv skill push <ref>with stdin from/dev/null(no
--key/--no-sign/--identity-token) — fails immediately with thedocumented text, no HTTP request attempted (no
thv servewas even running).--identity-token /no/such/filefails naming both possibilities rather thanbeing forwarded as a literal token. Did not exercise the real interactive
browser flow or a live Fulcio/Rekor round trip — that's covered by the
staging E2E job in the next PR in this stack, not here.
Changes
pkg/skills/identitytoken/{resolve,ambient,interactive,acquire}.gocmd/thv/app/skill_push.go--identity-tokenflag, TTY-gated confirm callback, oneAcquirecall beforePushdocs/arch/12-skills-system.mddocs/cli/thv_skill_push.mdtask docsDoes this introduce a user-facing change?
Yes —
thv skill pushgains--identity-token, and a push with none of--key/--identity-token/--no-signnow attempts keyless signingautomatically (ambient CI token, or an interactive browser prompt) instead
of unconditionally failing with "signing key required". If no credential can
be acquired, it still fails, with an error naming all three options.
Special notes for reviewers
Base branch is
skills-keyless/02-push-token-plumbing(#6385), notmain.Two things flagged during design review worth a second look:
sigstoreOAuth client ID used for the interactive flow is cosign'swell-known public value, but it isn't a constant in any vendored library
(only the issuer URL,
https://oauth2.sigstore.dev/auth, is), so it couldnot be confirmed from source alone — worth one real interactive run before
merge to be certain.
oauthflow.OIDConnecttakes nocontext.Contextand hardcodescontext.Background()internally, soInteractivehas no cancellationpath to plumb. The redirect wait self-limits to 120s, but provider
discovery, the code exchange, and the out-of-band stdin fallback are
unbounded. Accepted deliberately (documented in
interactive.go): thisonly runs after an explicit y/N on a TTY, so a human is present and Ctrl-C
is the exit — wrapping it in a goroutine would leak one blocked on stdin
or an open socket with no way to cancel it.
Out of scope, flagged for a separate issue:
thv ai-plugin pushstilldeclares no
--key/--no-sign/--identity-tokenat all, even thoughplugins.PushOptionsis a type alias ofskills.PushOptionsand alreadycarries
IdentityToken. That issue has not been filed yet.