Problem Statement
Provider automation cannot currently bind a credential/config mutation atomically to the provider identity it inspected.
In OpenShell v0.0.104 (dd2b4e3bc0688bdd59f90030f7c1d52511d6e354):
openshell provider list --output json returns a useful secret-free identity view (id, name, workspace, type, credential/config key names, and resource_version).
- The gateway supports client-driven optimistic concurrency when
Provider.metadata.resource_version is non-zero.
- The
provider update CLI always sends an empty provider ID and resource_version: 0.
- The gateway therefore resolves the provider by name at mutation time and CASes against the version it just read, rather than an identity/version previously observed by the caller.
If a provider is updated or deleted/recreated between an automation client's read and update, the new identity can receive the submitted credential/config before a client-side post-read detects drift. A post-mutation receipt cannot undo that credential delivery.
This blocks fail-closed consumers that need multiple endpoint-bound providers, including NemoClaw's judge/weak/strong model targets for native Hermes → NeMo Relay → Switchyard routing (NVIDIA/NemoClaw#8887).
The current secret-free inventory also cannot attest endpoint configuration or the resolved provider-profile revision: it intentionally exposes config key names but not a non-secret config digest or profile provenance. Consequently, callers cannot distinguish two same-shaped providers targeting different endpoints.
Proposed Design
Add an identity-bound conditional mutation contract to the provider API and CLI.
- Extend provider update with explicit expected identity fields, for example:
expected_provider_id
expected_resource_version
- Expose corresponding CLI flags, such as:
openshell provider update NAME --expected-id ID --expected-resource-version N ...
- Validate both fields against the current record before preparing or storing any submitted credential material.
- Preserve the existing unconditional behavior only when neither expectation is supplied; reject partial expectation pairs.
- Return a machine-readable, secret-free mutation receipt from create/update and a matching read surface containing:
- provider ID, name, workspace, type, and resource version;
- credential/config key names, never values;
- resolved profile ID, scope/workspace, and profile resource version;
- a versioned digest of canonical non-secret provider config (or another stable endpoint/config attestation).
The receipt should be generated by the gateway from the committed record, not reconstructed by the CLI.
Security Requirements
- A stale expected provider ID or resource version fails before any new credential reaches persistent or external credential storage.
- A concurrent delete/recreate under the same name cannot satisfy an update authorized for the old provider, even if its numeric resource version happens to match.
- Errors and structured output never contain credential values or credential-store handles.
- The config attestation format is versioned and deterministic; unknown/unsupported versions fail closed for consumers that require it.
- Profile provenance is resolved at the same gateway snapshot used for the mutation receipt.
Acceptance
Alternatives Considered
Client-side read → update → read: detects some drift after the fact but cannot prevent credential delivery to the wrong identity.
Resource version without provider ID: does not fully protect delete/recreate under the same name when versions coincide.
Expose raw provider config: unnecessary for the transaction and can expand disclosure. A stable versioned digest plus resolved profile provenance is sufficient for exact automation checks.
Checklist
Problem Statement
Provider automation cannot currently bind a credential/config mutation atomically to the provider identity it inspected.
In OpenShell v0.0.104 (
dd2b4e3bc0688bdd59f90030f7c1d52511d6e354):openshell provider list --output jsonreturns a useful secret-free identity view (id,name,workspace,type, credential/config key names, andresource_version).Provider.metadata.resource_versionis non-zero.provider updateCLI always sends an empty provider ID andresource_version: 0.If a provider is updated or deleted/recreated between an automation client's read and update, the new identity can receive the submitted credential/config before a client-side post-read detects drift. A post-mutation receipt cannot undo that credential delivery.
This blocks fail-closed consumers that need multiple endpoint-bound providers, including NemoClaw's judge/weak/strong model targets for native Hermes → NeMo Relay → Switchyard routing (NVIDIA/NemoClaw#8887).
The current secret-free inventory also cannot attest endpoint configuration or the resolved provider-profile revision: it intentionally exposes config key names but not a non-secret config digest or profile provenance. Consequently, callers cannot distinguish two same-shaped providers targeting different endpoints.
Proposed Design
Add an identity-bound conditional mutation contract to the provider API and CLI.
expected_provider_idexpected_resource_versionopenshell provider update NAME --expected-id ID --expected-resource-version N ...The receipt should be generated by the gateway from the committed record, not reconstructed by the CLI.
Security Requirements
Acceptance
Alternatives Considered
Client-side read → update → read: detects some drift after the fact but cannot prevent credential delivery to the wrong identity.
Resource version without provider ID: does not fully protect delete/recreate under the same name when versions coincide.
Expose raw provider config: unnecessary for the transaction and can expand disclosure. A stable versioned digest plus resolved profile provenance is sufficient for exact automation checks.
Checklist