Providers v2
Providers v2 turns providers from credential records into profile-backed access bundles. A provider profile describes the credentials, endpoints, binaries, policy rules, and refresh behavior for a provider type. A provider instance stores the concrete credential and config values for one gateway.
Use Providers v2 when you want provider-owned policy rules to travel with provider credentials. For example, a GitHub provider can describe both GITHUB_TOKEN and the GitHub API endpoints that a sandbox needs, so users do not have to copy the same network policy into every sandbox.
Why Providers v2 Exists
Provider credentials and network policy were previously configured through separate workflows. A user could create a GitHub provider that stored GITHUB_TOKEN, but the sandbox still needed a separate policy that allowed api.github.com, selected the right binaries, and configured REST enforcement.
Providers v2 keeps those pieces together:
Enable Providers v2
Provider profile policy composition is controlled by the gateway-level providers_v2_enabled setting. Enable it on the active gateway:
When the setting is disabled or unset, attached provider profiles do not add network policy entries to the effective policy. Static provider credential placeholders still use the profile endpoints as a resolution boundary.
To disable provider profile policy composition, delete the setting:
The feature flag controls provider-derived policy layers. It does not disable endpoint binding for static credential placeholders. Provider profiles can also declare dynamic token grants that the sandbox proxy resolves on demand for matching HTTP endpoints.
Available Features
Providers v2 currently includes these user-facing features:
- Built-in provider profiles loaded by the gateway by default.
- Gateway configuration can compose built-in, user-managed, and interceptor-vended profile sources. Selecting only an interceptor makes its catalog authoritative by omission.
openshell provider list-profileswith table, YAML, and JSON output.openshell provider profile export,import,update,lint, anddeletefor custom profiles.- Provider instances created from built-in or imported profile IDs with
openshell provider create --type <id>. - Provider instances whose submitted credentials can be stored by a configured gateway credential driver.
- Profile-backed credential discovery for explicit
openshell provider create --from-existingandopenshell provider update --from-existingflows. The built-ingoogle-vertex-aiprofile also supplements discovery with Vertex config env vars such asVERTEX_AI_PROJECT_IDandVERTEX_AI_REGION. - Just-in-time effective policy composition from sandbox policy plus attached provider profiles.
- Runtime sandbox provider lifecycle commands under
openshell sandbox provider list|attach|detach. - Credential refresh configuration with
openshell provider refresh status|configure|rotate|delete. - Credential expiry metadata with
openshell provider update --credential-expires-at; values accept Unix epoch milliseconds or ISO/RFC3339 timestamps. - Dynamic token grants that use the sandbox’s SPIFFE JWT-SVID as an OAuth2 client assertion and inject short-lived tokens into supported headers for matching profile endpoints.
- Endpoint-bound static credential placeholders. The sandbox proxy resolves a static credential only for request hosts, ports, and paths declared by its provider profile or explicitly bound in sandbox policy for an endpointless profile.
Understand Static Credential Endpoint Binding
Static credential endpoint binding prevents a placeholder for one service from resolving on a different policy-allowed service. OpenShell associates every static credential environment key with an endpoint boundary. Profile endpoints supply that boundary by default. For an endpointless profile, a sandbox policy endpoint can name the attached provider instance explicitly. The proxy checks the resulting association before it substitutes the real value.
A request can use a static credential only when all of these checks pass:
Network policy and credential binding serve different purposes. Network policy authorizes traffic. A credential binding authorizes use of one provider instance’s credentials at an admitted endpoint. A credential binding cannot widen sandbox network policy.
For example, this profile endpoint binds all static credential environment keys
from the profile to api.example.com:443 under /v1:
The path /v1/** matches /v1 and its descendants. An empty path, **, or
/** matches every path on the selected host and port. Other path values use
glob matching. OpenShell removes the query string and uses a canonical,
secret-redacted path for this check, so a credential embedded in a request path
does not need to be revealed before authorization.
Use an explicit sandbox policy binding when the profile intentionally defines credentials without defining service endpoints. The policy names the concrete provider instance, not the profile type:
The provider must be attached to the sandbox and must select an endpointless
profile. OpenShell rejects the complete policy update if the provider is
unattached, has no profile, or selects a profile that already defines endpoints.
This keeps one source of credential-binding authority for each provider.
credential_binding is sandbox-scoped and is not accepted in a gateway-global
policy.
For AWS endpoints, the binding and signing fields have separate jobs.
credential_binding.provider selects the provider instance that supplies
credentials. credential_signing, signing_service, and signing_region
control how the proxy applies those credentials:
The binding applies to CONNECT and forward-proxy HTTP requests, including
headers, Basic and Bearer authorization, URL paths, query parameters, opted-in
request bodies, AWS SigV4 signing, and opted-in WebSocket text messages. Raw
tls: skip and non-HTTP tunnels do not perform static credential substitution.
Before it activates a sandbox policy, OpenShell verifies that every endpoint
with credential_signing has an attached profile that declares
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. An endpoint-bearing profile must
cover the signed host, port, and path. An endpointless profile must be selected
by credential_binding.provider on that endpoint. A missing or mismatched
source rejects the whole policy update with FAILED_PRECONDITION.
If an HTTP request contains a known placeholder at a destination outside its binding, OpenShell returns HTTP 403 with this response:
The sandbox logs include the destination and
credential_endpoint_mismatch. OCSF output includes both the denied activity
and a detection finding. These events omit credential values, placeholders,
environment keys, and query strings.
For opted-in WebSocket text-message rewriting, the mismatch can occur after the HTTP 101 upgrade has completed. OpenShell closes that WebSocket with policy violation code 1008 instead of returning an HTTP response.
Binding updates apply to both current and retained placeholder generations. For gateway-managed refresh credentials, automatic and manual token rotation keep one opaque workload placeholder and replace only its current resolver value. This lets a long-running process use each newly minted access token without restarting. OpenShell does not retain older values behind this stable placeholder.
Explicitly configuring refresh again starts a new authorization epoch, even when the provider and credential key are unchanged. Reconfiguration, provider replacement or detachment, refresh deletion, and endpoint-boundary changes revoke the old placeholder. Updating a provider profile changes the binding on the next sandbox provider-environment sync.
Static credentials require at least one usable binding. OpenShell withholds only the static credential keys and associated expiry and binding metadata from an endpointless selected profile when no sandbox policy endpoint explicitly binds that provider. It retains that provider’s generated non-secret configuration and valid endpoint-bound static credentials from other attached providers.
After upgrading a gateway and supervisor to a release with endpoint binding, restart or recreate older running sandboxes. A new gateway withholds static credential material from supervisors that do not advertise binding support. Rotate attached static credentials after upgrading when an older sandbox may have received their real values.
When upgrading from revision-scoped refresh placeholders to stable refresh handles, restart each existing workload once so it receives the new placeholder. Later access-token rotations do not require workload restarts.
Roadmap
The following Providers v2 design items are not part of the current behavior:
Use Inference Routing for the current inference.local model.
Provider Profiles
A provider profile defines a provider type. It contains metadata, credential declarations, endpoint policy, binary policy, inference metadata, and optional credential refresh metadata.
List available profiles:
By default the gateway lists built-in profiles plus custom profiles imported through the profile APIs. When a configured gateway interceptor vends an authoritative provider profile catalog, that catalog becomes the visible source of truth: list, export, provider creation, policy composition, and sandbox provider environment resolution use the interceptor-vended profiles instead of built-in or user-imported profiles.
Built-in Providers v2 profiles currently include:
Export a built-in profile as YAML:
Lint a profile before importing it:
Import one profile file:
Import all non-recursive *.yaml, *.yml, and *.json files from a directory:
Import is create-only. It fails if a custom profile with the same ID already exists.
Update an existing custom profile by exporting the current custom profile, editing the file, and submitting the edited file back:
Exported custom profiles include resource_version. OpenShell requires that version during update so stale files cannot silently overwrite newer profile definitions. The target ID in the command must match the profile ID in the file. Update accepts one file at a time. If an update would make dynamic token grants ambiguous for an attached sandbox, OpenShell rejects it before changing the profile.
Custom profile IDs must use lowercase kebab-case with a-z, 0-9, and -. Built-in profile IDs and legacy provider aliases are reserved. Built-in and interceptor-managed profiles are read-only through the profile APIs. OpenShell also rejects deleting a custom profile while a sandbox-attached provider uses it.
Category Enum
The category field controls how openshell provider list-profiles groups profiles. Use one of these canonical YAML values:
Profile Schema
Provider profile YAML and JSON use this shape. Treat this as a field map, not a profile to import verbatim. The endpoint and rule fields mirror the network policy schema used under network_policies. Refer to Policy Schema Reference for field semantics.
Use annotations only for non-secret metadata such as source, signature, or governance markers. OpenShell preserves annotations through profile import, export, and interceptor-managed profile snapshots.
Profile Sections
id, display_name, and description identify the profile. id is the value passed to openshell provider create --type.
category groups profiles in openshell provider list-profiles. Use one of the values in the category enum.
credentials declares the credential names, environment variables, auth metadata, optional refresh metadata, and optional dynamic token grant metadata for the provider type. The auth_style field accepts basic, bearer, header, query, or path. When auth_style is path, set path_template to a URL path containing the {credential} placeholder exactly once (for example, /v1/{credential}/resources). Static credentials are exposed as placeholder environment variables and resolved in outbound HTTP requests only at their binding endpoints. Every static credential environment key receives the full profile endpoint set when the profile defines endpoints. An endpointless profile requires explicit sandbox policy bindings for each attached provider instance. Dynamic token grants are resolved by the sandbox proxy on demand for matching profile endpoints and support bearer or header placement. Credential environment variable names must not use the reserved v<digits>_ prefix, such as v10_GITHUB_TOKEN, because OpenShell uses that namespace for revision-scoped placeholders.
discovery controls what --from-existing scans when
providers_v2_enabled=true. Each entry in discovery.credentials must name a
credential declared under credentials. OpenShell scans the referenced
credential’s env_vars in order and stores the first non-empty local
environment value under the actual environment variable key.
endpoints contains the same endpoint object shape as sandbox network policy. A profile can use access presets, protocol-specific allow rules, deny rules, WebSocket credential rewriting, request body credential rewriting, GraphQL fields, and SSRF IP allowlists. Because profile credentials are not mapped to individual endpoints, OpenShell conservatively treats every endpoint in a profile that declares credentials as credentialed. Such endpoints require L7 inspection and cannot use tls: skip unless the profile explicitly sets allow_uninspected_credentials: true.
binaries contains the executable paths allowed to reach the profile endpoints when the profile contributes policy to a sandbox.
inference_capable marks profiles that are intended to participate in inference workflows. It does not currently mount or configure inference.local.
Refresh Metadata
Credential refresh metadata belongs to one credential declaration. The profile defines allowed defaults, such as token URL, scopes, refresh lead time, maximum lifetime, and required material keys. The provider instance stores the actual refresh material.
Profile YAML can declare these refresh strategies:
openshell provider refresh configure accepts only gateway-mintable strategies: oauth2-refresh-token, oauth2-client-credentials, google-service-account-jwt, and aws-sts-assume-role. Use openshell provider update for static and external refresh patterns.
Gateway-managed refresh strategies use these material keys:
OpenShell keeps token endpoints profile-owned. Refresh material cannot override token_url or token_uri during refresh configuration.
Additional Outputs
Most refresh strategies mint a single credential. aws_sts_assume_role mints three. A refresh declares the extra credentials it co-mints with additional_outputs, mapping each strategy-defined output id to a sibling credential whose env_vars receive the value:
The refresh attaches to the primary credential (access_key_id). Each referenced sibling must exist, declare exactly one env var, and not carry its own refresh. Because a refresh’s outputs are runtime-resolvable, a profile whose only credentials are STS-minted can be created with --runtime-credentials. The resolved output-to-env-key mapping is pinned when refresh is configured, so later profile edits do not silently redirect where minted values are written.
Dynamic Token Grants
token_grant belongs to one credential declaration. When a sandbox with the provider attached sends HTTP traffic to a matching profile endpoint, the supervisor requests a SPIFFE JWT-SVID from the local Workload API, exchanges it at token_endpoint, caches the returned access token, and injects it before forwarding the request upstream. Use auth_style: bearer to inject Authorization: Bearer <token>, or auth_style: header with header_name to inject the raw access token into a custom header. Token grants do not support query or path placement.
Create provider instances for token-grant-only profiles with --runtime-credentials. This records an empty provider instance and makes the runtime-resolved credential source explicit:
Token grant fields:
Token grants require the sandbox supervisor to have access to a SPIFFE Workload API socket. They apply to HTTP traffic that the proxy can inspect. Endpoints with tls: skip bypass TLS termination and cannot receive dynamic token grant injection for HTTPS traffic. The token service must return a token value that is safe for HTTP header placement; malformed values are rejected before caching or header injection.
Provider Instances
A provider instance stores concrete credentials and config for a profile type. Built-in profile IDs and imported custom profile IDs are accepted by --type.
Create a GitHub provider from the built-in github profile:
Create a provider from local credentials discovered through the provider profile:
When providers_v2_enabled=true, --from-existing uses the provider profile’s
discovery section. If no profile exists for the requested type, the command
fails instead of falling back to the legacy provider registry. When
providers_v2_enabled=false, --from-existing uses the legacy provider
registry and ignores profile discovery metadata.
For example, with Providers v2 enabled, --type openai --from-existing
requires an imported openai profile with a discovery section. Setting
OPENAI_API_KEY alone is not enough for v2 profile discovery.
Create a provider from an imported custom profile:
Create a provider whose credential is stored by a configured gateway credential driver:
The create/update API stores submitted provider credentials and secret refresh
material through the gateway’s active credential storage path and persists only
internal credential handles. Secret refresh material includes OAuth refresh
tokens, client secrets, service-account private keys, and temporary AWS source
secrets. By default, the gateway stores AES-256-GCM encrypted credential
envelopes in the gateway database outside provider and refresh-state records. The Helm chart
creates a retained Kubernetes Secret for the default storage key-encryption key
and injects it into every gateway pod when no external credential driver is enabled.
credential_drivers = [] is invalid. Multi-replica Kubernetes gateways can use
a shared database with the default encrypted store, or choose a shared backend
such as kubernetes-secrets or vault.
Every secret_material_keys entry must name a key supplied in material in the
same configure request. Clients submit secret values, not internal credential
handles.
Provider records that already contain inline database credentials remain readable for upgrade compatibility. New provider create/update requests store credential values through the active credential driver and persist only handles.
Provider profiles whose required credentials are fully runtime-resolvable through token_grant or gateway-managed refresh can be created without --credential.
Inspect the provider:
Update provider credentials:
Set or clear credential expiry metadata:
Use an ISO/RFC3339 timestamp or Unix epoch milliseconds. Use 0 as the timestamp to clear expiry for a credential key.
OpenShell skips expired provider credentials when it builds a sandbox provider environment. Running sandboxes also reject expired retained credential generations during placeholder resolution, so stale placeholders fail closed instead of forwarding unresolved or expired credential material.
The gateway sends a complete host, port, and path binding for every emitted static credential key. It derives bindings from profile endpoints or explicit sandbox policy endpoints for endpointless profiles. It withholds static credential keys from endpointless selected profiles that have no explicit policy binding. Supervisors reject other incomplete binding metadata and clear previously active provider material when a refresh fails validation. Refer to Static Credential Endpoint Binding for matching, denial, lifecycle, and migration behavior.
Configure Credential Refresh
Refresh configuration is stored separately from the current injectable credential value. Non-secret refresh configuration remains in the refresh-state record. Secret material is resolved from the active credential driver only while the gateway mints a new short-lived token. The gateway writes the token back through credential storage and updates credential expiry metadata. If an OAuth issuer rotates its refresh token, the gateway stages the replacement through credential storage before committing the refresh generation and removes the previous handle afterward.
Before OpenShell 0.1.0, refresh-state and credential-driver migrations are not supported. Upgrading from a build that stored refresh material inline requires reconfiguring the refresh grant. To change credential drivers, delete or reconfigure affected providers while the original driver is still available, then select the new driver and create the credentials again. Do not run mixed gateway versions against the same refresh records.
Each explicit refresh configure call also starts a new gateway-owned
authorization epoch. Automatic refresh and refresh rotate preserve that epoch,
so running workloads keep the same opaque credential handle while the short-lived
token changes. Configuring refresh again is a revocation boundary and causes
running workloads that still hold the previous handle to fail closed.
While gateway-managed refresh is configured, provider update --credential
cannot replace or delete its primary credential or any co-minted output. Use
provider refresh rotate to mint a new short-lived value. Re-run
provider refresh configure to start a new authorization, or use
provider refresh delete before returning those credential keys to manual
management. You can still update unrelated credentials, configuration, and
credential expiry metadata.
For a complete Microsoft Graph OAuth2 refresh-token walkthrough, see Refresh Microsoft Graph Credentials with Providers v2.
The profile YAML strategy values use underscores, while the CLI --strategy values use kebab-case:
Create the provider instance first:
This example assumes you imported a custom profile with
id: microsoft-graph-mail. Provider refresh can be configured only for provider
types whose profile declares compatible credential refresh metadata.
Configure OAuth2 client credentials refresh:
Configure OAuth2 refresh-token refresh:
Configure Google service account JWT refresh:
This example assumes you imported a custom profile with id: google-drive.
--secret-material-key takes the name of a --material key, not the secret value. For example, use --material client_secret="$MS_CLIENT_SECRET" with --secret-material-key client_secret. Prefer --secret-material-env so the value does not appear in shell history. The gateway combines caller markings with authoritative profile metadata and strategy-defined secret fields, then stores those values through the active credential driver. Only the --credential-key value, such as MS_GRAPH_ACCESS_TOKEN, becomes injectable. If an OAuth response rotates a refresh token, OpenShell stores the replacement through the credential driver automatically.
Use --credential-expires-at when the current provider credential already has a known expiry timestamp. For refresh-managed keys, the value can be Unix epoch milliseconds or an ISO/RFC3339 timestamp such as 2026-01-01T00:00:00Z or 2026-01-01T01:00:00+01:00. OpenShell stores that value as epoch milliseconds in both refresh state and provider credential metadata. Later gateway-managed refreshes replace it with the minted token expiry.
Force a refresh immediately:
Check refresh status:
The status table reports operational state without printing token values or refresh material:
When no refresh configuration exists, the CLI distinguishes whole-provider checks from credential-specific checks:
Delete refresh state for one credential:
Deleting refresh state clears the provider credential expiry only when that expiry came from the deleted refresh state. If you later set a different expiry manually with openshell provider update --credential-expires-at, OpenShell preserves the manual value.
Refresh Logs
The gateway emits secret-safe refresh logs during each worker sweep. Use these logs to check which credentials the gateway is watching, when the next refresh is due, and whether a credential is already refreshed.
The sweep line summarizes how many credential refresh records the worker inspected. The watch line shows the provider, credential key, strategy, refresh status, expiry time, next refresh time, and whether refresh is due or manually requested. It does not include access-token values or refresh material.
Refresh updates the provider record. Sandboxes receive the updated credential through the same placeholder environment and proxy rewrite path as other provider credentials.
Launch Sandboxes with Providers
Attach providers when creating a sandbox with repeated --provider flags:
When providers_v2_enabled=true, each attached provider with a matching profile contributes a provider policy layer to the sandbox effective policy. The base policy is the user-authored sandbox policy that you can edit and apply. The effective policy is the composed policy that the sandbox enforces: base policy plus provider policy layers. When the setting is disabled, the sandbox still receives endpoint-bound provider credentials but not provider-derived policy entries.
Updating a custom provider profile affects every provider instance whose type matches that profile ID. Provider instances are not rewritten, and sandbox-authored policies are not modified. Running sandboxes observe the updated provider-derived policy on their next config sync. If a gateway-global policy is active, provider-derived policy layers remain suppressed.
Providers v2 does not infer or auto-attach providers from the sandbox command.
Attach providers explicitly with --provider during sandbox creation, or use
openshell sandbox provider attach after creation.
List providers attached to a sandbox:
The list output includes provider name, provider type, credential key count, and config key count.
Policy Composition
OpenShell stores the base policy and provider attachments separately. When a sandbox asks for its effective policy, the gateway composes the current base policy with provider policy layers just in time.
For example, the built-in GitHub profile contains these endpoints and binaries:
The github.com git-transport endpoint uses explicit rules instead of the read-only preset so HTTPS clone and fetch work out of the box. Git smart HTTP performs a GET on */info/refs followed by a POST to */git-upload-pack; the read-only preset (GET/HEAD/OPTIONS) blocks that POST. The rules permit the read-only methods plus POST */git-upload-pack only, so clone and fetch succeed while push (git-receive-pack) and other API mutations stay denied. Enabling push requires an explicit policy proposal.
If a sandbox attaches a provider named work-github, the effective policy includes a generated provider rule:
Inspect the effective policy:
Pull the round-trippable base policy without provider entries:
Composition follows these rules:
- Provider policy entries use reserved
_provider_*keys derived from provider instance names. - Provider policy entries are derived data. OpenShell does not persist them back into the base policy.
- User-authored policies cannot define
_provider_*network policy keys. The gateway rejects those keys on sandbox create and full policy replacement, and sandbox-originated policy sync strips them before persistence. - Provider and user rules are concatenated. Overlapping endpoints remain separate rules.
- A gateway global policy override suppresses provider-derived policy layers.
Attach and Detach Providers
Attach an existing provider to a running sandbox:
Detach a provider:
Attach and detach are idempotent. Attach validates that the provider exists before mutating the sandbox, and provider deletion fails while the provider is attached to any sandbox.
Runtime Limitations
Provider attach and detach update the persisted sandbox provider list. Running sandboxes poll for provider environment revisions and effective policy changes.
The policy effect applies to future effective policy reads after the sandbox observes the update. The credential environment effect applies only to new process launches after the update is observed, such as later SSH, exec, or SFTP sessions.
Already-running processes keep the placeholder environment they started with. OpenShell does not mutate a live process environment after provider attach, detach, or credential update. The proxy resolves existing placeholders against current credentials and bindings, so rotation, expiry, endpoint changes, and detach take effect without restarting the process. If a long-running process needs a newly attached provider credential placeholder, restart that process or launch a new process after the sandbox has observed the provider update.
Detaching a provider removes its provider policy layer from future effective policy reads, revokes resolution for its existing placeholders, and removes its credential placeholders from future process environments. It does not remove the placeholder strings from already-running process environments.
OpenShell rejects provider updates and refresh configuration when they would make two providers attached to the same sandbox expose the same active credential environment key. It also rejects attached provider sets with ambiguous dynamic token grants at equal host/path specificity. Use provider-specific credential names and make one dynamic grant selector more specific when one sandbox needs multiple providers with overlapping upstream concepts.
Next Steps
- Use Providers for the current provider command reference.
- Use Customize Sandbox Policies to apply user-authored policy rules.
- Use Policy Schema Reference for endpoint and L7 rule field details.