Skip to content

feat(providers): support safe custom provider profile updates #1881

Description

@johntmyers

Problem Statement

Providers v2 supports importing, exporting, linting, and deleting custom provider profiles, but it does not support updating an existing custom provider profile.

This makes custom profile lifecycle management awkward and blocks safe rollout of profile changes. Provider instances reference profiles by provider.type; they do not copy endpoint or binary policy into the provider instance. Effective sandbox policy is composed just-in-time from the sandbox policy plus the attached providers' profiles. That means a provider profile update is the natural control-plane operation for changing provider-derived policy across all provider instances of that type.

Today, users must create a new profile ID, recreate or update provider instances to use the new type, and reattach or recreate sandbox state. That is unnecessary because the runtime model already resolves profiles dynamically.

Related umbrella issue: #896.

Proposed Design

Add first-class update support for custom provider profiles.

User-facing CLI:

openshell provider profile update -f custom-profile.yaml

Optional batch form:

openshell provider profile update --from ./provider-profiles

Server/API behavior:

  • Add an UpdateProviderProfile RPC.
  • Accept one profile per update request, or mirror the existing import batch shape if batch updates are preferred.
  • Require the profile ID to already exist as a custom profile.
  • Reject updates to built-in provider profile IDs.
  • Preserve the existing StoredProviderProfile.metadata.id, name, created_at_ms, and labels.
  • Update StoredProviderProfile.profile and increment resource_version.
  • Support optimistic concurrency with expected_resource_version if practical.
  • Reuse existing profile validation before persisting.
  • Reuse and extend existing attached-sandbox diagnostics so an update cannot introduce ambiguous dynamic token grants for sandboxes that already use affected provider types.
  • Do not mutate provider instances. Provider instances continue to store only type, credentials, config, and credential expiry metadata.
  • Do not persist provider-derived network rules into sandbox policies.

Effective policy behavior:

  • Sandboxes using providers of the updated profile type should receive updated provider-derived network rules on the next sandbox config/policy sync.
  • This works because GetSandboxConfig composes effective policy from the current sandbox policy plus profile_provider_policy_layers(...).
  • If providers_v2_enabled=false, profile network policy changes should not affect sandbox effective policy.
  • If a gateway-global policy is active, provider-derived policy layers should remain suppressed as they are today.

Credential/dynamic credential behavior:

  • If the profile update changes dynamic token grants, provider environment revision should change so sandbox-side provider credential state refreshes.
  • Existing compute_provider_env_revision(...) already hashes custom profile payloads; tests should lock this in through the public update path.

Validation and safety:

  • Reject invalid profile IDs using the same normalization rules as import/get/delete.
  • Reject profile ID changes during update.
  • Reject updates that conflict with existing profile IDs or built-in profile IDs.
  • Reject updates that would make active attached-provider sets ambiguous for dynamic token grant resolution.
  • Keep delete behavior unchanged: custom profile delete remains blocked while in use by sandboxes.

Implementation outline:

  • proto/openshell.proto: add UpdateProviderProfileRequest and UpdateProviderProfileResponse or reuse ProviderProfileResponse.
  • crates/openshell-cli/src/main.rs: add openshell provider profile update.
  • crates/openshell-cli/src/run.rs: parse YAML/JSON profile input using existing profile import helpers.
  • crates/openshell-server/src/grpc/provider.rs: add handler that validates, fetches existing custom profile, runs attached-sandbox diagnostics with the candidate profile, then writes with CAS/update semantics instead of WriteCondition::MustCreate.
  • crates/openshell-server/src/grpc/policy.rs: add or extend tests proving updated profile endpoints appear in effective policy without modifying provider instances or persisted sandbox source policy.
  • docs/sandboxes/providers-v2.mdx: document update semantics and rollout behavior.
  • docs/sandboxes/manage-providers.mdx: add CLI examples for updating custom profiles.

