For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
OpenShell
Route OpenShell CLI traffic through agentgateway to sandboxed agents on Kubernetes.
OpenShell runs AI agents in sandboxes. Use agentgateway as its Kubernetes Gateway API implementation to route OpenShell CLI requests to the OpenShell gateway.
The OpenShell Helm chart creates a dedicated Gateway and GRPCRoute. Setting grpcRoute.gateway.className to agentgateway selects agentgateway to provision the proxy.
Architecture
The following diagram shows how an OpenShell CLI request reaches a sandbox through agentgateway.
flowchart LR
cli["OpenShell CLI"] -->|"gRPC over HTTP/2"| proxy["agentgateway proxy"]
subgraph cluster["Kubernetes cluster"]
proxy -->|"gRPC over HTTP/2"| gateway["OpenShell gateway"]
gateway --> sandbox["Agent Sandbox workload"]
end
- The OpenShell CLI sends gRPC requests over HTTP/2 to the agentgateway proxy.
- The agentgateway proxy forwards the requests over HTTP/2 to the OpenShell gateway inside the Kubernetes cluster.
- The OpenShell gateway creates and manages the Agent Sandbox workloads.
This guide routes OpenShell’s gRPC control traffic through agentgateway. It does not configure agent LLM or MCP traffic.
Warning
This example disables TLS and authentication for local development and uses a loopback port-forward for CLI access. Before exposing the gateway, configure TLS and authentication as described in Secure external access.
Before you begin
- A Kubernetes cluster whose CNI enforces ingress and egress NetworkPolicy. Kind’s default CNI does not enforce NetworkPolicy; use a CNI that does.
- Cluster nodes with a Linux kernel that meets the OpenShell sandbox requirements. For details, see the OpenShell support matrix.
- agentgateway installed, with the
agentgatewayGatewayClass and Gateway API CRDs, including GRPCRoute. The example in the agentgateway repository was tested with agentgateway v1.6.0. - Agent Sandbox installed, including its CRDs and controller. The example was tested with Agent Sandbox v1.0.6.
- Helm,
kubectl, and the OpenShell CLI. Use OpenShell v0.1.3 to match the chart in this guide. - Network access to pull images, manifests, and Helm charts, and available loopback port
18080.
Configure OpenShell
Save the following configuration as
openshell-values.yaml.server: disableTls: true auth: allowUnauthenticatedUsers: true grpcRoute: enabled: true gateway: create: true className: agentgateway name: openshell-ingressReview the following table to understand this configuration.
Field Description server.disableTlsTurns off TLS on the OpenShell gateway so that the proxy can forward plaintext HTTP/2. Use for local development only. server.auth.allowUnauthenticatedUsersAccepts CLI requests without credentials and treats them as a local developer. Use for local development only. grpcRoute.enabledCreates the GRPCRoute that sends gRPC requests to the OpenShell Service. grpcRoute.gateway.createCreates a Gateway in the release namespace. grpcRoute.gateway.classNameSelects the agentgatewayGatewayClass, so that agentgateway provisions the proxy.grpcRoute.gateway.nameNames the Gateway openshell-ingress. The proxy Service takes the name of the Gateway, so the name must differ from the OpenShell Service namedopenshell.Install OpenShell with the example values.
helm upgrade --install openshell \ oci://ghcr.io/nvidia/openshell/helm-chart \ --version 0.1.3 \ --create-namespace \ --namespace openshell \ --values openshell-values.yaml \ --wait \ --timeout 10mThe chart creates an HTTP listener on port
80and routes gRPC requests to the OpenShell Service on port8080. OpenShell manages the sandbox workloads; agentgateway manages the ingress proxy.Example output, truncated:
NAME: openshell NAMESPACE: openshell STATUS: deployed REVISION: 1
Verify the integration
Wait for the Gateway, route, and workloads to become ready.
kubectl wait --for=condition=Programmed gateway/openshell-ingress \ -n openshell --timeout=5m kubectl wait \ --for=jsonpath='{.status.parents[0].conditions[?(@.type=="Accepted")].status}'=True \ grpcroute/openshell -n openshell --timeout=5m kubectl wait \ --for=jsonpath='{.status.parents[0].conditions[?(@.type=="ResolvedRefs")].status}'=True \ grpcroute/openshell -n openshell --timeout=5m kubectl rollout status deployment/openshell-ingress \ -n openshell --timeout=5m kubectl rollout status statefulset/openshell \ -n openshell --timeout=5mExample output:
gateway.gateway.networking.k8s.io/openshell-ingress condition met grpcroute.gateway.networking.k8s.io/openshell condition met grpcroute.gateway.networking.k8s.io/openshell condition met deployment "openshell-ingress" successfully rolled out partitioned roll out complete: 1 new pods have been updated...In one terminal, forward the agentgateway proxy Service.
kubectl port-forward -n openshell service/openshell-ingress 18080:80Example output:
Forwarding from 127.0.0.1:18080 -> 80 Forwarding from [::1]:18080 -> 80In a second terminal, register the gateway and run a sandbox smoke test. Run these commands in a subshell with a temporary configuration directory to preserve your existing OpenShell gateway registrations and shell environment.
( export XDG_CONFIG_HOME="$(mktemp -d)" openshell gateway add http://127.0.0.1:18080 \ --local --name agentgateway-example openshell status openshell sandbox create \ --name agw-example --detach -- sleep 3600 openshell sandbox exec agw-example \ --no-login-shell -- sh -c 'printf agentgateway-openshell' openshell sandbox delete agw-example rm -rf "$XDG_CONFIG_HOME" )The first two commands print the registration and the gateway status.
✓ Gateway 'agentgateway-example' added and set as active Endpoint: http://127.0.0.1:18080 Type: local Auth: plaintext Server Status Gateway: agentgateway-example Server: http://127.0.0.1:18080 Status: Connected Authentication: Not required (gateway) Version: 0.1.3The exec command prints
agentgateway-openshell. Stop the port-forward with Control-C when testing is complete.
Secure external access
For external access, terminate client TLS at agentgateway, encrypt the connection from agentgateway to OpenShell, and let OpenShell validate OpenID Connect (OIDC) bearer tokens and authorize CLI operations.
OpenShell CLI → HTTPS → agentgateway → TLS → OpenShell gatewayThis section upgrades the release that you installed in Configure OpenShell. You need the following:
- A DNS hostname for the agentgateway proxy Service. The example uses
gateway.example.com. - A certificate and key for that hostname, issued by a certificate authority (CA) that your CLI clients trust.
- An OIDC provider that issues access tokens for OpenShell. The example uses the audience and client ID
openshell-cli, and roles in therealm_access.rolesclaim. Configure both theopenshell-adminandopenshell-userroles, and assign the appropriate role to each caller. For login flows and role configuration, see the OpenShell OIDC authentication reference and the OpenShell access control reference. - An OIDC issuer URL that the OpenShell pod can reach over HTTPS. OpenShell contacts the issuer when it starts, so the pod crash-loops if the issuer is unreachable. If the issuer uses a private CA, set
server.oidc.caConfigMapNameto a ConfigMap that holds the CA bundle in theca.crtkey. - Gateway API CRDs v1.4 or later, which serve BackendTLSPolicy at
v1in the standard channel.
Replace the example hostname and identity provider settings with your own values.
Create a TLS Secret in the
openshellnamespace with the certificate and key for your external hostname. For certificate requirements and listener configuration, see agentgateway HTTPS listeners.kubectl -n openshell create secret tls openshell-ingress-tls \ --cert=tls.crt --key=tls.keySave the following complete configuration as
openshell-secure-values.yaml. Use this file instead ofopenshell-values.yaml.server: disableTls: false auth: allowUnauthenticatedUsers: false tls: enableMtls: false gatewayConfig: openshell.gateway.oidc: issuer: https://idp.example.com/realms/openshell audience: openshell-cli roles_claim: realm_access.roles admin_role: openshell-admin user_role: openshell-user grpcRoute: enabled: true hostnames: - gateway.example.com gateway: create: true className: agentgateway name: openshell-ingress listener: protocol: HTTPS port: 443 tls: certificateRefs: - name: openshell-ingress-tls backendTLSPolicy: enabled: trueReview the following table to understand this configuration.
Field Description server.disableTlsSet to falseso that the OpenShell gateway serves TLS. agentgateway re-encrypts the connection to this endpoint.server.auth.allowUnauthenticatedUsersSet to falseto reject requests without valid credentials. Keep anonymous access disabled; this configuration uses the OpenShell OIDC login flow.server.tls.enableMtlsSet to false. Client TLS terminates at agentgateway, so OpenShell cannot use the original CLI client certificate for identity, and the proxy cannot present a client certificate to OpenShell. OpenShell instead validates the bearer token that the CLI sends in the gRPCauthorizationmetadata, and applies the configured roles.gatewayConfig.openshell.gateway.oidcThe OIDC settings of the OpenShell gateway. The dotted name openshell.gateway.oidcis one key, the name of a TOML table, not nested fields.issuerThe OIDC issuer URL. The OpenShell pod must reach this URL over HTTPS when it starts. audienceThe audience claim that access tokens must carry. roles_claimThe dot-separated path to the roles array in the token. Keycloak uses realm_access.roles, Entra ID usesroles, and Okta usesgroups.admin_roleThe role that is required for admin operations. Set admin_roleanduser_roletogether.user_roleThe role that is required for standard user operations. grpcRoute.hostnamesThe hostnames that the GRPCRoute matches. Use the hostname on your certificate and DNS record. grpcRoute.gateway.listenerCreates an HTTPS listener on port 443that terminates client TLS at agentgateway.certificateRefsThe TLS Secret that you created in the previous step. grpcRoute.backendTLSPolicy.enabledCreates a BackendTLSPolicy for the OpenShell Service. By default, the chart’s PKI hook creates the CA ConfigMap openshell-backend-ca, and agentgateway validates the backend certificate against that CA and the hostnameopenshell.openshell.svc.cluster.local. The frontend certificate Secret and the backend CA ConfigMap secure different TLS connections. For more information, see agentgateway backend TLS.Upgrade the example release with the secure configuration.
--reset-valuesreplaces the development overrides with chart defaults and this values file. If you are adapting an existing deployment, include its other required settings in the file before upgrading.helm upgrade --install openshell \ oci://ghcr.io/nvidia/openshell/helm-chart \ --version 0.1.3 \ --namespace openshell \ --reset-values \ --values openshell-secure-values.yaml \ --wait \ --timeout 10mIf the upgrade times out or the
openshell-0pod crash-loops, check the pod logs first withkubectl logs openshell-0 -n openshell. If anOIDC discovery request failederror confirms that the pod cannot reach the issuer, correct the issuer settings. Then delete the pod. The StatefulSet uses theOrderedReadypod management policy, so a pod that never became ready blocks the rollout and is not replaced with the corrected configuration until you delete it. For more information, see Forced rollback in the Kubernetes docs.kubectl delete pod openshell-0 -n openshellRepeat the readiness checks from Verify the integration, and inspect the backend TLS policy. Confirm that its status reports
Accepted=TrueandResolvedRefs=True.kubectl describe backendtlspolicy openshell -n openshell kubectl get configmap openshell-backend-ca -n openshell kubectl get service openshell-ingress -n openshellExample output, truncated:
Status: Ancestors: Ancestor Ref: Group: gateway.networking.k8s.io Kind: Gateway Name: openshell-ingress Conditions: ... Reason: Accepted Status: True Type: Accepted ... Reason: ResolvedRefs Status: True Type: ResolvedRefs Controller Name: agentgateway.dev/agentgateway NAME DATA AGE openshell-backend-ca 1 4m17s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE openshell-ingress LoadBalancer 10.96.217.138 <external-ip> 443:30909/TCP 12mConfigure DNS and network access so that your hostname reaches the agentgateway proxy Service on HTTPS port
443.Register the HTTPS endpoint with the CLI and authenticate with your identity provider. The command opens a browser for the identity provider login, which uses the authorization code flow with Proof Key for Code Exchange (PKCE), and stores the resulting token. For CI and other non-interactive use, set the
OPENSHELL_OIDC_CLIENT_SECRETenvironment variable to use the client credentials flow instead. The CLI refreshes the token automatically. If the token refresh fails or reauthentication is required, runopenshell gateway login production.openshell gateway add https://gateway.example.com \ --name production \ --oidc-issuer https://idp.example.com/realms/openshell \ --oidc-client-id openshell-cli \ --oidc-audience openshell-cli openshell status -g productionCheck the authentication result as well as gateway reachability. A successful public health check alone does not demonstrate authenticated access.
Run a sandbox smoke test through the secure endpoint to verify authorized operations. These commands use your regular CLI configuration, which holds the
productionregistration. The-g productionflag selects the gateway that you registered in the previous step.openshell sandbox create -g production \ --name agw-secure --detach -- sleep 3600 openshell sandbox exec -g production agw-secure \ --no-login-shell -- sh -c 'printf agentgateway-openshell' openshell sandbox delete -g production agw-secureThe exec command prints
agentgateway-openshell.
Troubleshooting
- Gateway is not programmed: Confirm that the
agentgatewayGatewayClass exists and its controller is running. Inspectkubectl describe gateway openshell-ingress -n openshellfor listener and provisioning errors. - Route is not accepted or references are unresolved: Inspect
kubectl describe grpcroute openshell -n openshell. Its parent must beopenshell-ingressin theopenshellnamespace, and its backend must be Serviceopenshellon port8080. - Proxy Service name conflicts: Keep
grpcRoute.gateway.name=openshell-ingress. The chart’s default Gateway name would match the OpenShell backend Service name. - CLI cannot connect: Keep the port-forward running and register
http://127.0.0.1:18080. This local example uses plaintext HTTP/2; HTTPS requires a different listener and authentication configuration. - Sandbox creation waits several minutes and then fails: If the CLI reports
sandbox provisioning stream ended before reaching terminal phase, inspect the sandbox pod in theopenshellnamespace. Runkubectl get pods -n openshell, and thenkubectl logs <sandbox-pod> -n openshell -c agent. ALandlock probe child exitederror means that the node kernel does not provide the required Landlock support. See the kernel requirements in Before you begin. - Sandbox creation or exec fails for another reason: Confirm that Agent Sandbox is installed, inspect OpenShell and sandbox workload events, and verify that the CNI enforces the required NetworkPolicies.
- OpenShell pod crash-loops after the secure upgrade: Run
kubectl logs openshell-0 -n openshell. AnOIDC discovery request failederror means that the pod cannot reach the issuer URL over HTTPS. Correct the issuer settings and delete the pod as described in the upgrade step.
Clean up
Remove the release and namespace only if you created them for this example. For an existing OpenShell deployment, restore its previous Helm values instead.
helm uninstall openshell --namespace openshell --ignore-not-found
kubectl delete namespace openshell --ignore-not-foundThe secure example also registered the production gateway in your CLI configuration, and deleting the cluster resources does not remove that registration. Remove it, along with its stored tokens.
openshell gateway remove production