Skip to content

Actor JWT contract and OIDC discovery for external relying parties #1756

Open

Description

Summary

ateapi mints actor JWTs that nothing outside the cluster can verify. The issuer is a hardcoded string, no OpenID Connect discovery document or JWK set is served, and there is no way to rotate the signing key. Finalize the token contract and serve discovery and JWKS so that the egress gateway can inject the token (#1660) and exchange it (#1661), and so operators can federate actors into cloud IAM.

Design: Actor JWT Schema + OIDC (shared with ate-dev@).

Motivation

Current state

  • Control.MintActorJWT signs with the pool from --actor-id-jwt-pool. iss is the literal https://api.ate-system.svc; the header carries alg and kid but no typ.
  • Both installers create an ES256 pool with kid "1". make-jwt-pool can only create a single-authority Secret; there is no add, activate, or remove.
  • Pool.VerificationKeys exists but has no caller. ateapi exposes gRPC on 443 and plaintext metrics on 9090; neither serves HTTP discovery or JWKS.
  • The RPC logging interceptor writes the full MintActorJWTResponse, token included, at Info.

Proposal

The design doc has the detail. In short:

  • Token: keep sub as atespaces:<atespace>:actors:<name>, aud always an array, ate.dev claims stable. Add typ: JWT. New authorities default to RS256 with an RFC 7638 thumbprint as kid; existing "1" keys are grandfathered. A single --actor-jwt-issuer value feeds iss, the discovery issuer, and jwks_uri.
  • API: MintActorJWTRequest accepts a requested lifetime clamped to [5m, 1h], with zero meaning the current 15m default; the response returns expires_at.
  • Discovery: /.well-known/openid-configuration and /openid/v1/jwks, shaped like the Kubernetes service account issuer, with no authorization_endpoint. The JWKS carries every authority in the pool, so rotation is add, publish, activate, wait, remove.
  • Serving: a small read-only ate-oidc-server behind an operator-supplied Ingress, serving public key material reconciled from the pool Secret into a ConfigMap. ateapi gains no public listener.

Work

  • Contract: typ header, algorithm on verification keys, issuer flag, requested duration and expires_at, redact actor_jwt from RPC logs.
  • Keys: RS256 generation and thumbprint kids as the default in kubectl ate admin make-jwt-pool and ate-setup; add, activate, and remove subcommands for rotation.
  • Serving: shared discovery and JWKS package, ConfigMap reconciler, ate-oidc-server binary and manifests, issuer plumbing through both installers, and an e2e that verifies a minted token against the served JWKS.
  • Docs: fix docs/api-guide.md section 7 and docs/authentication.md, which describe an ActorIdentity service and checks that do not exist; add the contract, federation recipes for GCP, AWS, and Azure, and a
    rotation runbook.

Out of scope

Activity

  1. ignaciojaneiro commented on Oct 22, 2026

    @ignaciojaneiro

    Hi — coming from the Agent Mesh side. The development/examples/substrate-actor example there puts Substrate actors on the mesh under their own identity; it still uses a stand-in CredentialProvider, and a first iteration (a second actor plus an isolation check) just merged as google/agentmesh#806.

    The natural next step on the agentmesh side is to drop that stand-in and validate native actor-JWT federation, which depends on the discovery/JWKS server tracked here (ate-idp-server, currently on main but not yet in a release). Before I start anything, two questions:

    1. Is the example-facing validation already in flight on your end, or is there room for me to take the agentmesh-side piece once a release includes ate-idp-server?
    2. Anything you'd want pinned down first — the sub format (actor/<atespace>/<name>), the issuer and TLS-trust config a relying party needs to reach ate-idp-server, or the contract doc in Document the actor JWT contract and how relying parties verify it #2605?

    Happy to coordinate however suits; I just don't want to duplicate work already under way.

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

Metadata

Metadata

Labels

area/apiUser-facing API changesarea/identityarea/securitySecurity related issue/prkind/featureAn enhancement / feature request or implementationprio/P0Highest priority / required for next milestone

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions