Skip to content

Add identity-bound CAS and secret-free attestation for provider mutations #2722

Description

@cjagwani

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.

  1. Extend provider update with explicit expected identity fields, for example:
    • expected_provider_id
    • expected_resource_version
  2. Expose corresponding CLI flags, such as:
    • openshell provider update NAME --expected-id ID --expected-resource-version N ...
  3. Validate both fields against the current record before preparing or storing any submitted credential material.
  4. Preserve the existing unconditional behavior only when neither expectation is supplied; reject partial expectation pairs.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:needs-infoAssessment needs specific evidence or reproduction details

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions