Skip to content

api: /api/model, /api/config and /api/provider/{id} return resolved provider credentials #53465

Open

Description

@ned9

Description

/api/model, /api/config and /api/provider/{providerID} return provider settings with environment-substitution credentials resolved to their literal secrets: a provider configured as "apiKey": "{env:ACME_API_KEY}" — kept out of every config file on disk — comes back from these endpoints as plaintext.

The server is what performs the inference requests, so catalog/config clients don't need the literal key; returning it verbatim breaks least exposure. The impact is amplified under agentic use: agents are now the primary client class for this API, and when one prints an endpoint response into a transcript, that transcript is persisted on disk and re-sent to the model provider as context on every subsequent turn — so a locally-bound secret ends up both in on-disk plaintext and at the model provider.

Expected: catalog/config endpoints redact credential-bearing fields or return what was configured (the {env:...} template), resolving secrets server-side only at request time.

Plugins

None

OpenCode version

2.0.23

Steps to reproduce

  1. Configure a custom provider with an env-substitution credential:

    {
      "$schema": "https://opencode.ai/config.json",
      "provider": {
        "acme": {
          "npm": "@ai-sdk/openai-compatible",
          "options": {
            "apiKey": "{env:ACME_API_KEY}",
            "baseURL": "https://example.com/v1"
          },
          "models": {
            "m1": { "limit": { "context": 100000, "output": 8192 } }
          }
        }
      }
    }
  2. Start opencode with ACME_API_KEY in the environment.

  3. From any shell:

    opencode api get /api/model | jq '.data[] | select(.providerID == "acme") | .settings.apiKey'
    opencode api get /api/config | jq '.. | .apiKey? // empty'
    opencode api get /api/provider/acme | jq '.settings.apiKey'
  4. All three print the literal secret, while the config file still holds only the {env:...} template. Verified on 2.0.23: template is 24 chars, the endpoints return a 43-char literal (lengths compared; the value was never printed or stored in this issue).

Operating System

macOS (Darwin 25.6.0, arm64)

Terminal

Ghostty / tmux

Activity

  1. github-actions commented on Oct 6, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    • #22157: reports /config returning resolved provider API keys and other environment secrets to browser clients.
    • #50915: reports resolved provider API keys exposed by opencode debug config, a related credential-exposure surface.
  2. ned9 commented on Oct 6, 2026

    @ned9
    Author

    This issue might be a duplicate of existing issues. Please check:

    • #22157: reports /config returning resolved provider API keys and other environment secrets to browser clients.
    • #50915: reports resolved provider API keys exposed by opencode debug config, a related credential-exposure surface.

    I don't believe this issue is a duplicate, but they're related and worth cross-linking:

    • #22157: same problem class, different endpoint — serve-mode/web-UI GET /config, and no fix landed. This issue covers the v2 HTTP API surface, including the v2 GET /api/config, GET /api/model, and GET /api/provider/{providerID}.
    • #50915: fixed, but scoped to the opencode debug config CLI command only — the merged fix states "Redact only the debug output; the resolved configuration must remain usable by providers." The HTTP API endpoints here are a different surface, still exposed; verified on 2.0.23, and (the identical model.list handler (catalog.model.available() passthrough) ships unchanged in current stable v1.18.34).

    Comparing them all:

    Surface What's exposed Status
    GET /api/model Per-model settings.apiKey — resolved literal Open — only reported here
    GET /api/config Entire resolved config document: provider options.apiKey, plus MCP server environment values and MCP OAuth secrets Open — configSvc.get() returned raw on dev; fix attempt #33723 closed unmerged
    GET /api/provider/{providerID} Resolved provider entry incl. settings.apiKey Open — only report is here
    GET /config (Serve-mode/web-UI ) Resolved provider keys and MCP environment secrets to authenticated web clients Stale-closed (#22157), never fixed; fix attempt #33723 closed unmerged
    opencode debug config (CLI) Credentials in plaintext Fixed — #50956 / #50957, shipped in v1.18.33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions