Add identity-bound CAS and secret-free attestation for provider mutations
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
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).- The gateway supports client-driven optimistic concurrency when
Provider.metadata.resource_versionis non-zero. - The
provider updateCLI always sends an empty provider ID andresource_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_idexpected_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
- CLI and API can conditionally update a provider by exact ID plus resource version.
- Concurrent update and delete/recreate tests prove stale mutations do not persist or deliver submitted credentials.
- Successful create/update returns a secret-free receipt bound to the committed identity/version.
- A read API exposes the same attestation fields without credential values.
- Same-shaped providers with different endpoint config produce different config attestations.
- Resolved provider-profile revision/scope drift is observable.
- Backward-compatible unconditional updates remain explicitly documented, while security-sensitive automation can require the conditional path.
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
- Existing issues were searched for provider CAS, expected resource version, concurrent provider update, and provider attestation.
- The current CLI, proto, gateway update path, and concurrency tests were inspected.
- This is a design/API request, not a request to expose credentials.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the provider update CLI, provider proto/API, gateway update path, and existing concurrency tests mentioned in the issue. Trace how identity, resource version, credentials, and profile data flow through create/update and read operations. Done means the acceptance checklist is covered, including stale identity tests, secret-free receipts, deterministic config attestations, and profile provenance.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100