Skip to content

Release v0.43.0 - #6333

Merged
jerm-dro merged 1 commit into
mainfrom
release/v0.43.0
Aug 14, 2026
Merged

Release v0.43.0#6333
jerm-dro merged 1 commit into
mainfrom
release/v0.43.0

Conversation

@toolhive-release-app

Copy link
Copy Markdown
Contributor

Release v0.43.0

Version Bump

minor release

Files Updated

  • VERSION
  • deploy/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)
  • Helm chart docs (via helm-docs)

Next Steps

  1. Review this PR
  2. Merge to main
  3. Release automation will handle the rest

Checklist

  • Version bump is correct
  • All CI checks pass

Release-Triggered-By: jerm-dro
@github-actions github-actions Bot added the size/XS Extra small PR: < 100 lines changed label Aug 14, 2026
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.98%. Comparing base (cfba580) to head (d3de799).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6333      +/-   ##
==========================================
+ Coverage   72.94%   72.98%   +0.04%     
==========================================
  Files         742      742              
  Lines       78236    78236              
==========================================
+ Hits        57070    57103      +33     
+ Misses      17188    17137      -51     
- Partials     3978     3996      +18     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jerm-dro
jerm-dro merged commit 8f294e2 into main Aug 14, 2026
76 of 77 checks passed
@jerm-dro
jerm-dro deleted the release/v0.43.0 branch August 14, 2026 15:57
@github-actions

Copy link
Copy Markdown
Contributor

📝 Generated release notes for v0.43.0

Auto-generated by the release-notes skill. Review and, if good, apply with:

gh release edit v0.43.0 --notes-file <paste-below>.md
Click to expand release notes

🚀 Toolhive v0.43.0 is live!

A security-and-hardening release: RFC 8693 token-exchange delegation becomes usable end to end, skills signing graduates out of its feature gate, and a 111-finding GitHub Actions hardening backlog is cleared down to zero. Several long-standing security seams were tightened in ways that require action — read the breaking changes before upgrading.

Upgrading (Kubernetes): apply the toolhive-operator-crds v0.43.0 chart before upgrading the operator. The API additions are backwards compatible, but CRD schema pruning means a manifest using the new delegateClients / allowConfidentialClientRegistration fields against v0.42.1 CRDs is accepted while those fields are silently discarded. Note also that this release changes the middleware order baked into the runconfig ConfigMap, so every MCPServer / MCPRemoteProxy with telemetry enabled will roll its pods on upgrade — even if you pin toolhiveRunnerImage.

⚠️ Breaking Changes

  • vMCP rejects Authorization and Cookie in passthroughHeaders — a vMCP config listing either header now fails validation at startup; on Kubernetes the CR is still admitted and the pod CrashLoopBackOffs, so edit your VirtualMCPServer before upgrading (migration guide below).
  • Cedar read_resource policies must name the exact resource URI — entity IDs are no longer character-sanitized, so a policy naming a mangled ID like Resource::"file____etc_passwd" silently stops matching and the resource disappears from resources/list (migration guide below).
  • thv skill push now requires --key or --no-sign, and the skills lock file is no longer gated — existing publish scripts fail with a 400, and project-scoped installs of any artifact published before v0.43.0 fail with a 403 until you pass --allow-unsigned (migration guide below).
  • Telemetry now records rate-limited and webhook-denied requests — on operator-managed workloads, 429/403/500 rejections newly land in MCP metrics and traces, so error-rate alerts can fire on ordinary throttling (migration guide below).
  • Client-controlled metric labels are capped at 128 bytesmcp_resource_id carries resource URIs and pagination cursors, so exact-match dashboard queries on long values stop returning data (migration guide below).
  • The proxy /health response no longer includes the version object — read version information from thv version --format json instead; liveness/readiness probes are unaffected (migration guide below).
  • thv llm teardown of your last tool now clears the gateway connection settings — the next thv llm setup needs --gateway-url, --issuer and --client-id supplied again (migration guide below).
Migration guide: vMCP rejects Authorization and Cookie in passthroughHeaders

Affects any vMCP deployment — CLI (thv vmcp serve) or the VirtualMCPServer CRD — whose config lists Authorization or Cookie (any casing) in passthroughHeaders.

On v0.42.1 such a config started cleanly and forwarded the caller's incoming credential verbatim to every backend, contradicting the documented contract in docs/operator/virtualmcpserver-api.md ("restricted headers … are rejected at startup"). v0.43.0 enforces the documented behaviour. It fails closed — no traffic, no credential leak — but the failure mode on Kubernetes is unkind: the operator-side validator and the CRD have no rule for this field, so the resource is still admitted and only the vMCP pod fails, with the error visible solely in pod logs.

Before

apiVersion: toolhive.stacklok.dev/v1beta1
kind: VirtualMCPServer
spec:
  passthroughHeaders:
    - authorization        # rejected on v0.43.0
    - cookie               # rejected on v0.43.0
    - x-tenant-id          # still allowed

After

apiVersion: toolhive.stacklok.dev/v1beta1
kind: VirtualMCPServer
spec:
  passthroughHeaders:
    - x-tenant-id
  outgoingAuth:
    source: inline
    backends:
      github:                       # exchange the caller's identity per RFC 8693
        type: externalAuthConfigRef
        externalAuthConfigRef:
          name: github-token-exchange
      slack:                        # or inject a backend-owned static credential
        type: service_account
        serviceAccount:
          credentialsRef:
            name: slack-bot-token
            key: token
          headerName: Authorization
          headerFormat: "Bearer {token}"

Migration steps

  1. Audit every vMCP config for passthroughHeaders containing authorization or cookie, matching case-insensitively — validation canonicalizes header names, so authorization, Authorization and AUTHORIZATION are all rejected. Check spec.config.passthroughHeaders too.
  2. Remove those two entries. Leave every other header as-is; only Authorization and Cookie were added to the vMCP restricted set, and the standalone header-forward middleware used by thv proxy is unchanged.
  3. Replace the forwarding with a per-backend outgoingAuth strategy: externalAuthConfigRef (RFC 8693 token exchange) when the backend needs the caller's identity, service_account / header_injection for a backend-owned static credential, upstream_inject when the embedded auth server already holds an upstream IdP token, or unauthenticated. There is deliberately no pass-through strategy — MCP forbids forwarding client tokens upstream.
  4. Validate before rolling out: thv vmcp validate -f <config> runs the same validator, so a bad config surfaces as a clear message instead of a CrashLoopBackOff.
  5. On Kubernetes, apply the edited VirtualMCPServer before upgrading the operator or the vMCP image, since the CRD will admit the old spec.
  6. If a backend genuinely needs a caller-supplied opaque credential, move it to a non-Authorization header the backend understands (for example x-api-key) and keep that name in passthroughHeaders.

PR: #6235

Migration guide: Cedar read_resource policies must name the exact resource URI

Affects anyone with a Cedar authz policy — --authz-config, MCPToolConfig / authzConfig.inline.policies, or vMCP authzConfig — that names a Resource:: entity ID for Action::"read_resource" whose ID contains a _ standing in for a rewritten character.

On v0.42.1, resource URIs were turned into entity IDs by a lossy sanitizer that rewrote : / \ ? & = #, space and . to _, while the policy text was never rewritten — so a working policy had to spell the sanitized form. That mapping was many-to-one, which was the bug: file:///etc/passwd and file://_etc/passwd both became Resource::"file____etc_passwd", so one grant authorized every URI in the collision class. v0.43.0 uses the exact URI as the entity ID and deletes the sanitizer.

This fails closed. Cedar defaults to deny, and because resources/list responses are filtered by running the same read_resource check per URI, an unmigrated policy means the resource is silently absent from resources/list rather than raising an error. Conversely, a single legacy policy may have been granting a whole collision class, so do not assume a 1:1 rewrite.

Policy styles that are unaffected: plain IDs with no rewritten character (Resource::"data"), attribute matching (when { resource.uri == … }, resource.name, resource.visibility), hierarchy matching (resource in MCP::"<server>"), and every non-read_resource action.

Before

permit(
  principal,
  action == Action::"read_resource",
  resource == Resource::"file____etc_passwd"
);

After

permit(
  principal,
  action == Action::"read_resource",
  resource == Resource::"file:///etc/passwd"
);

Migration steps

  1. Grep every authz policy source — CLI --authz-config JSON/YAML, authzConfig.inline.policies on MCPServer / VirtualMCPServer, authz ConfigMaps — for Action::"read_resource".
  2. For each resource == Resource::"<id>", check whether <id> contains _ where the real URI has : / \ ? & = #, space or .. If so it is a legacy sanitized ID.
  3. Replace it with the URI exactly as the MCP server publishes it in the uri field of resources/list. Only " and \ need escaping: file://C:\share\data becomes Resource::"file://C:\\share\\data".
  4. Enumerate each URI you actually intend to permit and emit one policy per URI — a legacy policy may have been covering more than you realise.
  5. Non-URI names containing a dot are affected too: a resource named config.json needed Resource::"config_json" before and needs Resource::"config.json" now.
  6. Optional hardening — switch to attribute matching, which was stable across both versions:
    permit(principal, action == Action::"read_resource", resource)
    when { resource.uri == "file:///etc/passwd" };
    
  7. Verify after upgrading by calling resources/list and resources/read for each intended URI. A missed policy shows up as the resource missing from the list, not as an error.

PR: #6239

Migration guide: skill push requires an explicit signing decision, and the skills lock file is no longer gated

Three independent breaks, all landing with the same PR. Affects everyone who runs thv skill push or POST /api/v1beta/skills/push; everyone who installs project-scoped skills (--scope project, including via --project-root in CI); and anyone who published skills with any ToolHive up to v0.42.1. Default user-scoped installs (thv skill install <name> with no --scope) are unaffected.

  1. thv skill push with no new flags now fails with a 400signing key required: set key (--key), or no_sign (--no-sign) to push unsigned — before anything is pushed.
  2. Signature verification on install is no longer gated. A project-scoped install of an unsigned OCI artifact, unsigned git commit, or local build now returns a 403 unless --allow-unsigned is passed. Because pre-v0.43.0 thv skill push could never sign anything, every previously published skill is unsigned — so project-scoped installs that worked on v0.42.1 now fail.
  3. TOOLHIVE_SKILLS_LOCK_ENABLED is removed entirely. Users who never opted in now get behaviour they did not ask for: toolhive.lock.yaml is written at the project root on every project-scoped install, toolhive.requires dependencies are materialized (installing extra skills), and uninstall edits the lock file and cascades dependency removal. Setting the old env var is now a silent no-op. On the plus side, thv skill sync and thv skill upgrade no longer return the "experimental" 403.

Before

thv skill push ghcr.io/org/my-skill:v1
thv skill install ghcr.io/org/my-skill:v1 --scope project --project-root .

After

# Signed (preferred). COSIGN_PASSWORD for an encrypted key must be in the
# `thv serve` process environment — signing happens server-side, not in the CLI shell.
thv skill push ghcr.io/org/my-skill:v1 --key cosign.key

# Or explicitly unsigned
thv skill push ghcr.io/org/my-skill:v1 --no-sign

# Installing an artifact published before v0.43.0
thv skill install ghcr.io/org/my-skill:v1 --scope project --project-root . --allow-unsigned

Migration steps

  1. Add --key <path> or --no-sign to every thv skill push invocation in your scripts and CI jobs.
  2. API callers: add "key" or "no_sign": true to the POST /api/v1beta/skills/push body. {"reference":"ghcr.io/org/my-skill:v1"} now returns 400.
  3. For project-scoped installs of artifacts published before v0.43.0 (or of local builds), add --allow-unsigned. The exception is recorded as unsigned: true in the lock entry and honoured by later sync / upgrade without re-asking.
  4. Preferred longer-term fix: re-push the artifact with --key so consumers verify normally. The first verified install pins the signer identity (trust on first use) and prints it.
  5. Commit toolhive.lock.yaml at your project root — it is meant to be reviewed like package-lock.json — and update any CI job that asserts a clean working tree after a project-scoped install.
  6. Remove TOOLHIVE_SKILLS_LOCK_ENABLED from thv serve environments and manifests; it is now ignored.

PR: #6139 — Closes #5899

Migration guide: telemetry now records rate-limited and webhook-denied requests

Affects dashboard owners, alert authors and SLO owners on Kubernetes-operator / thv-proxyrunner deployments (MCPServer, MCPRemoteProxy) that have rate limiting and/or webhooks configured. thv run / API-created workloads, vMCP, and operator deployments with neither rate limiting nor webhooks are unaffected — span names, attributes and metric label values are byte-identical to v0.42.1.

Telemetry moved from inside rate limiting and the webhooks to outside them. Requests the limiter rejects with 429, a validating webhook denies with 403, or a mutating webhook fails with 500 previously never reached the telemetry middleware and were absent from every OTEL metric and trace; they are now recorded. Request-duration histograms also now include rate-limit and webhook round-trip time.

Two concrete regressions:

  • sum(rate(toolhive_mcp_requests_total{status="error"}[5m])) / sum(rate(toolhive_mcp_requests_total[5m])) — on v0.42.1 throttled traffic was invisible; now every 429 lands as status="error", status_code="429", so a 5% error-budget alert can fire purely from normal throttling.
  • mcp_server_operation_duration_seconds carries no status / status_code dimension, so near-zero 429/403 rejections are now indistinguishable from successes and drag percentiles down, while allowed requests drag them up by the webhook round-trip.

New span attributes on the existing server request span (no new span is created), on both the runner and vMCP paths:

Attribute Type Values
rate_limit.decision string allowed, rejected
rate_limit.rejected_by string none, shared_server, shared_tool, per_user_server, per_user_tool
rate_limit.fail_open bool always false in this release

The span is now created as "{HTTP method} {url.path}" and renamed after the inner chain completes, so head samplers or SpanProcessor.OnStart logic keyed on span name see POST /mcp rather than tools/call <tool>. Tail sampling in the collector is unaffected. For users of mutating webhooks, http.request.body.size now reports the pre-mutation body length.

Operator upgrade side effect: because the middleware order is serialized into the runconfig ConfigMap and hashed into the pod template's toolhive.stacklok.dev/runconfig-checksum annotation, every MCPServer / MCPRemoteProxy with telemetry enabled gets a changed runconfig and a rolling restart on upgrade — even if you pin toolhiveRunnerImage.

Known gap: a Redis failure returns before any span annotation, so fail-open requests get none of the three attributes and rate_limit.fail_open is a constant false. Detect fail-open via toolhive_rate_limit_redis_errors until the follow-up counter lands.

Before

# Error-budget alert — counted only genuine backend errors on v0.42.1
sum(rate(toolhive_mcp_requests_total{status="error"}[5m]))
  / sum(rate(toolhive_mcp_requests_total[5m])) > 0.05

After

# Exclude throttling and authz denials from the error budget
sum(rate(toolhive_mcp_requests_total{status="error", status_code!~"429|403"}[5m]))
  / sum(rate(toolhive_mcp_requests_total[5m])) > 0.05

Migration steps

  1. Identify affected deployments: operator-managed MCPServer / MCPRemoteProxy with spec.rateLimit, spec.mutatingWebhooks or spec.validatingWebhooks, plus telemetry enabled.
  2. Exclude throttling and denials from error-rate alerts as shown above, or split them into a separate throttling panel.
  3. Rebase latency SLOs onto toolhive_mcp_request_duration_seconds{status_code!~"429|403"}mcp_server_operation_duration_seconds cannot filter out rejections — or re-baseline the thresholds after upgrading.
  4. Re-baseline request-count and tool-call-count panels; expect a step change in totals at upgrade time.
  5. If you run a name-based head sampler, key it on attributes (mcp.method.name) or move the decision to the collector.
  6. Do not alert on rate_limit.fail_open="true" in this release; use toolhive_rate_limit_redis_errors.
  7. Plan for the pod roll described above, or schedule the operator upgrade in a maintenance window.

PR: #5800 — Part of #4553

Migration guide: client-controlled metric labels are capped at 128 bytes

Affects dashboard owners and alert authors whose queries exact-match mcp_resource_id on toolhive_mcp_requests_total / toolhive_mcp_request_duration_seconds, and anyone using label_values(mcp_resource_id) as a Grafana template variable. Trace consumers, tool-name panels and method-name panels are unaffected.

Cumulative metric readers keep every distinct attribute set resident for the process lifetime, and MCP method, tool and prompt names arrive verbatim from the client request bounded only by the 8 MB body cap — so roughly 64 crafted requests could retain ~512 MB permanently and OOM a pod. Client-controlled metric label values are now clamped to 128 bytes on a UTF-8 rune boundary with a trailing ....

128 bytes is comfortably enough for real tool names, method names and prompt names. The problem is mcp_resource_id, which also carries the full resource URI for resources/read and the pagination cursor for */list — both of which routinely exceed 128 bytes.

Labels affected: mcp_method and mcp_resource_id on toolhive_mcp_requests_total and toolhive_mcp_request_duration_seconds; tool on toolhive_mcp_tool_calls_total; mcp.method.name, gen_ai.tool.name and gen_ai.prompt.name on mcp_server_operation_duration_seconds. No metric names, label keys or span attributes changed — span attributes deliberately keep the full value, since spans are sampled and ephemeral. That does mean a metric label and its corresponding span attribute no longer match for truncated values, breaking label-equality "jump to traces" workflows for exactly those series.

Before

toolhive_mcp_requests_total{mcp_method="resources/read", mcp_resource_id="https://api.example.com/…<long URI>…"}

After

sum by (mcp_method) (
  toolhive_mcp_requests_total{mcp_method="resources/read", mcp_resource_id=~"https://api\\.example\\.com/.*"}
)

Migration steps

  1. Grep dashboards and recording rules for exact mcp_resource_id= matches. For resources/read and paginated */list panels, switch to a prefix regex or drop the label from the selector and aggregate by mcp_method.
  2. Expect a one-time counter discontinuity for affected series at upgrade: the full-value series goes stale and a ...-suffixed series starts from zero. Use sum without (mcp_resource_id) across the transition window, or accept the reset in rate() / increase().
  3. Refresh Grafana template variables built from label_values(mcp_resource_id), and any recording rules that pin the label.
  4. Be aware that two resource URIs sharing a 125-byte prefix now collapse into one series. This is unreachable for tool names but plausible for deep paths and presigned URLs, and it silently sums distinct resources in per-resource panels.
  5. No action needed for trace queries, tool-name panels or method-name panels.

PR: #6279 — Part of #6271

Migration guide: the proxy /health response no longer includes the version object

Affects anyone parsing the JSON body of a workload proxy's unauthenticated /health endpoint — monitoring scripts, dashboards, deployment gates, curl … | jq .version.version checks. The endpoint must stay unauthenticated so Kubernetes probes can reach it, so the fix was to stop disclosing the build fingerprint rather than to add auth.

The version object (version, commit, build_date, go_version, platform) is gone from all three proxies — transparent, httpsse and streamable. A client reading .version.version now gets null with no error and no flag to restore it. status, timestamp, transport and mcp are unchanged, so liveness/readiness probes and degraded-state detection need no change (/health still returns 503 when unhealthy). This aligns the proxies with the minimal shape vMCP and the v1 API already used.

Note the replacement lives on a different server and port than the proxy that lost the field, so it is not a drop-in substitute.

Before

curl -s http://localhost:8080/health | jq -r .version.version

After

thv version --format json | jq -r .version

# or, against the thv serve API (authenticated, different port)
curl -s http://127.0.0.1:8080/api/v1beta/version | jq -r .version

Migration steps

  1. Stop reading version data from a workload proxy's /health.
  2. Use thv version --format json — this is the only remaining source of commit, build_date, go_version and platform. The GET /api/v1beta/version endpoint on the thv serve API returns version only.
  3. Keep using /health for status and mcp.available; probes need no change.

PR: #6280 — Part of #6271

Migration guide: thv llm teardown of the last tool clears the gateway connection settings

Affects thv llm gateway users who run a full thv llm teardown, or tear down their last remaining tool, and then expect thv llm setup to work from persisted settings.

Settings like Bedrock and Models were persisted so a plain setup re-run kept them, but nothing ever ended that stickiness — a value outlived every tool that read it and got re-applied by the next setup, even against a gateway since repointed elsewhere. Teardown now resets the LLM config once no configured tool remains, matching what thv llm config reset already did.

On v0.42.1, thv llm teardown && thv llm setup re-applied the persisted connection settings. On v0.43.0 the second command fails with LLM gateway is not configured — run "thv llm config set" first. The whole llm: block is cleared: gateway_url, oidc.issuer / client_id / callback_port, proxy.listen_port, models and bedrock.compat / enable_1m. Cached OIDC token references survive unless --purge-tokens is passed, so the keyring secret is never left orphaned. A teardown that leaves other tools configured is unchanged.

Before

thv llm teardown
thv llm setup                      # re-used persisted settings on v0.42.1

After

thv llm teardown
thv llm setup \
  --gateway-url https://llm.example.com \
  --issuer https://auth.example.com \
  --client-id my-client-id

Migration steps

  1. Capture your settings before tearing down if you need them: thv llm config show --format json > llm-config.json.
  2. Re-supply the connection settings on the next setup, either inline as above or via thv llm config set --gateway-url … --issuer … --client-id … first.
  3. Re-add any non-default extras that were also cleared: --models, --bedrock-compat, --enable-1m, --tls-skip-verify, and the proxy listen port.
  4. To keep the old behaviour, tear down one tool at a time and leave at least one configured — thv llm teardown claude-code preserves the config while any tool remains.

PR: #6295

