Skip to main content

Secret Sources

Secrets are configured under secrets and resolved at execution time.

Supported source types

TypeExampleRuntime behavior
env{"type":"env","value":"HELPDESK_TOKEN"}Reads os.Getenv(value)
file{"type":"file","value":"/run/secrets/helpdesk-token"}Reads the entire file contents
osKeychain{"type":"osKeychain","value":"helpdesk/token"}Uses the platform keychain helper
exec{"type":"exec","command":["sh","-lc","printf token"]}Runs a command and uses stdout
oauth2see belowAcquires, caches, and reuses OAuth access tokens

env

{
"secrets": {
"bearerAuth": {
"type": "env",
"value": "HELPDESK_TOKEN"
}
}
}

Nuance: if the env var is unset, the runtime gets an empty string. That usually means auth is skipped or malformed, not that config loading fails.

file

{
"secrets": {
"bearerAuth": {
"type": "file",
"value": "/run/secrets/helpdesk-token"
}
}
}

Nuance: the runtime reads the file verbatim. Trailing newlines are preserved.

osKeychain

{
"secrets": {
"bearerAuth": {
"type": "osKeychain",
"value": "helpdesk/token"
}
}
}

Supported reference formats:

  • service/account
  • service:account

Current helpers:

  • macOS: security find-generic-password -s <service> -a <account> -w
  • Linux: secret-tool lookup service <service> account <account>

The default keychain resolver trims surrounding whitespace from command output.

exec

exec secrets are disabled unless:

{
"policy": {
"allowExecSecrets": true
}
}

Example:

{
"policy": {
"allowExecSecrets": true
},
"secrets": {
"bearerAuth": {
"type": "exec",
"command": ["sh", "-lc", "printf token-from-exec"]
}
}
}

Important nuances:

  • if command is omitted and value is set, value is treated as the executable path, not as a shell command line
  • stdout is used as-is; trailing newlines are not trimmed
  • there is no stdin wiring or timeout in the current implementation
  • command failures propagate as secret-resolution errors, which usually causes auth to be skipped for that scheme

Operational guidance

  • prefer env or osKeychain for local developer workflows
  • prefer file for container/secret-volume setups
  • use exec only when you need dynamic retrieval and accept the extra risk/latency

oauth2

oauth2 secrets are used for OpenAPI oauth2 and openIdConnect security schemes.

Supported runtime modes:

  • clientCredentials
  • authorizationCode

Example:

{
"secrets": {
"pets.petstore_oauth": {
"type": "oauth2",
"mode": "clientCredentials",
"clientId": {
"type": "env",
"value": "PETSTORE_CLIENT_ID"
},
"clientSecret": {
"type": "osKeychain",
"value": "oas-cli/petstore-secret"
},
"scopes": ["pets.read"],
"tokenStorage": "instance"
}
}
}

Important runtime behavior:

  • service-specific lookup wins before shared lookup, so pets.petstore_oauth overrides petstore_oauth
  • openIdConnect uses OIDC discovery and then follows the configured mode
  • tokens are cached per runtime instance under the instance state directory
  • tokenStorage: "memory" keeps tokens in-process only
  • authorizationCode uses a loopback callback on 127.0.0.1
  • implicit and password flows are not supported

MCP headerSecrets

MCP transport.headerSecrets references top-level static secrets and injects them into remote MCP transport headers.

Example:

{
"sources": {
"remoteDocs": {
"type": "mcp",
"transport": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp",
"headerSecrets": {
"X-API-Key": "mcp.apiKey"
}
}
}
},
"secrets": {
"mcp.apiKey": {
"type": "env",
"value": "MCP_API_KEY"
}
}
}

MCP transport oauth

Remote MCP HTTP transports can also authenticate the transport itself with a source-local oauth block.

Important runtime behavior:

  • only clientCredentials is allowed for MCP transport OAuth
  • tokenURL can be configured directly, or discovered through the configured OIDC issuer
  • transport tokens are cached per runtime instance
  • if the provider returns a refresh token, later streamable-http and sse requests refresh the token before expiry
  • concurrent MCP clients that share the same instance state reuse the same refreshed transport token instead of racing duplicate refresh requests
  • if refresh fails, or the refreshed token expires again, the transport fails closed and requires reauthorization instead of silently retrying forever