mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-20 06:00:06 +08:00
* docs: refresh architecture and agent guides Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs: describe updated security architecture neutrally Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs: highlight new isolation primitives Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs(sandboxes): clarify how to disconnect Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs: align architecture and guides with current navigation Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs(extensibility): streamline extension authentication guidance Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Drew Newberry <anewberry@nvidia.com>
490 lines
20 KiB
Plaintext
490 lines
20 KiB
Plaintext
---
|
|
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
title: "Providers"
|
|
sidebar-title: "Overview"
|
|
description: "Create and manage credential providers that inject API keys and tokens into OpenShell sandboxes."
|
|
keywords: "Generative AI, Cybersecurity, Providers, Credentials, API Keys, Sandbox, Security"
|
|
position: 4
|
|
---
|
|
|
|
AI agents typically need credentials to access external services: an API key for the AI model provider, a token for GitHub or GitLab, and so on. OpenShell manages these credentials as first-class entities called *providers*.
|
|
|
|
Create and manage providers that supply credentials to sandboxes.
|
|
|
|
<Info>
|
|
Provider profiles define provider credentials, policy, and refresh behavior. See
|
|
[Profiles](/how-it-works/providers/profiles) for the profile format and custom
|
|
profile workflow.
|
|
</Info>
|
|
|
|
Provider profiles include metadata for known endpoints and binaries. View
|
|
the available profiles before creating a provider:
|
|
|
|
```shell
|
|
openshell profile list
|
|
openshell profile describe github
|
|
```
|
|
|
|
## Create a Provider
|
|
|
|
Providers can be created from local environment variables or with explicit credential values.
|
|
|
|
For refresh-backed providers such as `google-vertex-ai --from-gcloud-adc`, `openshell provider create` now waits for the gateway to configure refresh metadata and mint the initial access token before it reports success.
|
|
|
|
### From Local Credentials
|
|
|
|
The fastest way to create a provider is to let the CLI discover credentials from
|
|
your shell environment:
|
|
|
|
```shell
|
|
openshell provider create --name my-claude --type claude-code --from-existing
|
|
```
|
|
|
|
This reads `ANTHROPIC_API_KEY` or `CLAUDE_API_KEY` from your current environment
|
|
and stores them in the provider.
|
|
|
|
### With Explicit Credentials
|
|
|
|
Supply a credential value directly:
|
|
|
|
```shell
|
|
openshell provider create --name my-nvidia --type nvidia --credential NVIDIA_API_KEY=nvapi-example
|
|
```
|
|
|
|
### Bare Key Form
|
|
|
|
Pass a key name without a value to read the value from the environment variable
|
|
of that name:
|
|
|
|
```shell
|
|
openshell provider create --name my-nvidia --type nvidia --credential NVIDIA_API_KEY
|
|
```
|
|
|
|
This looks up the current value of `$NVIDIA_API_KEY` in your shell and stores it.
|
|
|
|
### From the Current OIDC Login
|
|
|
|
Profile-backed token-exchange providers can store the current gateway OIDC
|
|
access token as their subject credential. The subject credential stays
|
|
gateway-only and is not emitted into sandbox environment material or static
|
|
credential bindings:
|
|
|
|
```shell
|
|
openshell provider create \
|
|
--name custom-api \
|
|
--type custom-api \
|
|
--from-oidc-token
|
|
```
|
|
|
|
OpenShell infers the destination credential from the provider profile when the
|
|
profile has exactly one `token_grant.subject_token.credential`. If the profile
|
|
has more than one, pass `--credential <key>`. Refresh the stored subject token
|
|
later with:
|
|
|
|
```shell
|
|
openshell provider update custom-api --from-oidc-token
|
|
```
|
|
|
|
This copies the current OIDC access token and expiry from the active gateway
|
|
login. This requires an active named gateway that was registered for OIDC. If
|
|
the stored gateway access token is expired and a refresh token is available, the
|
|
CLI refreshes it before storing the provider credential. It does not store the
|
|
OIDC refresh token in the provider.
|
|
|
|
`--from-existing` uses profile-backed discovery. The requested `--type` must
|
|
name an imported provider profile with a `discovery` section. If no matching
|
|
profile exists, the CLI returns an error instead of falling back to legacy
|
|
discovery.
|
|
|
|
Create a provider whose profile requires no static credentials without a
|
|
credential source:
|
|
|
|
```shell
|
|
openshell provider create --name public-pypi --type pypi
|
|
```
|
|
|
|
Provider creation rejects profileless types. For a custom GitLab deployment,
|
|
import a profile with the deployment's endpoints and then create an instance
|
|
using that profile ID. Existing profileless provider records remain usable, but
|
|
OpenShell does not create new ones.
|
|
|
|
Update a custom provider profile after exporting it, editing its endpoints,
|
|
binaries, or credential metadata, and preserving the exported `resource_version`:
|
|
|
|
```shell
|
|
openshell profile export my-api -o yaml > my-api-profile.yaml
|
|
openshell profile update my-api -f my-api-profile.yaml
|
|
```
|
|
|
|
Import remains create-only and fails if the profile ID already exists. Use
|
|
`profile update <id>` for existing profiles. Interceptor-managed
|
|
profiles are read-only. The target ID must match the profile ID in the file. Update accepts
|
|
one file at a time and rejects stale resource versions. Updated profile policy
|
|
applies to all provider instances of that type on the next sandbox config sync.
|
|
|
|
Delete one or more custom provider profiles by ID:
|
|
|
|
```shell
|
|
openshell profile delete custom-api custom-alt
|
|
```
|
|
|
|
When a multi-profile delete fails for one entry, the CLI reports that profile's
|
|
failure and continues with the remaining IDs. The command exits with an error
|
|
after it attempts every requested deletion if any entry failed.
|
|
|
|
<Warning>
|
|
Static credentials require an imported provider profile. Profiles
|
|
normally define at least one endpoint. An endpointless profile requires an
|
|
explicit `credential_binding.provider` on a sandbox policy endpoint. OpenShell
|
|
does not activate static credentials from a profileless provider because it
|
|
cannot determine which credential definition applies.
|
|
</Warning>
|
|
|
|
## Manage Providers
|
|
|
|
List, inspect, update, and delete providers from the active gateway.
|
|
|
|
List all providers:
|
|
|
|
```shell
|
|
openshell provider list
|
|
```
|
|
|
|
Use `-o json` or `-o yaml` for machine-readable output:
|
|
|
|
```shell
|
|
openshell provider list -o json
|
|
openshell provider list -o yaml
|
|
```
|
|
|
|
Structured list output is an envelope with `providers` and
|
|
`next_page_token` fields. Each provider includes metadata (`id`, `name`,
|
|
`type`), credential key names, config key names, labels, creation timestamp,
|
|
resource version, and credential expiration times. Only credential and config
|
|
keys are exposed, never values, preventing accidental credential leakage in
|
|
logs or output. Pass the returned token to `--page-token` to continue. For
|
|
details on how credentials are injected into sandboxes, refer to
|
|
[Credential Injection](#how-credential-injection-works).
|
|
|
|
Inspect a provider:
|
|
|
|
```shell
|
|
openshell provider get my-claude
|
|
```
|
|
|
|
Update a provider's credentials:
|
|
|
|
```shell
|
|
openshell provider update my-claude --from-existing
|
|
```
|
|
|
|
To wait until attached sandboxes apply the updated credentials, add `--wait`:
|
|
|
|
```shell
|
|
openshell provider update my-claude --from-existing --wait --timeout 30 --output json
|
|
```
|
|
|
|
The update records which sandboxes are attached before saving the new credentials. Its result includes a change ID and outcome for each of those sandboxes. Sandboxes attached later are outside this wait. The timeout applies to the whole group; if any selected sandbox fails or times out, the command exits with an error and still reports each sandbox's outcome. When no sandboxes are attached, it reports the saved change and an empty target list.
|
|
|
|
Credential refresh status tells you whether OpenShell obtained credentials. [Provider readiness](/how-it-works/providers/profiles#inspect-provider-readiness) tells you whether the sandbox applied the credentials, policy, and environment for new processes. It does not test access to a backend model or cancel requests already sent upstream.
|
|
|
|
After a static credential update completes, launch a new client process to use the updated reference. An existing process keeps its revision-scoped reference; readiness does not make that old reference resolve the new value. Acknowledged detach revokes retained references and removes them from environments for future launches.
|
|
|
|
Set or clear a credential expiry timestamp:
|
|
|
|
```shell
|
|
openshell provider update my-graph \
|
|
--credential MS_GRAPH_ACCESS_TOKEN="$MS_GRAPH_ACCESS_TOKEN" \
|
|
--credential-expires-at MS_GRAPH_ACCESS_TOKEN=1767225600000
|
|
```
|
|
|
|
Use `0` as the timestamp to clear expiry for a credential key.
|
|
|
|
## Credential Refresh
|
|
|
|
Provider refresh stores non-injectable refresh material separately from the
|
|
provider's current credential values. The gateway can mint OAuth2 refresh-token
|
|
tokens, OAuth2 client credentials tokens, Google service account JWT tokens, and
|
|
AWS STS temporary credentials, then write the current access token back to the
|
|
provider record for sandbox injection.
|
|
|
|
Configure refresh metadata for one injectable credential key:
|
|
|
|
```shell
|
|
openshell provider refresh configure my-graph \
|
|
--credential-key MS_GRAPH_ACCESS_TOKEN \
|
|
--strategy oauth2-client-credentials \
|
|
--material tenant_id="$TENANT_ID" \
|
|
--material client_id="$CLIENT_ID" \
|
|
--secret-material-env client_secret=CLIENT_SECRET \
|
|
--credential-expires-at 1767225600000
|
|
```
|
|
|
|
Pass secret material with `--secret-material-env KEY[=ENVVAR]` (`ENVVAR`
|
|
defaults to `KEY`): the CLI reads the value from its own environment, so the
|
|
secret never appears in the host process table the way an expanded
|
|
`--material KEY="$VALUE"` argument would, and `KEY` is automatically marked
|
|
secret. Keep non-secret material on `--material`; `--secret-material-key KEY`
|
|
still marks a key supplied through `--material` as secret.
|
|
|
|
Check refresh status:
|
|
|
|
```shell
|
|
openshell provider refresh status my-graph
|
|
```
|
|
|
|
Delete refresh metadata for a credential:
|
|
|
|
```shell
|
|
openshell provider refresh delete my-graph \
|
|
--credential-key MS_GRAPH_ACCESS_TOKEN
|
|
```
|
|
|
|
Force a gateway-managed refresh for one credential:
|
|
|
|
```shell
|
|
openshell provider refresh rotate my-graph --credential-key MS_GRAPH_ACCESS_TOKEN
|
|
```
|
|
|
|
### AWS STS
|
|
|
|
The gateway calls `sts:AssumeRole` and writes three short-lived credentials
|
|
(`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) to the
|
|
provider record atomically.
|
|
The `aws` and `aws-s3` profiles declare `AWS_SECRET_ACCESS_KEY` and
|
|
`AWS_SESSION_TOKEN` as `additional_outputs` of the `AWS_ACCESS_KEY_ID` refresh,
|
|
so all three credentials are gateway-minted. Create the provider with
|
|
`--runtime-credentials` — no placeholder credential is needed.
|
|
|
|
```shell
|
|
openshell provider create --name my-aws --type aws-s3 --runtime-credentials
|
|
|
|
openshell provider refresh configure my-aws \
|
|
--credential-key AWS_ACCESS_KEY_ID \
|
|
--strategy aws-sts-assume-role \
|
|
--material role_arn="arn:aws:iam::123456789012:role/SandboxS3Writer" \
|
|
--material session_name="openshell-sandbox"
|
|
|
|
openshell provider refresh rotate my-aws --credential-key AWS_ACCESS_KEY_ID
|
|
```
|
|
|
|
The gateway resolves its own AWS credentials using the default credential chain
|
|
(instance role, IRSA, ECS task role, environment variables). For gateways not
|
|
running on AWS, provide explicit IAM keys via refresh material:
|
|
|
|
```shell
|
|
openshell provider refresh configure my-aws \
|
|
--credential-key AWS_ACCESS_KEY_ID \
|
|
--strategy aws-sts-assume-role \
|
|
--material role_arn="arn:aws:iam::123456789012:role/SandboxS3Writer" \
|
|
--material aws_access_key_id="$AWS_ACCESS_KEY_ID" \
|
|
--secret-material-env aws_secret_access_key=AWS_SECRET_ACCESS_KEY
|
|
```
|
|
|
|
Pass the long-lived secret with `--secret-material-env` so the CLI reads it
|
|
from its own environment instead of expanding it into a process argument,
|
|
where it would be visible in the host process table.
|
|
|
|
For temporary source credentials (AWS SSO or a prior `AssumeRole`), also pass a
|
|
session token with `--secret-material-env aws_session_token=AWS_SESSION_TOKEN`.
|
|
It requires the `aws_access_key_id` and `aws_secret_access_key` pair.
|
|
|
|
Use the generic `aws` profile type for multi-service access and scope endpoints
|
|
via sandbox network policy. Use `aws-s3` for S3-specific endpoint rules.
|
|
|
|
The proxy re-signs requests using SigV4 before forwarding to AWS. Both curl and
|
|
Python boto3 are supported.
|
|
|
|
External refresh systems should continue to push new current credentials through
|
|
`openshell provider update`. The `--credential-expires-at` option works for
|
|
static credentials, externally refreshed credentials, and gateway-managed
|
|
refresh strategies.
|
|
|
|
Delete a provider:
|
|
|
|
```shell
|
|
openshell provider delete my-claude
|
|
```
|
|
|
|
Delete multiple providers by listing their names in one command:
|
|
|
|
```shell
|
|
openshell provider delete my-claude my-github
|
|
```
|
|
|
|
When a multi-provider delete fails for one entry, the CLI reports that
|
|
provider's failure and continues with the remaining names. The command exits
|
|
with an error after it attempts every requested deletion if any entry failed.
|
|
|
|
## Attach Providers to Sandboxes
|
|
|
|
Pass one or more `--provider` flags when creating a sandbox:
|
|
|
|
```shell
|
|
openshell sandbox create --from registry.example.com/your-org/claude-agent:latest --provider my-claude --provider my-github -- claude
|
|
```
|
|
|
|
Each `--provider` flag attaches one provider. The sandbox receives eligible
|
|
credentials from every attached provider as placeholders at runtime. Each
|
|
static credential resolves only for endpoints in that provider's profile, or
|
|
for explicitly bound sandbox policy endpoints when the profile is endpointless.
|
|
Profile-managed providers also contribute provider-generated network policy
|
|
entries. Endpoint binding independently limits where each static credential can
|
|
be resolved.
|
|
|
|
<Info>
|
|
Use `--provider` to attach providers when creating a sandbox. To change the
|
|
providers attached to a running sandbox, use `openshell sandbox provider attach`
|
|
and `openshell sandbox provider detach`. See
|
|
[Profiles](/how-it-works/providers/profiles#attach-and-detach-providers) for details.
|
|
|
|
</Info>
|
|
|
|
### Auto-Discovery Shortcut
|
|
|
|
Name a provider with `--provider`. If no provider by that name exists and the
|
|
name matches an imported profile ID, the CLI creates one from local profile
|
|
discovery, so you do not need to create it separately:
|
|
|
|
```shell
|
|
openshell sandbox create --from registry.example.com/your-org/claude-agent:latest --provider claude-code -- claude
|
|
```
|
|
|
|
This finds your `ANTHROPIC_API_KEY`, creates a `claude-code` provider, attaches
|
|
it to the sandbox, and launches Claude Code. Add `--auto-providers` to skip the
|
|
confirmation prompt, or `--no-auto-providers` to skip creation entirely.
|
|
|
|
A provider is attached only when you name it. OpenShell does not derive one from
|
|
the trailing command: a profile's `binaries` list authorizes a binary to reach
|
|
that profile's endpoints, which is not a statement that running the binary asks
|
|
for the provider. A sandbox with no providers is a normal, fully supported
|
|
state.
|
|
|
|
## How Credential Injection Works
|
|
|
|
The agent process inside the sandbox never sees real credential values. At
|
|
startup, OpenShell replaces each credential with an opaque placeholder token in
|
|
the agent's environment. When the agent sends an HTTP request containing a
|
|
placeholder, the proxy resolves it immediately before forwarding the request.
|
|
|
|
Static credential resolution has two independent authorization boundaries:
|
|
|
|
1. Network policy must allow the calling binary and request destination.
|
|
2. The credential binding must include the request host, port, and path. Profile
|
|
endpoints provide the binding by default. An endpointless profile can use a
|
|
sandbox policy endpoint that names the attached provider instance through
|
|
`credential_binding.provider`.
|
|
|
|
Both checks must pass. A provider profile endpoint does not grant network access
|
|
unless provider policy composition or the sandbox's own policy allows the
|
|
request. A plain sandbox policy endpoint does not grant credential use unless
|
|
the profile already covers it or the endpoint explicitly binds an endpointless
|
|
provider.
|
|
|
|
Every static credential declared by a provider receives the complete endpoint set from that provider's
|
|
profile, or the explicitly bound sandbox policy endpoints for an endpointless
|
|
profile.
|
|
|
|
Credential resolution requires the proxy to handle the request as HTTP. Raw
|
|
`tls: skip` and non-HTTP tunnels remain opaque and do not support credential
|
|
rewrite. Refer to [Profiles](/how-it-works/providers/profiles#understand-static-credential-endpoint-binding)
|
|
for endpoint matching and migration guidance.
|
|
|
|
### Supported injection locations
|
|
|
|
The proxy resolves credential placeholders in the following parts of an HTTP request:
|
|
|
|
| Location | How the agent uses it | Example |
|
|
|---|---|---|
|
|
| Header value | Agent reads `$API_KEY` from env and places it in a header. | `Authorization: Bearer <placeholder>` |
|
|
| Header value (Basic auth) | Agent base64-encodes `user:<placeholder>` in an `Authorization: Basic` header. The proxy decodes, resolves, and re-encodes. | `Authorization: Basic <base64>` |
|
|
| Query parameter value | Agent places the placeholder in a URL query parameter. | `GET /api?key=<placeholder>` |
|
|
| URL path segment | Agent builds a URL with the placeholder in the path. Supports concatenated patterns. | `POST /bot<placeholder>/sendMessage` |
|
|
| Supported request body | An inspected REST endpoint opts in with `request_body_credential_rewrite: true`. | `{"api_key":"<placeholder>"}` |
|
|
| WebSocket text message | A REST or WebSocket endpoint opts in with `websocket_credential_rewrite: true`. | `{"token":"<placeholder>"}` |
|
|
| AWS SigV4 signing | An endpoint configures `credential_signing`, and the proxy signs with the endpoint-bound AWS credentials. | `credential_signing: sigv4` |
|
|
|
|
The proxy does not rewrite cookies, response content, unsupported request
|
|
bodies, or WebSocket binary frames.
|
|
|
|
### Fail-closed behavior
|
|
|
|
If policy allows a request but the credential binding does not include the
|
|
request endpoint, the proxy rejects the request with HTTP 403 and the
|
|
`credential_endpoint_mismatch` reason. It emits a denied activity event and a
|
|
security finding without recording the secret, placeholder, environment key, or
|
|
query string.
|
|
|
|
Unknown, malformed, expired, or otherwise unresolved placeholders also fail
|
|
closed instead of being forwarded to the upstream service.
|
|
|
|
### Inspect an Endpoint Binding
|
|
|
|
Export the profile used by a provider to inspect its credential boundary:
|
|
|
|
```shell
|
|
openshell profile export github -o yaml
|
|
```
|
|
|
|
For the GitHub example profile, `GITHUB_TOKEN` and `GH_TOKEN` can resolve for
|
|
the profile's `api.github.com:443` and `github.com:443` endpoints. Even if a
|
|
sandbox policy allows `uploads.example.com:443`, sending either placeholder
|
|
there returns `credential_endpoint_mismatch`.
|
|
|
|
For another service, define the credential in its own provider profile. Put
|
|
stable endpoints in the profile, or leave the profile endpointless and bind
|
|
each concrete provider instance from sandbox policy. Refer to
|
|
[Profiles](/how-it-works/providers/profiles#provider-profiles) for the profile workflow
|
|
and schema.
|
|
|
|
## Available Provider Types
|
|
|
|
The provider types available to `--type` are the IDs of the profiles you
|
|
imported. List them:
|
|
|
|
```shell
|
|
openshell provider list-profiles
|
|
```
|
|
|
|
The repository's
|
|
[`providers/`](https://github.com/NVIDIA/OpenShell/tree/main/providers) directory
|
|
ships example profiles for GitHub, PyPI, and the major inference and agent
|
|
providers. Each file's header states the credential environment variables it
|
|
declares, the binaries it authorizes, and the endpoints it grants. Read it,
|
|
adjust the binary paths for your image, and import your copy.
|
|
|
|
<Note>
|
|
`ANTHROPIC_API_KEY` is an API key from [console.anthropic.com](https://console.anthropic.com), not a subscription token. Subscription users must generate a separate API key from the Anthropic Console.
|
|
</Note>
|
|
|
|
## Inference Provider Access
|
|
|
|
Attach an inference provider to each sandbox that needs it and configure the
|
|
workload to call the provider's native endpoint. The workload, not OpenShell,
|
|
selects the model and request timeout.
|
|
|
|
| Provider | Example profile | Native base URL | Credential variable |
|
|
|---|---|---|---|
|
|
| OpenAI | `openai` | `https://api.openai.com/v1` | `OPENAI_API_KEY` |
|
|
| Anthropic | `anthropic` | `https://api.anthropic.com` | `ANTHROPIC_API_KEY` |
|
|
| NVIDIA API Catalog | `nvidia` | `https://integrate.api.nvidia.com/v1` | `NVIDIA_API_KEY` |
|
|
| DeepInfra | `deepinfra` | `https://api.deepinfra.com/v1/openai` | `DEEPINFRA_API_KEY` |
|
|
| Google Vertex AI | `google-vertex-ai` | Region and model dependent | `GOOGLE_VERTEX_AI_TOKEN` or `GOOGLE_VERTEX_AI_SERVICE_ACCOUNT_TOKEN` |
|
|
|
|
An OpenAI-compatible protocol does not make the `openai` profile safe for an
|
|
arbitrary host. Baseten, Bitdeer, Groq, Ollama, LM Studio, self-hosted NIM, and
|
|
other alternate endpoints need their own profile declaring the actual host,
|
|
port, credential, and allowed binaries. See
|
|
[Inference](/how-it-works/inference) for complete examples
|
|
and migration guidance.
|
|
|
|
## Next Steps
|
|
|
|
Explore related topics:
|
|
|
|
- To manage workspace access for providers, refer to [Workspaces](/how-it-works/workspaces).
|
|
- To control what the agent can access, refer to [Policies](/how-it-works/policies/overview).
|
|
- To use the default workload image, refer to [Sandboxes](/how-it-works/sandboxes/overview#default-workload-image).
|
|
- To view the complete field reference for the policy YAML, refer to the [Policy Schema Reference](/how-it-works/policies/schema).
|