🆕 New Features

  • Operators can declare pre-provisioned confidential OAuth clients via delegateClients on MCPExternalAuthConfig / VirtualMCPServer, making RFC 8693 token-exchange delegation reachable end to end for agentic use cases — the secret is always by reference and never copied into a ConfigMap or status (#6320)
  • The embedded auth server can issue confidential clients through dynamic client registration behind a default-off allowConfidentialClientRegistration, with forceConfidentialRedirectURIs for clients that declare themselves public and then require a secret (#6252)
  • thv skill push signs artifacts with a cosign key via --key, and a failed signing fails the push so nothing is ever silently published unsigned (#6139)
  • Skill installs and thv skill info now display the recorded trust state — the pinned signer identity, certificate issuer and unsigned exceptions — instead of the user first meeting it weeks later inside a 403 (#6137)
  • Skill verification now records and enforces the signing workflow's git ref and runner class from the Fulcio certificate, so a lock entry pinning "this workflow in this repository" is no longer satisfied by the same workflow run from an attacker's branch or moved to a self-hosted runner (#6315)
  • The lock file provenance block records repositoryRef and runnerEnvironment, with absence meaning unconstrained so existing lock files keep verifying unchanged (#6312)
  • macOS thv binaries in release archives can now carry a Developer ID Application: Stacklok, Inc signature and a Team ID that managed-macOS fleets can allowlist by code-signing identity, instead of only Go's ad-hoc signature (#6156)
  • Traces for Redis-backed rate-limited tool calls now expose whether a request was allowed or rejected and which bounded bucket rejected it (#5800)
  • toolhive.lock.yaml gains a typed sibling plugins: key so project-scoped AI-plugin installs can be pinned in the same version-1 lock file as skills, without a schema bump that would hard-fail mixed-version teams (#6303)
  • Groundwork for plugin sync and upgrade: a PluginLockService interface plus an InstalledPlugin.managed marker so sync --prune can tell lock-managed plugins from hand-installed ones (#6311)

🐛 Bug Fixes

  • MCP clients that negotiate a Legacy protocol version while including reserved _meta keys — the ChatGPT connector among them — can call tools again; previously every tools/call from such a client was rejected with -32020 before reaching the backend, a regression introduced in v0.41.0 (#6231)
  • MCP clients whose client-metadata document declares grant types the embedded auth server does not support — VS Code's device_code among them — now resolve successfully with the unsupported entries ignored, instead of failing with an opaque invalid_client (#6297)
  • vMCP correctly detects conformant Modern backends that split one JSON-RPC response across multiple SSE data: lines, instead of silently downgrading them to Legacy and dropping interleaved progress notifications (#6126)
  • thv llm setup works on Windows for Claude Code, where it previously failed on every machine because the executable path contained backslashes (#6326)
  • The interactive-login OAuth callback server binds to loopback only, so a host on your LAN can no longer observe or abort a ToolHive login in progress (#6238)
  • The private-IP guard that protects against SSRF now decodes 6to4 addresses and blocks Teredo wholesale, closing two IPv6 transition families that were previously treated as ordinary public addresses (#6249)

🧹 Misc

  • Cleared a 111-finding GitHub Actions hardening backlog to zero and made the zizmor check blocking at medium severity, so a PR that introduces a workflow finding now fails CI instead of reporting quietly (#6251, #6274, #6262)
  • Removed shell-injection sinks across the workflows by binding expressions through env: rather than interpolating them into run: blocks, including the github.ref handling in the image publish workflow (#6248, #6258, #6275, #6281)
  • Stopped persisting the checkout credential in .git/config across 20 checkouts in 12 CI workflows, where every subsequent step and the third-party code it invokes could read it (#6255)
  • Scoped release app tokens and workflow permissions to what each job actually needs, and stopped handing every repository secret to the test workflows via secrets: inherit (#6263, #6266, #6269, #6270, #6272)
  • The PR size labeler derives its target from the consuming workflow's own trigger event instead of trusting an artifact produced by a pull_request workflow (#6259, #6261)
  • @claude now responds only to accounts with write access to the repository, replacing a deny-list that permanently admitted anyone who had ever landed a single PR (#6260)
  • Pinned all actions to commit SHAs and corrected version comments that named a different version than the pinned commit (#6254, #6313)
  • Deflaked TestRoundTripDoesNotReplayInitializeOnDialError, which shared a keep-alive transport with this package's parallel tests (#6264)

📦 Dependencies

Module Version
github.com/stacklok/toolhive-catalog v0.20260810.0
golang.org/x/crypto v0.55.0
golang.org/x/mod v0.40.0
golang.org/x/net v0.58.0
golang.org/x/text v0.41.0
golang.org/x/tools v0.49.0

The golang.org/x/* bumps carry CVE fixes (#6327); the catalog update is #6257.

👋 Welcome to our newest contributor: @kocaemre 🎉

Full commit log

What's Changed

New Contributors

Full Changelog: v0.42.1...v0.43.0

🔗 Full changelog: v0.42.1...v0.43.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release size/XS Extra small PR: < 100 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant