Repository navigation
feat: add credential drivers for provider secret storage #1931
Description
Activity
- added a parent issue
on Jun 16, 2026 - changed the title
[-]Credential drivers[/-][+]feat: add credential drivers for provider secret resolution[/+]on Jun 17, 2026 - changed the title
[-]feat: add credential drivers for provider secret resolution[/-][+]feat: add credential drivers for provider secret storage[/+]on Jun 22, 2026 Context: we ran the vault credential driver hands-on against OpenBao 2.6.2 outside Kubernetes (gateway as a host process,
auth_method = "token_file", v0.0.113), and it works as shipped. What follows is about the "Alternatives Considered" line in this issue's body — "Using provider-authored references to existing backend secrets was rejected after design review" — which we think is worth reopening in a narrower form than #1939 proposed.The recorded objection is that references "expand provider configuration into backend addressing and policy". Read literally, that is an objection to where the addressing lives, not to reading from a backend as such — and #1939's driver service was already read-only by construction (four RPCs, no
StoreCredential/DeleteCredential). So the narrower question is whether addressing can be kept out of provider config entirely. We think it can.User-facing workflow (illustrative; internals deliberately left open):
-
The gateway owner — not the provider author — declares named, allowlisted credential sources in gateway config:
[credential_sources.team-vault] driver = "vault" mount = "kv" prefix = "openshell-reads/" # reads confined to this prefix
-
A provider references a logical name and key only:
source = "team-vault",key = "clickhouse-bot". Mounts, paths, namespaces and versions are not expressible in provider config. -
Resolution is read-only: references validated at provider create/update, read at resolve time, never written back.
Why we think this merits a second look:
- Read-only consumption held across every system in a ~35-system survey we ran of neighbouring integrations (ESO, Secrets Store CSI, Vault Agent, Ansible/Terraform integrations, ECS/Cloud Run/Azure secret references, systemd
LoadCredential, CI vault integrations, Secretless Broker). KEP-2907 states it as a design non-goal, verbatim: "Consume only: The proposed CRD and implementation does not provide a way to add/write/edit secrets in the external stores." None of the systems we surveyed derives destination paths and owns storage inside an operator's vault as its way of providing credentials. - The addressing split your objection asks for is the strict, well-precedented end of a genuine spectrum — we're not claiming it's the norm. Most systems we looked at do let consumers write raw backend paths, and we are deliberately not proposing that. ESO's
SecretStore/ExternalSecretsplit exists, per its docs, to "separate concerns of authentication/access and the actual Secret"; Jenkins consumers reference an opaque credential ID; and CyberArk's Secretless Broker — the same product category as OpenShell's injection path — has consumers sayfrom: vault, get: "path#field"against a provider interface with no write method at all. If even a logical key reads as too much addressing, HashiCorp Boundary's shape (an opaque, operator-defined credential-library ID) is a stricter variant we'd take happily. - The cross-workspace overwrite concern doesn't transfer: managed-path derivation guards the write path, and a read-only reference has no write path. That finding also postdates the design review by several weeks, so it doesn't appear to have been the rejection's basis either.
- Users are landing on worse patterns in the meantime. feat(providers): support Kubernetes Secret credential sources for provider credentials #1882 asks for exactly this shape (provider credentials from a named Secret, ESO-synced from Vault/OpenBao); the copy-in workaround it settles for means two sources of truth and sync lag, a weaker custody story than a prefix-confined read. And
token_grant.token_endpointalready concedes that operator-owned external credentials are real — this extends the same concession to secrets that aren't OAuth-shaped. - Rotation improves. Today a rotation in the operator's vault reaches new sandboxes only if it is pushed back through OpenShell — the resolve-once shape plus an extra step. Read-side resolution, with the poll/TTL options these systems usually offer, makes the backend genuinely the single source of truth.
Alternatives we considered, per the contribution guide: an out-of-process driver on the documented socket (viable, but the proto self-describes as an internal contract with no stated compatibility policy);
token_grant(OAuth-shaped flows only); ESO copy-in (works today, at the cost of two sources of truth).Happy to develop this into a full RFC if maintainers think the direction is worth pursuing.
Drafted by an agent from our hands-on evaluation and the ~35-system prior-art survey behind it; reviewed and endorsed by a human before posting.
-
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Problem Statement
OpenShell Providers v2 submit provider credentials, such as API keys, through the
normal gateway API. Historically, provider records could contain inline
plaintext credential values in gateway persistence. That is acceptable only as an
upgrade-compatibility read path, not as the long-term storage model for new
provider writes.
Credential storage should be gateway-owned. Users submit provider credentials
through the normal Providers v2 create/update flow. The gateway owner decides
whether those submitted secrets are stored in OpenShell's default encrypted
database credential store or in an external backend such as Kubernetes Secrets or
OpenBao. Provider configuration must not point at arbitrary pre-existing backend
secrets.
The first concrete use cases are:
credential objects.
Kubernetes Secrets.
Keychain.
This issue only targets Providers v2. Providers v1 should not be extended.
Proposed Design
Add a Credential Driver interface, modeled after the compute driver boundary,
that lets the gateway store, resolve, and delete provider credentials through a
gateway-owned storage backend.
Credential storage should support:
driver is configured.
compute-driver model.
provider records created before credential storage handles.
The public provider API remains
provider.credentials/openshell provider create --credential.provider.credential_handlesis internal gateway state andmust be rejected when supplied by clients.
Example provider create flow:
When no external credential driver is enabled, the gateway stores the submitted
credential in the default encrypted database credential store, clears inline
credential values before persisting the provider record, and stores only opaque
handles in the provider record. Existing provider records that already contain
inline plaintext credentials remain readable for upgrade compatibility.
Example gateway configuration for the default encrypted database credential
store:
Or, for multi-replica deployments where all gateway replicas must share the same
key-encryption key:
Example gateway configuration for Kubernetes Secrets:
Example gateway configuration for OpenBao:
External drivers may use the advanced UDS transport:
Built-in external drivers default to in-tree transport and do not require a
transportfield.Credential driver contract:
GetCapabilities: report driver identity and feature support.StoreCredential: store or overwrite one provider credential and return anopaque handle.
ResolveCredentials: resolve opaque handles into string secret values whenthe gateway materializes a sandbox provider environment.
DeleteCredential: delete one stored provider credential handle.ListCredentials: optional driver-owned inspection surface.Backend layout is OpenShell-managed:
provider credential outside the provider record. Each object contains an
encrypted value envelope, and the provider record stores only the handle.
using deterministic names derived from provider name and credential key.
managed prefix such as
openshell/provider-credentials/.namespaces in provider configuration.
Provider delete and credential removal should delete backend credential objects
before completing the provider database write/delete. Cleanup failures fail
closed.
Acceptance Criteria
storage path.
stored in the default encrypted database credential store.
readable for upgrade compatibility.
driver.
credential_drivers = []; omitting thefield selects default encrypted database credential storage.
credential_handlesare rejected by provider create/update.provider credential writes.
handles.
credentials while moving newly submitted credential values into credential
storage.
on backend cleanup errors.
values with gateway-owned key material and can reuse that key material across
gateway restarts.
default encrypted database store when no external credential driver is enabled.
using gateway Kubernetes RBAC.
deployed OpenBao instance.
storage backends one at a time using the local Skaffold/Helm development
environment.
Alternatives Considered
Keeping only inline plaintext credentials was rejected because it forces provider
records to own secret material directly and does not provide defense in depth for
default gateway deployments. Inline credentials remain supported only as a
legacy read path for provider records created before credential handles.
Using provider-authored references to existing backend secrets was rejected after
design review. It expands provider configuration into backend addressing and
policy, while the intended ownership model is that OpenShell internal driver
logic manages backend destinations.
Using environment variables as the primary credential driver was rejected because
environment variables are an input/discovery mechanism, not a durable backend
storage contract. They may remain useful for local provider discovery.
Resolving credentials inside sandbox runtimes was rejected because backend
access should remain controlled by the gateway owner and should not expand
sandbox authority.
Agent Investigation
UDS-connected driver implementations.
gateway owner controls backend access and policy.
unchanged.
credentials while routing new writes through credential storage handles.
matches the current compute-driver model.
posture without requiring Kubernetes Secrets or OpenBao.
the primary production deployment paths.
environment so behavior is tested against a real Kubernetes deployment.