Definition of done:

  • openshell provider profile update -f profile.yaml updates an existing custom profile.
  • Updating a built-in profile returns a clear error.
  • Updating a missing custom profile returns a clear not-found error.
  • Updating a profile changes effective policy for sandboxes with attached providers of that type on next config sync.
  • Provider objects are not rewritten when a profile is updated.
  • Persisted sandbox source policy does not gain _provider_* rules.
  • Dynamic token grant ambiguity is detected before persisting an update.
  • Docs explain that profile updates affect all provider instances of that type.

Alternatives Considered

Use openshell provider profile import --replace.

This is compact, but it makes import more dangerous because the existing command is create-only today. A dedicated update command is clearer, easier to gate with stronger validation, and avoids accidental replacement when users expect import to be non-destructive.

Create a new profile ID for every change.

This works today but forces provider instance churn and sandbox attachment churn. It does not match the current runtime model, where providers reference profiles dynamically by type.

Copy profile network policy into provider instances.

This would make profile updates harder because every provider instance would need migration. The current design already avoids this by resolving profiles from provider.type during policy composition.

Agent Investigation

  • Confirmed the CLI has provider profile export, import, lint, and delete, but no update.
  • Confirmed ImportProviderProfiles persists custom profiles with WriteCondition::MustCreate.
  • Confirmed import rejects existing custom profile IDs and rejects overwriting built-in profile IDs.
  • Confirmed provider instances store only metadata, type, credentials, config, and credential_expires_at_ms.
  • Confirmed sandbox provider attachment is SandboxSpec.providers, a repeated list of provider names.
  • Confirmed effective policy composition walks from sandbox provider names to provider records, then from provider.type to the provider profile.
  • Confirmed profile-derived policy layers are composed just-in-time and are not persisted into the sandbox source policy.
  • Confirmed compute_provider_env_revision(...) hashes custom profile payloads, so profile changes can trigger sandbox-side provider refresh behavior.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Activity

  1. added
    area:cliCLI-related work
    area:policyPolicy engine and policy lifecycle work
    area:gatewayGateway server and control-plane work
    on Jun 11, 2026
  2. johntmyers commented on Jun 11, 2026

    @johntmyers
    CollaboratorAuthor

    🏗️ build-plan

    Implementation Plan

    Issue type: feat
    Complexity: Medium
    Confidence: High — clear path

    Summary

    Add safe update support for custom provider profiles through a new provider-profile update RPC and openshell provider profile update -f|--from. The implementation will preserve stored profile identity/metadata, reject built-ins and missing custom profiles, reuse existing validation and attached-sandbox ambiguity checks, and rely on existing just-in-time policy/env revision composition so provider instances and sandbox source policies are not mutated.

    Scope

    • proto/openshell.proto: Add provider profile update RPC and request/response messages modeled after existing import/lint diagnostics.
    • crates/openshell-server/src/grpc/mod.rs: Wire the new RPC with provider-profile write permissions matching import/delete.
    • crates/openshell-server/src/grpc/provider.rs: Implement profile update handling, ID normalization, built-in/missing-profile rejection, metadata preservation, validation, attached-sandbox dynamic-token ambiguity checks, and persistence with resource-version update semantics.
    • crates/openshell-cli/src/main.rs: Add openshell provider profile update with -f/--file and --from inputs.
    • crates/openshell-cli/src/run.rs: Add provider_profile_update using existing profile loading, diagnostics, and output conventions.
    • crates/openshell-server/src/grpc/policy.rs: Add tests proving updated custom profile endpoints and provider env revision flow through config/environment paths without rewriting provider instances or sandbox source policy.
    • docs/sandboxes/providers-v2.mdx: Document update semantics, rollout behavior, built-in immutability, and providers-v2/global-policy interactions.
    • docs/sandboxes/manage-providers.mdx: Add concise examples for updating custom profiles.

    Implementation Steps

    1. Define the proto API and regenerate/update generated bindings through the normal Rust build path.
    2. Add server trait wiring in grpc/mod.rs.
    3. Refactor provider-profile validation helpers so import and update can share parsing, validation, dynamic-token ambiguity checks, and diagnostics without reusing import’s “must not already exist” conflict rule.
    4. Implement update persistence: fetch the existing custom profile, verify the candidate profile ID normalizes to the same ID, copy existing metadata, replace only profile, and write with update/CAS semantics.
    5. Add the CLI subcommand and runner using existing profile file loading, diagnostics, and output conventions.
    6. Add server tests for success, built-in rejection, missing profile rejection, ID-change rejection, metadata preservation, and dynamic token grant ambiguity.
    7. Add policy/env tests for effective policy and provider_env_revision changes after update.
    8. Update docs.

    Test Plan

    • Unit tests: Provider update handler tests in provider.rs; CLI parse tests in main.rs; policy/env revision tests in policy.rs.
    • Integration tests: Handler-level import → update → get/list flows using test store/state; GetSandboxConfig behavior with providers v2 enabled/disabled and with global policy active.
    • E2E tests: Not expected. This is control-plane CRUD plus existing config sync behavior; existing unit/integration coverage should be sufficient unless an existing provider-profile CLI/gateway E2E suite is found and already covers this path.

    Risks & Open Questions

    • Batch update semantics: prefer all-or-none server-side batch diagnostics if supporting --from; sequential CLI updates could partially roll out a directory.
    • Public CAS usability: current profile response exposes ProviderProfile, not stored metadata/resource version. If CAS is included, decide whether the CLI can fetch a version, expose an optional flag, or leave expected_resource_version=0.
    • Auth scope should match import/delete profile behavior rather than provider read.
    • Validation must avoid reusing import’s “already exists” conflict diagnostic for the profile being updated.
    • Custom profile YAML is untrusted input that can expand sandbox network egress; strict validation and pre-persistence ambiguity rejection are required.

    Documentation Impact

    • Update docs/sandboxes/providers-v2.mdx and docs/sandboxes/manage-providers.mdx.
    • No gateway TOML, driver config, config default, or Helm-rendered gateway.toml changes are expected, so docs/reference/gateway-config.mdx is not expected to change.

    LSM Compatibility

    • No LSM-specific impact expected. The change is gateway/CLI/control-plane profile persistence and policy composition; it does not touch process identity, /proc, binary execution, Landlock, seccomp, SELinux, or AppArmor behavior.

    Revision 1 — initial plan

  3. johntmyers commented on Jun 15, 2026

    @johntmyers
    CollaboratorAuthor

    🏗️ build-from-issue-agent

    Implementation Complete

    PR: #1914

    What was built

    Added custom provider profile update support through a new RPC and openshell provider profile update -f|--from. Updates validate profile batches before writing, preserve stored custom profile metadata, reject built-ins and missing profiles, and keep provider-derived policy resolved dynamically from the current profile.

    Tests

    • Unit: provider update handler tests for success, metadata preservation, built-in/missing rejection, and dynamic token ambiguity rejection
    • Integration: CLI lifecycle and sandbox effective-policy/provider-env revision behavior
    • E2E: skipped; no e2e/ files changed

    Docs updated

    • docs/sandboxes/providers-v2.mdx
    • docs/sandboxes/manage-providers.mdx

    The issue will auto-close when the PR is merged.

  4. added
    state:pr-openedPR has been opened for this issue
    gator:validatedGator validated this issue as ready for work
    and removed on Jun 15, 2026
  5. johntmyers commented on Jun 15, 2026

    @johntmyers
    CollaboratorAuthor

    gator-agent

    Validated

    This issue is valid for OpenShell because it is approved provider-v2 control-plane work with a clear CLI/API path, security boundary analysis for custom profile updates, and an active implementation PR (#1914).

    Recommended next step: continue review on PR #1914.

  6. moved this from Todo to In progress in OpenShell Roadmapon Jun 16, 2026
  7. self-assigned this
    on Jun 16, 2026
  8. moved this from In progress to Done in OpenShell Roadmapon Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:cliCLI-related workarea:gatewayGateway server and control-plane workarea:policyPolicy engine and policy lifecycle workgator:validatedGator validated this issue as ready for workstate:agent-readyApproved for agent implementationstate:pr-openedPR has been opened for this issue

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions