Skip to content

feat(providers): composable provider profiles with explicit credential↔endpoint relationship #2330

Open

Description

@russellb

Problem Statement

Today, accessing a new AWS service means authoring a full service-specific provider profile that bundles both credentials (STS refresh material) and endpoints. providers/aws-s3.yaml is the concrete example: it carries the STS AssumeRole refresh config and the S3 endpoint allowlist together.

This couples two concerns that naturally vary independently:

  • Credentials are shared across an entire suite of AWS services (one AssumeRole session works for S3, Lambda, etc.).
  • Endpoints are per-service.

As a result, a user who wants to reach several AWS services must re-declare the same credential/refresh material in each service profile, and every new service needs a new profile that duplicates the credential half.

Proposed Design

Split the two concerns into separate, composable provider profiles that the user attaches together to a sandbox:

  • aws.yaml — credentials only (the STS refresh material it already carries).
  • aws-<service>.yaml (e.g. aws-s3, aws-lambda) — endpoints only, no credentials.

The user configures the aws credential provider instance once and reuses it with multiple service providers:

  • provider_1: brings AWS creds
  • provider_2: access S3
  • provider_3: access Lambda

The core problem to solve

The credentials↔endpoints relationship becomes implicit. A sandbox attached to aws-s3 but not aws would pass configuration but fail at runtime during SigV4 signing (no credentials available to sign with). That late, opaque failure is the reason this wasn't done in the originating PR.

The design work is making that relationship explicit and validated — so an endpoint-only service provider declares its dependency on a credential-bearing provider, and attach-time validation rejects an incomplete combination instead of letting it fail during signing.

This overlaps with the additional_outputs model introduced in #1782 (a refresh declaring the extra credentials it co-mints), which should inform how the dependency is represented and validated.

Alternatives Considered

  • Status quo (bundled profiles): each service profile duplicates credential/refresh material. Simple, but no reuse and N copies of the same STS config to keep in sync.
  • Implicit composition (attach both, no validation): achievable today by attaching aws + aws-<service> but the dependency is invisible and only surfaces as a runtime signing failure — poor UX and hard to debug. Making the relationship explicit and validated is the improvement this issue tracks.

Agent Investigation

Originating discussion: review thread on providers/aws-s3.yaml:61 in #1782.

Relevant context from that PR:

  • providers/aws-s3.yaml currently bundles STS refresh material + S3 endpoints.
  • providers/aws.yaml is a generic base profile with STS refresh material but no endpoints (users attach their own policy for endpoints today).
  • The additional_outputs refresh model (a refresh declaring co-minted sibling credentials, with the output→env-key mapping pinned at configure time) was added in feat(providers): AWS STS AssumeRole refresh strategy and aws-s3 profile #1782 and is the closest existing mechanism for representing multi-credential relationships explicitly.

Related

Activity

  1. maruiz93 commented on Sep 21, 2026

    @maruiz93

    Two problems with provider composition

    Problem 1 — Identical credential schemas across service profiles prevent composition.

    The AWS service profiles (aws-s3, aws-bedrock) each declare the same credential schema as the base aws profile — AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN. Creating two providers of these types and attaching them to the same sandbox fails the env var uniqueness check, even when the credential values are identical (same STS session).

    Example: a sandbox that needs S3 (artifact storage) + Bedrock (inference) can't have both providers attached.

    Problem 2 — Different credential values on the same env var key across providers with non-overlapping endpoints.

    Two providers of different types that both use the same env var key (e.g., OPENAI_API_KEY) but with different values targeting non-overlapping endpoints are rejected at configuration time, even though the endpoints never overlap.

    Example: two OpenAI-compatible inference endpoints (production and staging) that both need OPENAI_API_KEY with different values.

    Root cause: the gateway validation is the only blocker — the supervisor already handles this.

    validate_provider_environment_key_ownership() unconditionally rejects any credential env var key collision between different providers. It doesn't consider whether the providers' endpoints overlap or whether the credentials come from the same schema — it just sees two providers claiming the same key and returns failed_precondition.

    Meanwhile, the supervisor already scopes credential resolution per-endpoint:

    1. The sandbox never sees real credentials — only placeholders (e.g., openshell:resolve:env:v1_AWS_ACCESS_KEY_ID)
    2. resolver_for_endpoint() builds an allowed set from static_credential_bindings matching the request's host/port/path
    3. scoped_to_env_keys() filters the resolver — credentials bound to other endpoints go into denied_env_keys

    For non-overlapping endpoints, the runtime already resolves the right credential per-endpoint. The gateway validation prevents the configuration from ever reaching the supervisor.

    One case that must stay rejected: same key + overlapping endpoints with different values. The current env-building code would produce inconsistent state — env.entry(key).or_insert(value) is first-writer-wins for the secret value, while static_credential_bindings.insert(key, ...) is last-writer-wins for the binding metadata.

    Minimal path forward: relax validate_provider_environment_key_ownership to allow key collisions when:

    • the credential values come from the same source (same profile credential schema), or
    • the providers' endpoints don't overlap

    and keep rejecting collisions when endpoints overlap with different values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions