Repository navigation
Conversation
LLM gateways need the controller's JWKS to verify the JWTs it issues. Until now the management router also served /.well-known/jwks.json, so a gateway configured against it had to reach the management API, which can read the device inventory and delete devices. - The management router no longer serves the JWKS. The fleet listener still does, so existing gateway configurations keep working. - New optional `gatewayJwt.jwksListener` (`listen`, `tls`) serves only the JWKS, over plain HTTP or over HTTPS with the controller certificate. Point gateways at it so a NetworkPolicy or firewall can allow them that one port. Gateways that export usage still need the fleet port. - Helm: a `<release>-jwks` ClusterIP Service, container port and `jwksListener` config, controlled by `gatewayJwks` values. - examples/claude fetches the JWKS from the new listener on 8081. Hardening plan item A4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
yuval-k
marked this pull request as ready for review
November 11, 2026 20:39
`RustlsConfig::from_pem_file` relies on the process-default rustls CryptoProvider. Workspace builds enable both the aws-lc-rs and ring features, so rustls cannot pick one and the HTTPS JWKS listener panicked at startup (and in its test under `cargo test --workspace`). Reuse the management listener's explicit aws-lc-rs config, renamed to `tls_server_config` with a label for its error messages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move `tls_server_config` from the controller's management module to `agentdesktop_core::tls::server_config` so any crate can build a rustls server config without relying on the process-default CryptoProvider. Core already depends on rustls with aws-lc-rs through reqwest, so the direct dependency adds nothing to the build; the controller no longer needs its own. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#226 bumped react and lucide-react in the controller and desktop apps but not in the shared @agentdesktop/ui package, so pnpm installed a second copy (react 19.2.8, lucide-react 1.31.0) for it. With a cold Vite cache, the controller storybook tests loaded both copies and 89 of 92 failed with "Invalid hook call"; runs that reused a warm cache passed. Raise the peer ranges to react ^19.3.1 and lucide-react ^1.67.0 so the lockfile resolves a single copy of each. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
A 63-character fullnameOverride causes the JWKS Service name to collide with the fleet Service name, preventing independent deployment when JWKS is enabled.
1 open issue
1 file not reviewed
frontend/pnpm-lock.yaml: Generated file
What changed in this PR
Separates gateway key discovery from the controller’s management API. An optional HTTP/HTTPS listener serves only the gateway JWKS, while the fleet endpoint remains available for compatibility.
Changes:
- Removes management JWKS routing, adds the dedicated listener and validation, and shares TLS setup through the core crate.
- Adds Helm configuration and Service support, updates examples and documentation, and adds listener tests.
- Aligns shared UI peer dependencies and lockfile entries.
| File | Description |
|---|---|
schema/controller-config.md |
Documents dedicated listener settings. |
schema/controller-config.json |
Defines the listener configuration schema. |
README.md |
Explains JWKS endpoints and network isolation. |
frontend/ui/package.json |
Updates React and icon peer dependencies. |
frontend/pnpm-lock.yaml |
Aligns resolved frontend dependencies. |
examples/claude/controller.yaml |
Enables the dedicated local listener. |
examples/claude/agentgateway.yaml |
Points JWKS requests at the new port. |
docs/device-identity.md |
Clarifies where gateways retrieve public keys. |
deploy/helm/agentdesktop-controller/values.yaml |
Adds JWKS listener and Service options. |
deploy/helm/agentdesktop-controller/templates/service-jwks.yaml |
Defines the dedicated JWKS Service. |
deploy/helm/agentdesktop-controller/templates/NOTES.txt |
Displays the gateway JWKS URL. |
deploy/helm/agentdesktop-controller/templates/deployment.yaml |
Declares the optional JWKS container port. |
deploy/helm/agentdesktop-controller/templates/configmap.yaml |
Renders dedicated listener configuration. |
deploy/helm/agentdesktop-controller/templates/_helpers.tpl |
Adds JWKS enablement, naming, and port helpers. |
crates/core/src/tls.rs |
Adds shared server TLS configuration and tests. |
crates/core/src/lib.rs |
Exports the TLS module. |
crates/core/src/config.rs |
Adds listener settings, port validation, and tests. |
crates/core/Cargo.toml |
Adds TLS and test dependencies. |
crates/controller/src/management/auth/tests.rs |
Tests management JWKS removal and updates signatures. |
crates/controller/src/management.rs |
Removes JWKS routing and reuses shared TLS setup. |
crates/controller/src/gateway_jwt.rs |
Implements and tests the HTTP/HTTPS JWKS listener. |
crates/controller/src/application.rs |
Runs the optional listener alongside existing APIs. |
crates/controller/Cargo.toml |
Removes the direct Rustls dependency. |
Cargo.lock |
Records the Rust dependency changes. |
Review effort: Balanced
Comment on lines
+26
to
+27
| {{- printf "%s-jwks" (include "agentdesktop-controller.fullname" .) | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} |
This branch has not been deployed
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.

LLM gateways need the controller's JWKS to verify the JWTs it issues. The management router also served
/.well-known/jwks.json, so a gateway configured against it had to reach the management API, which can read the device inventory and delete devices. This is item A4 of the control plane hardening plan.gatewayJwt.jwksListener(listen,tls) serves only the JWKS: plain HTTP by default, or HTTPS with the controller certificate fromtls. Point gateways at it so a NetworkPolicy or firewall can allow them that one port. Gateways that export usage still need the fleet port. The listener must use a port other thanfleetListenandadminListen.gatewayJwt(orexistingConfigSecret) is set, the chart adds a<release>-jwksClusterIP Service, ajwkscontainer port and thejwksListenerconfig, controlled by newgatewayJwksvalues (port 8081; Service port 80, or 443 withtls). WithexistingConfigSecret, that configuration must setjwksListeneritself.examples/claudefetches the JWKS from the new listener on127.0.0.1:8081instead of the management port.docs/device-identity.mdand the generated controller config reference describe where gateways fetch the JWKS and why.Testing
cargo test -p agentdesktop-controller -p agentdesktop-core, clippy with-D warnings,cargo fmt --check, andcargo xtask schemawith no diff.helm lint, plushelm templatefor each variant (default,tls: true, disabled, nogatewayJwt,examples/kubernetes/values.yaml); the renderedcontroller.yamlvalidates against the generated schema.Not included
The controller settings page does not show the new listener yet.