Release v0.50.0 - #6691
Release v0.50.0#6691
Conversation
Release-Triggered-By: jhrozek
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6691 +/- ##
==========================================
- Coverage 79.24% 79.18% -0.06%
==========================================
Files 792 795 +3
Lines 79873 80380 +507
==========================================
+ Hits 63294 63652 +358
- Misses 16574 16723 +149
Partials 5 5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
📝 Generated release notes for
|
| Case | Result |
|---|---|
Catalog declares a constraint ToolHive cannot verify (sigstore_url, attestation) |
422, fails closed |
Unsigned or key-signed artifact where the catalog declares signer_identity / cert_issuer / repository_uri / repository_ref / runner_environment — --allow-unsigned no longer bypasses this |
403 |
--public-key used against a certificate-shaped catalog policy |
403 |
A signature that verifies but whose identity does not match the declared constraint also now fails with 403. Rejected installs leave no database record and no materialized files.
Before
# First project install, catalog declares a signer identity, artifact unsigned
thv ai-plugin install --scope project --allow-unsigned my-plugin
# → installed, TOFU recordedAfter
thv ai-plugin install --scope project --allow-unsigned my-plugin
# → 403: plugin is unsigned but its catalog entry requires verified provenanceMigration steps
- Fix the artifact so it satisfies the catalog's declared constraint — this is the intended path.
- If you need to unblock immediately, install by full OCI reference (
thv ai-plugin install ghcr.io/<ns>/<name>:<tag>), which carries no catalog provenance, or install user-scoped, which does not verify. - For an entry declaring an unsupported
sigstore_urlorattestationconstraint there is no client-side bypass — the catalog entry must be corrected. thv ai-plugin syncin an already-locked project is unaffected and keeps working.
Migration guide: authserver.Storage gained six required methods
The device authorization grant added two new storage capabilities that are embedded into the composite Storage interface in pkg/authserver/storage/types.go, so they are required unconditionally — even with device flow disabled.
Who is affected: anyone outside this repo with a custom pkg/authserver/storage.Storage implementation. In-tree implementations (MemoryStorage, RedisStorage, and the CIMD/SPIFFE decorators, which embed Storage) are unaffected.
Before
type Storage interface {
// ... existing capabilities
PendingAuthorizationStorage
}After
type Storage interface {
// ... existing capabilities
PendingAuthorizationStorage
PendingDeviceLoginStorage // Store/Load/DeletePendingDeviceLogin
PendingDeviceConfirmationStorage // Store/Load/DeletePendingDeviceConfirmation
}Migration steps
- Implement the six new methods, using
MemoryStorageinpkg/authserver/storage/memory.goas the reference. HonorDefaultDeviceLoginTTL(10 minutes) and the existingErrNotFound/ErrExpiredcontracts. - Implement the separate
DeviceCodeStorageinterface only if you intend to setdevice_flow_enabled— it is reached by type assertion at construction time, andNewHandlerreturns a hard error if the flag is set and the backend does not implement it. - Note the new
storage.ErrInvalidState, returned byMarkDeviceRequestAuthorized/MarkDeviceRequestDeniedwhen the request is not pending. - Unrelated Go API break in the same area:
registration.ValidatePublicResponseTypeschanged from(responseTypes []string)to(responseTypes, grantTypes []string).
🆕 New Features
- The embedded authorization server now supports the RFC 8628 Device Authorization Grant end to end — device/user code issuance, a browser verification page with an explicit approve/deny step, and DCR registration of device-code clients — opt-in via
device_flow_enabled, off by default (#6647, #6682). - A ToolHive-fronted MCP server can now be the resource side of Cross App Access: the token endpoint accepts IdP-minted ID-JAG assertions (
typ: oauth-id-jag+jwt) under the existing per-issuerjwt_bearerpolicy, with client authentication always required and the assertion'sclient_idbound to the redeeming client (#6677, closes #6676). - Upstream providers accept
additionalTokenParamsonoidcConfigandoauth2Config, so ToolHive works with authorization servers that enforce RFC 8707 resource indicators on the token request body and not just the authorize URL (#6430). - Virtual MCP gains
operational.listChangedto exclude specific backends fromlist_changedsubscription, andoperational.timeouts.backendInitto cap how long session initialization waits for a single backend — so a backend that accepts the notification stream and never services it can no longer stall the handshake (#6633). thv skill upgrade --allow-signer-change --public-key <path>can re-anchor a key-pinned OCI skill, andthv skill sync --adopt --public-key <path>can adopt an installed key-signed skill, with lock updates now transactional and compare-and-swap protected (#6662, part of #6640).- The same key-rotation workflow is available for plugins via
thv ai-plugin upgradeandthv ai-plugin sync --adopt(#6663, closes #6640). - Plugin installs resolved from the registry now verify catalog-declared signer, issuer, repository, ref and runner provenance before trust is recorded, closing the parity gap with skills — see the breaking-change note above (#6653, closes #6643).
- The encrypted secrets store derives its key with Argon2id over a per-file salt instead of an unsalted SHA-256, hardening a stolen secrets file against offline guessing — see the breaking-change note above (#6657).
🐛 Bug Fixes
- A transient JWKS or network failure during ID-token verification after an upstream refresh no longer kills the session: verification is retried in place instead of replaying the already-consumed rotating refresh token, and the previously validated ID token and subject are retained if keys still cannot be fetched — permanent failures such as a bad signature or subject mismatch still fail closed (#6559, fixes #6194).
- Virtual MCP no longer drops a backend entirely when it advertises the
resourcesorpromptscapability but answers the matching list method with-32601 Method not found(seen with Atlassian Jira/Rovo); the backend's remaining tools keep aggregating and a warning is logged instead. Operators who alerted on backend health for this condition should alert on the new warning log (#6660, related to #6339 and #5231). - MCPServer and MCPRemoteProxy pods now receive the operator's default Redis password alongside the default address, so they no longer crash-loop with
NOAUTH Authentication requiredagainst an authenticated Redis/Valkey. Upgrade note: if you setoperator.defaultRedis.existingSecret/global.redis.existingSecret, make sure that Secret exists in every namespace where MCPServer or MCPRemoteProxy workloads run before upgrading — the injectedsecretKeyRefis not optional, and pods will fail to start if it is missing (#6679). - Fixed a data race that turned the whole
pkg/vmcp/servertest binary red onmainby moving the version poll interval from a package-level variable ontoServer(#6644).
🧹 Misc
- All 31 in-repo workflow references moved from workspace-relative
uses: ./...to GitHub's$/...self-repository syntax, so a step running untrusted PR code can no longer substitute a workflow or composite action that a later step executes (#6654). - The operator guide and
docs/authz.mdnow document the opaque-access-token claim fallback alongside the session-less one — the normal state for Google and GitHub upstreams, previously described as a single edge case, which could lead operators to write Cedar policies assuming claims were upstream-asserted when they were not (#6656). - Documented that cosign key-pair signing is a weaker trust tier than keyless, covering missing certificate identity, transparency-log evidence, signing time, revocation signals and recovery after key compromise (#6661, closes #6641).
- Nine stale documentation paths now point at code that exists, mostly packages that moved to
toolhive-core(#6650, closes #6387). pkg/authserver/storageadds a presence-gatedUpdateDCRCredentialsIfPresentwrite path for DCR credentials on both the memory and Redis backends, with CAS semantics on Redis (#6674, closes #6673).buildProviderwas split intobuildJWTBearerFactoriesandbuildDeviceFlowFactoryto bring its cyclomatic complexity back under the lint threshold (#6680).- Renovate dependency groups simplified into smaller chunks that are easier to review and merge (#6658).
- The Anthropic gateway base URL in the Claude workflows moved to a repo-level variable so it can be repointed in one place (#6672).
📦 Dependencies
| Module | Version |
|---|---|
k8s.io/api, k8s.io/apimachinery, k8s.io/client-go, k8s.io/apiextensions-apiserver |
v0.35.x → v0.37.0 |
sigs.k8s.io/controller-runtime |
v0.23.3 → v0.25.0 |
sigs.k8s.io/structured-merge-diff/v6 |
→ v6.4.2 |
k8s.io/kube-openapi, k8s.io/utils |
bumped |
github.com/lestrrat-go/jwx/v3 → github.com/lestrrat-go/jwx/v4 |
v3.3.0 → v4.5.0 |
github.com/jwx-go/jwkfetch/v4 |
v4.0.4 (new) |
github.com/cenkalti/backoff/v5 → github.com/cenkalti/backoff/v7 |
v5.0.3 → v7.0.0 |
github.com/getsentry/sentry-go/otel |
v0.44.1 → v0.49.0 |
github.com/getsentry/sentry-go/otel/otlp |
v0.49.0 (new) |
github.com/charmbracelet/bubbletea → charm.land/bubbletea/v2 |
v1.3.10 → v2.0.9 |
github.com/charmbracelet/bubbles → charm.land/bubbles/v2 |
v1.0.0 → v2.2.1 |
github.com/charmbracelet/lipgloss → charm.land/lipgloss/v2 |
v1.1.0 → v2.0.6 |
github.com/stacklok/toolhive-core |
v0.0.47 → v0.0.49 |
github.com/modelcontextprotocol/go-sdk |
v1.7.0 → v1.8.0 |
| Helm (CI and chart publishing) | v3.22.0 → v4.3.0 |
Notes for Go consumers: importing ToolHive as a library now requires controller-runtime v0.25.x and client-go v0.37.x. controller-runtime v0.25 dropped sigs.k8s.io/controller-runtime/pkg/scheme, so v1alpha1.SchemeBuilder / v1beta1.SchemeBuilder are no longer of type *scheme.Builder — calling AddToScheme, Register(...) or Build() still compiles, but code that names the type does not. The published Helm charts are unchanged and still install with Helm 3.10+; the Helm 4 bump is CI and publishing only. Finally, session.WithBackendInitTimeout in pkg/vmcp/session is now a hard cap that WithRequestTimeoutResolver can no longer raise — ToolHive's own defaults and YAML behavior are unchanged.
👋 Welcome to our newest contributors: @siddiqueirshad, @hellouz818 🎉
Full commit log
What's Changed
- Use self-repository syntax for local workflow refs by @rdimitrov in Use self-repository syntax for local workflow refs #6654
- Upgrade CI to Helm 4 by @renovate[bot] in Upgrade CI to Helm 4 #6614
- Update kubernetes dependencies by @renovate[bot] in Update kubernetes dependencies #6589
- Simplify Renovate dependency groups by @rdimitrov in Simplify Renovate dependency groups #6658
- Update module github.com/getsentry/sentry-go/otel to v0.49.0 by @renovate[bot] in Update module github.com/getsentry/sentry-go/otel to v0.49.0 #6592
- Update cli and ui libraries to v2 by @renovate[bot] in Update cli and ui libraries to v2 #6611
- Derive secrets encryption key with Argon2id by @rdimitrov in Derive secrets encryption key with Argon2id #6657
- Document request-claim fallback provenance by @rdimitrov in Document request-claim fallback provenance #6656
- Retry ID-token validation after OIDC refresh by @siddiqueirshad in Retry ID-token validation after OIDC refresh #6559
- Enforce catalog provenance for plugin installs by @samuv in Enforce catalog provenance for plugin installs #6653
- Document key-pair signing trust limits by @samuv in Document key-pair signing trust limits #6661
- Update module github.com/cenkalti/backoff/v5 to v7 by @renovate[bot] in Update module github.com/cenkalti/backoff/v5 to v7 #6625
- Tolerate MethodNotFound for optional capabilities in ListCapabilities by @hellouz818 in Tolerate MethodNotFound for optional capabilities in ListCapabilities #6660
- feat(authserver): support additional token-request params (RFC 8707 resource indicators) by @aron-muon in feat(authserver): support additional token-request params (RFC 8707 resource indicators) #6430
- feat(vmcp): stop session init stalling on backends that never service the notification stream by @aron-muon in feat(vmcp): stop session init stalling on backends that never service the notification stream #6633
- fix(vmcp): make the version poll interval per-Server, not a global by @aron-muon in fix(vmcp): make the version poll interval per-Server, not a global #6644
- docs: point nine stale paths at the code that exists by @melbinjp in docs: point nine stale paths at the code that exists #6650
- Update module github.com/lestrrat-go/jwx/v3 to v4 by @renovate[bot] in Update module github.com/lestrrat-go/jwx/v3 to v4 #6626
- Use repo variable for Anthropic base URL by @eleftherias in Use repo variable for Anthropic base URL #6672
- feat(authserver): accept ID-JAG assertions with a bound JWT-bearer handler by @aron-muon in feat(authserver): accept ID-JAG assertions with a bound JWT-bearer handler #6677
- Add RFC 8628 device authorization grant support by @reyortiz3 in Add RFC 8628 device authorization grant support #6647
- Reduce buildProvider cyclomatic complexity by @reyortiz3 in Reduce buildProvider cyclomatic complexity #6680
- fix(operator): forward default Redis password to MCPServer and MCPRemoteProxy by @reyortiz3 in fix(operator): forward default Redis password to MCPServer and MCPRemoteProxy #6679
- Add lock-driven skill key rotation by @samuv in Add lock-driven skill key rotation #6662
- Add plugin key rotation workflows by @samuv in Add plugin key rotation workflows #6663
- Add device flow verification page and DCR support by @reyortiz3 in Add device flow verification page and DCR support #6682
- authserver/storage: add UpdateDCRCredentialsIfPresent by @tgrunnagle in authserver/storage: add UpdateDCRCredentialsIfPresent #6674
- Release v0.50.0 by @toolhive-release-app[bot] in Release v0.50.0 #6691
New Contributors
- @siddiqueirshad made their first contribution in Retry ID-token validation after OIDC refresh #6559
- @hellouz818 made their first contribution in Tolerate MethodNotFound for optional capabilities in ListCapabilities #6660
Full Changelog: v0.49.0...v0.50.0
🔗 Full changelog: v0.49.0...v0.50.0
Release v0.50.0
Version Bump
minor release
Files Updated
VERSIONdeploy/charts/operator-crds/Chart.yaml(path:version)deploy/charts/operator-crds/Chart.yaml(path:appVersion)deploy/charts/operator/Chart.yaml(path:version)deploy/charts/operator/Chart.yaml(path:appVersion)deploy/charts/operator/values.yaml(path:operator.image)deploy/charts/operator/values.yaml(path:operator.toolhiveRunnerImage)deploy/charts/operator/values.yaml(path:operator.vmcpImage)Next Steps
Checklist