stacklok / stacklok/toolhive

Implement SPIFFE trust-domain bundle management and rotation

Open
#6,201 1 comment 0 reactions 1 assignee View on GitHub

@jhrozek is already working on this.

Since Aug 14, 2026.

authentication authorization enhancement go
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Description

Provide the validation material both SPIFFE credential types are checked
against: X.509 roots and intermediates for X.509-SVID chains, and JWT
verification keys for JWT-SVID signatures, keyed by an explicitly configured
trust domain.

This is the largest greenfield piece of the epic and the largest risk. The
spiffee-authserver branch has no bundle or rotation code at all —
spiffe.NewMiddleware takes only an expected trust domain and relies on chain
validation configured on the TLS listener. There is nothing to harvest here, so
the work should be split along the line where existing machinery stops.

Scope

One bundle document carries both key types, so both halves share a single
fetch. Do not split this work by credential type.

  • Add a trust-domain-keyed bundle registry supplying both x509bundle.Source
    and jwtbundle.Source. Those are the interfaces x509svid.Verify (#6202)
    and jwtsvid.ParseAndValidate (#6203) consume, and they are the deliverable.
  • Supply X.509 roots and intermediates for chain validation, and JWT
    verification keys restricted to the bundle's jwt-svid keys.
  • Support explicitly configured SPIFFE Bundle Endpoints.
  • Support a SPIRE Workload API bundle source for locally attested deployments.
  • Implement bundle refresh hints, sequence-number monotonicity, and an explicit
    last-known-good policy.
  • Keep Workload API and remote Bundle Endpoint configuration independent —
    neither should imply or override the other.

go-spiffe/v2 is already a direct dependency and covers most of the above:
workloadapi.NewBundleSource, spiffebundle.Set (trust-domain-keyed, already
fails closed on unknown domains), spiffebundle.Read, Bundle.RefreshHint(),
Bundle.SequenceNumber(), and federation.NewHandler for serving spec-correct
bundle documents in tests. Do not reimplement any of it. Note that go-spiffe
parses spiffe_sequence but never enforces it, so rollback protection is ours.

Two corrections to the original scope

Do not extend pkg/oauthproto/jwks.go. A SPIFFE bundle is keyed by trust
domain rather than issuer, carries spiffe_sequence and spiffe_refresh_hint,
and partitions keys by use. JWKSFetcher is issuer-keyed and discovery-driven,
so adopting it means reimplementing spiffebundle.Read worse. That file is also
an unpushed spike artifact that should not be adopted at all — see #6319 for the
separate and unrelated JWKS consolidation work.

Rotation overlap is not configurable. A bundle is an authority set, and the
trust domain publishes old and new authorities together during rotation. A local
overlap window would retain authorities the domain has already withdrawn, which
contradicts the "removed keys stop validating" criterion below. Overlap is the
bundle's content, not a knob.

Acceptance criteria

  • Bundle material is selected by a configured trust domain, never by an
    arbitrary issuer or claim taken from the credential being validated.
  • Bundle Endpoint URLs are explicitly configured and authenticated using
    WebPKI, kept separate from SPIFFE workload trust.
  • X.509 and JWT validation cannot mix keys across trust domains.
  • A rotated bundle validates its new authorities and stops validating removed
    ones on the next successful fetch, with no restart.
  • A bundle whose sequence number is lower than the current one is rejected and
    the current material is retained.
  • Removed keys stop validating once the updated bundle becomes authoritative.
  • Unknown trust domains fail closed.
  • Bundle refresh and failure behaviour is observable without logging
    credentials or key material.

Dependencies

  • Sub-issue 1 (identity, association and configuration model) — the trust
    domain this registry is keyed by is declared there.

Related

  • Part of the SPIFFE client-authentication epic

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.