Skip to content

feat(provider): selective credential passthrough#984

Draft
laitingsheng wants to merge 1 commit intoNVIDIA:mainfrom
laitingsheng:fix/894-customisable-credential-placeholder
Draft

feat(provider): selective credential passthrough#984
laitingsheng wants to merge 1 commit intoNVIDIA:mainfrom
laitingsheng:fix/894-customisable-credential-placeholder

Conversation

@laitingsheng
Copy link
Copy Markdown
Contributor

@laitingsheng laitingsheng commented Apr 27, 2026

Summary

Adds an opt-in passthrough mechanism for provider credentials. The supervisor injects the real value into the agent's environment instead of the canonical openshell:resolve:env:<KEY> placeholder, and the L7 proxy performs no substitution for that key. Required for SDKs that validate credential format in-process (Slack xoxb-*/xapp-*), credentials consumed by in-process crypto (HMAC signing secrets, signature verification), and transports the proxy cannot rewrite (WebSocket payloads after HTTP 101 upgrade).

Related Issue

Fixes #894.

Related: #872, #913, #988.

Groundwork for NVIDIA/NemoClaw#1569, NVIDIA/NemoClaw#2024, NVIDIA/NemoClaw#2031, and NVIDIA/NemoClaw#2085.

Changes

Adds one new optional field to Provider in proto/datamodel.proto:

  • passthrough_credentials: repeated string listing credential keys whose real value is injected directly into the agent process. Drops the "agent never sees the real secret" invariant for the listed keys; treated as a per-credential security trade-off.

GetSandboxProviderEnvironmentResponse gains passthrough_keys so the supervisor knows which entries in environment are real values rather than canonical-placeholder targets.

Custom-format placeholders (the second mode in earlier drafts of this PR) are deliberately deferred. Passthrough alone covers all three failure classes from #894 (in-process format check, opaque transport, in-process crypto). A custom-placeholder field is purely additive when a future Class A use case justifies it.

Server changes (crates/openshell-server):

  • grpc/provider.rs: round-trip on Create/Update/Get/List. resolve_provider_environment returns a ProviderEnvironment struct containing the merged env map and the union of passthrough keys; on duplicate keys across providers, the passthrough flag follows the winning value.
  • grpc/validation.rs: validate each entry as a valid env var name, present in credentials, no duplicates, under the entry-count cap.
  • grpc/policy.rs: plumb passthrough_keys into GetSandboxProviderEnvironmentResponse.
  • Update merge has two modes:
    • empty incoming = preserve existing list, but auto-prune entries whose credential was deleted in the same update — without this, marking a credential passthrough and then deleting that credential locks the provider in an invalid state;
    • non-empty incoming = replace verbatim; explicit orphans still rejected as a caller bug.

Sandbox changes (crates/openshell-sandbox):

  • grpc_client.rs: fetch_provider_environment now returns a ProviderEnvironment carrying both the env map and the passthrough key list.
  • secrets.rs: new SecretResolver::from_provider_env_with_passthrough — for passthrough keys puts the real value in child_env and skips resolver registration; non-passthrough keys keep the existing canonical-placeholder behaviour. Resolver is None when every key is passthrough.
  • lib.rs: thread the passthrough list through the supervisor launch path; OCSF log includes both env_count and passthrough_count.

CLI changes (crates/openshell-cli):

  • provider create/provider update: --passthrough KEY (repeatable; key must also appear in --credential on create).
  • provider get: annotates passthrough keys (KEY (passthrough)).
  • provider list: adds a PASSTHROUGH count column.

Architecture docs (architecture/sandbox-providers.md): new "Selective Passthrough" section covering motivation, configuration, resolution-and-merge semantics, supervisor injection, the auto-prune-on-credential-deletion behaviour, and the security trade-off.

User-facing docs (docs/sandboxes/manage-providers.mdx): how-to with example and <Warning> on the security trade-off.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented Apr 27, 2026

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@laitingsheng laitingsheng force-pushed the fix/894-customisable-credential-placeholder branch from 260dc2b to e424f38 Compare May 1, 2026 16:25
@laitingsheng laitingsheng changed the title feat(provider): per-credential custom placeholders and passthrough opt-in feat(provider): selective credential passthrough May 1, 2026
@laitingsheng laitingsheng force-pushed the fix/894-customisable-credential-placeholder branch 2 times, most recently from 2f814f3 to 5395b92 Compare May 1, 2026 16:58
Add an opt-in passthrough mechanism for provider credentials that bypass the
canonical openshell:resolve:env:* placeholder and L7 proxy substitution.

Provider gains a repeated string passthrough_credentials field listing
credential keys whose real value should be injected directly into the agent
process environment. Required for SDKs that validate credential format
in-process (Slack xoxb-*/xapp-*), credentials consumed by in-process crypto
(HMAC signing secrets, signature verification), and transports the proxy
cannot rewrite (WebSocket payloads after HTTP 101 upgrade). Passthrough
drops the "agent never sees the real secret" invariant for the listed
keys; documented as a per-credential security trade-off.

Server: validation rejects keys not in credentials, invalid env var names,
duplicates, and over-cap. resolve_provider_environment returns a
ProviderEnvironment struct with the merged env map and the union of
passthrough keys; passthrough flag follows the winning value on duplicate
keys across providers. Update merge auto-prunes orphaned passthrough
entries when their credential is deleted in the same call (proto3 cannot
distinguish "field unset" from "set to empty list"), preserving the
"recover via update" path. Explicit non-empty incoming list still rejects
orphans as a caller bug.

Sandbox: SecretResolver::from_provider_env_with_passthrough handles the
new mode — passthrough keys go to child env as real values, resolver does
not learn them; non-passthrough keys still get the canonical placeholder.

CLI: provider create/update accept --passthrough KEY (repeatable);
provider get annotates passthrough keys, provider list adds a PASSTHROUGH
column.

Refs NVIDIA#894 (selective passthrough lands; custom-format placeholders deferred
to a follow-up).

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@laitingsheng laitingsheng force-pushed the fix/894-customisable-credential-placeholder branch from 5395b92 to 8446a90 Compare May 1, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Credential placeholder model breaks SDKs that validate token format before making network calls

1 participant