feat(providers): reopen model request transport config (#60327)

* feat(providers): reopen model request transport config

* chore(config): refresh request override baselines
This commit is contained in:
Vincent Koc
2026-04-03 23:25:11 +09:00
committed by GitHub
parent cedc8bdebb
commit 8f5f78bbe8
18 changed files with 2355 additions and 23 deletions

View File

@@ -9,7 +9,7 @@ Docs: https://docs.openclaw.ai
- Channels/context visibility: add configurable `contextVisibility` per channel (`all`, `allowlist`, `allowlist_quote`) so supplemental quote, thread, and fetched history context can be filtered by sender allowlists instead of always passing through as received.
- Matrix/exec approvals: add Matrix-native exec approval prompts with account-scoped approvers, channel-or-DM delivery, and room-thread aware resolution handling. (#58635) Thanks @gumadeiras.
- Providers/StepFun: add the bundled StepFun provider plugin with standard and Step Plan endpoints, China/global onboarding choices, `step-3.5-flash` on both catalogs, and `step-3.5-flash-2603` currently exposed on Step Plan. (#60032) Thanks @hengm3467.
- Providers/config: add `models.providers.*.request` overrides for headers and auth on model-provider paths, and full request transport overrides for media provider HTTP paths.
- Providers/config: add full `models.providers.*.request` transport overrides for model-provider paths, including headers, auth, proxy, and TLS, and keep media provider HTTP request transport overrides aligned with the same request-policy surface. Thanks @vincentkoc.
- Control UI/skills: add ClawHub search, detail, and install flows directly in the Skills panel. (#60134) Thanks @samzong.
- Outbound/runtime seams: split delivery, target-resolution, and session/transcript helper loading into narrower runtime seams so outbound hot paths and their owner tests avoid broader setup fan-out. (#60311) Thanks @shakkernerd.

View File

@@ -4013,6 +4013,26 @@
"tags": [],
"hasChildren": true
},
{
"path": "agents.defaults.subagents.allowAgents",
"kind": "core",
"type": "array",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "agents.defaults.subagents.allowAgents.*",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "agents.defaults.subagents.announceTimeoutMs",
"kind": "core",
@@ -15601,7 +15621,7 @@
"models"
],
"label": "Model Provider Request Overrides",
"help": "Optional request overrides for model-provider requests. Today this path supports header and auth overrides only; proxy and TLS transport settings are reserved for request paths that can carry them end to end.",
"help": "Optional request overrides for model-provider requests, including extra headers, auth overrides, proxy routing, and TLS client settings. Use these only when your upstream or enterprise network path requires transport customization.",
"hasChildren": true
},
{
@@ -15817,6 +15837,521 @@
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy",
"help": "Optional proxy override for model-provider requests. Use \"env-proxy\" to honor environment proxy settings or \"explicit-proxy\" to route through a specific proxy URL.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.mode",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy Mode",
"help": "Proxy override mode for model-provider requests: \"env-proxy\" or \"explicit-proxy\".",
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy TLS",
"help": "Optional TLS settings used when connecting to the configured proxy.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.ca",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS CA",
"help": "Custom CA bundle used to verify the proxy TLS certificate chain.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.ca.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.ca.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.ca.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.cert",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Cert",
"help": "Client TLS certificate presented to the proxy when mutual TLS is required.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.cert.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.cert.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.cert.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.insecureSkipVerify",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Skip Verify",
"help": "Skips proxy TLS certificate verification. Use only for controlled development environments.",
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.key",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Key",
"help": "Private key paired with request.proxy.tls.cert for proxy mutual TLS.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.key.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.key.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.key.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.passphrase",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Passphrase",
"help": "Optional passphrase used to decrypt request.proxy.tls.key.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.passphrase.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.passphrase.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.passphrase.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.serverName",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy TLS Server Name",
"help": "Optional SNI/server-name override used when establishing TLS to the proxy.",
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.url",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [
"models",
"url-secret"
],
"label": "Model Provider Request Proxy URL",
"help": "Explicit proxy URL used when request.proxy.mode is explicit-proxy. Credentials embedded in the URL are treated as sensitive and redacted from snapshots.",
"hasChildren": false
},
{
"path": "models.providers.*.request.tls",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request TLS",
"help": "Optional TLS settings used when connecting directly to the upstream model endpoint.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.ca",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS CA",
"help": "Custom CA bundle used to verify the upstream TLS certificate chain.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.ca.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.ca.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.ca.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.cert",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS Cert",
"help": "Client TLS certificate presented to the upstream endpoint when mutual TLS is required.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.cert.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.cert.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.cert.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.insecureSkipVerify",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"models",
"security"
],
"label": "Model Provider Request TLS Skip Verify",
"help": "Skips upstream TLS certificate verification. Use only for controlled development environments.",
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.key",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS Key",
"help": "Private key paired with request.tls.cert for upstream mutual TLS.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.key.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.key.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.key.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.passphrase",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS Passphrase",
"help": "Optional passphrase used to decrypt request.tls.key.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.passphrase.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.passphrase.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.passphrase.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.serverName",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request TLS Server Name",
"help": "Optional SNI/server-name override used when establishing upstream TLS.",
"hasChildren": false
},
{
"path": "nodeHost",
"kind": "core",

View File

@@ -4012,6 +4012,26 @@
"tags": [],
"hasChildren": true
},
{
"path": "agents.defaults.subagents.allowAgents",
"kind": "core",
"type": "array",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "agents.defaults.subagents.allowAgents.*",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "agents.defaults.subagents.announceTimeoutMs",
"kind": "core",
@@ -15600,7 +15620,7 @@
"models"
],
"label": "Model Provider Request Overrides",
"help": "Optional request overrides for model-provider requests. Today this path supports header and auth overrides only; proxy and TLS transport settings are reserved for request paths that can carry them end to end.",
"help": "Optional request overrides for model-provider requests, including extra headers, auth overrides, proxy routing, and TLS client settings. Use these only when your upstream or enterprise network path requires transport customization.",
"hasChildren": true
},
{
@@ -15816,6 +15836,521 @@
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy",
"help": "Optional proxy override for model-provider requests. Use \"env-proxy\" to honor environment proxy settings or \"explicit-proxy\" to route through a specific proxy URL.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.mode",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy Mode",
"help": "Proxy override mode for model-provider requests: \"env-proxy\" or \"explicit-proxy\".",
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy TLS",
"help": "Optional TLS settings used when connecting to the configured proxy.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.ca",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS CA",
"help": "Custom CA bundle used to verify the proxy TLS certificate chain.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.ca.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.ca.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.ca.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.cert",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Cert",
"help": "Client TLS certificate presented to the proxy when mutual TLS is required.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.cert.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.cert.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.cert.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.insecureSkipVerify",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Skip Verify",
"help": "Skips proxy TLS certificate verification. Use only for controlled development environments.",
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.key",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Key",
"help": "Private key paired with request.proxy.tls.cert for proxy mutual TLS.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.key.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.key.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.key.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.passphrase",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request Proxy TLS Passphrase",
"help": "Optional passphrase used to decrypt request.proxy.tls.key.",
"hasChildren": true
},
{
"path": "models.providers.*.request.proxy.tls.passphrase.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.passphrase.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.passphrase.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.tls.serverName",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request Proxy TLS Server Name",
"help": "Optional SNI/server-name override used when establishing TLS to the proxy.",
"hasChildren": false
},
{
"path": "models.providers.*.request.proxy.url",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [
"models",
"url-secret"
],
"label": "Model Provider Request Proxy URL",
"help": "Explicit proxy URL used when request.proxy.mode is explicit-proxy. Credentials embedded in the URL are treated as sensitive and redacted from snapshots.",
"hasChildren": false
},
{
"path": "models.providers.*.request.tls",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request TLS",
"help": "Optional TLS settings used when connecting directly to the upstream model endpoint.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.ca",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS CA",
"help": "Custom CA bundle used to verify the upstream TLS certificate chain.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.ca.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.ca.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.ca.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.cert",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS Cert",
"help": "Client TLS certificate presented to the upstream endpoint when mutual TLS is required.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.cert.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.cert.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.cert.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.insecureSkipVerify",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"advanced",
"models",
"security"
],
"label": "Model Provider Request TLS Skip Verify",
"help": "Skips upstream TLS certificate verification. Use only for controlled development environments.",
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.key",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS Key",
"help": "Private key paired with request.tls.cert for upstream mutual TLS.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.key.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.key.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.key.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.passphrase",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"models",
"security"
],
"label": "Model Provider Request TLS Passphrase",
"help": "Optional passphrase used to decrypt request.tls.key.",
"hasChildren": true
},
{
"path": "models.providers.*.request.tls.passphrase.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.passphrase.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.passphrase.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "models.providers.*.request.tls.serverName",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models"
],
"label": "Model Provider Request TLS Server Name",
"help": "Optional SNI/server-name override used when establishing upstream TLS.",
"hasChildren": false
},
{
"path": "nodeHost",
"kind": "core",

View File

@@ -27,6 +27,14 @@ Scope intent:
- `models.providers.*.request.auth.token`
- `models.providers.*.request.auth.value`
- `models.providers.*.request.headers.*`
- `models.providers.*.request.proxy.tls.ca`
- `models.providers.*.request.proxy.tls.cert`
- `models.providers.*.request.proxy.tls.key`
- `models.providers.*.request.proxy.tls.passphrase`
- `models.providers.*.request.tls.ca`
- `models.providers.*.request.tls.cert`
- `models.providers.*.request.tls.key`
- `models.providers.*.request.tls.passphrase`
- `skills.entries.*.apiKey`
- `agents.defaults.memorySearch.remote.apiKey`
- `agents.list[].memorySearch.remote.apiKey`

View File

@@ -461,6 +461,62 @@
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.proxy.tls.ca",
"configFile": "openclaw.json",
"path": "models.providers.*.request.proxy.tls.ca",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.proxy.tls.cert",
"configFile": "openclaw.json",
"path": "models.providers.*.request.proxy.tls.cert",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.proxy.tls.key",
"configFile": "openclaw.json",
"path": "models.providers.*.request.proxy.tls.key",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.proxy.tls.passphrase",
"configFile": "openclaw.json",
"path": "models.providers.*.request.proxy.tls.passphrase",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.tls.ca",
"configFile": "openclaw.json",
"path": "models.providers.*.request.tls.ca",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.tls.cert",
"configFile": "openclaw.json",
"path": "models.providers.*.request.tls.cert",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.tls.key",
"configFile": "openclaw.json",
"path": "models.providers.*.request.tls.key",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "models.providers.*.request.tls.passphrase",
"configFile": "openclaw.json",
"path": "models.providers.*.request.tls.passphrase",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "plugins.entries.brave.config.webSearch.apiKey",
"configFile": "openclaw.json",

View File

@@ -136,7 +136,7 @@ describe("config secret refs schema", () => {
expect(result.ok).toBe(true);
});
it("accepts model provider request secret refs for auth and headers", () => {
it("accepts model provider request secret refs for auth, headers, and tls material", () => {
const result = validateConfigObjectRaw({
models: {
providers: {
@@ -150,6 +150,17 @@ describe("config secret refs schema", () => {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "OPENAI_PROVIDER_TOKEN" },
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: { source: "file", provider: "filemain", id: "/tls/provider-proxy-ca" },
},
},
tls: {
cert: { source: "file", provider: "filemain", id: "/tls/provider-cert" },
key: { source: "file", provider: "filemain", id: "/tls/provider-key" },
},
},
models: [{ id: "gpt-5", name: "gpt-5" }],
},
@@ -160,7 +171,7 @@ describe("config secret refs schema", () => {
expect(result.ok).toBe(true);
});
it("rejects model provider request proxy and tls overrides", () => {
it("rejects model provider request proxy url secret refs", () => {
const result = validateConfigObjectRaw({
models: {
providers: {
@@ -169,10 +180,7 @@ describe("config secret refs schema", () => {
request: {
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
},
tls: {
cert: { source: "file", provider: "filemain", id: "/tls/provider-cert" },
url: { source: "env", provider: "default", id: "PROVIDER_PROXY_URL" },
},
},
models: [{ id: "gpt-5", name: "gpt-5" }],
@@ -184,7 +192,7 @@ describe("config secret refs schema", () => {
expect(result.ok).toBe(false);
if (!result.ok) {
expect(
result.issues.some((issue) => issue.path.includes("models.providers.openai.request")),
result.issues.some((issue) => issue.path.includes("models.providers.openai.request.proxy")),
).toBe(true);
}
});

View File

@@ -12,5 +12,6 @@ export const redactSnapshotTestHints: ConfigUiHints = {
"models.providers.*.baseUrl": { sensitive: true },
"models.providers.*.request.headers.*": { sensitive: true },
"models.providers.*.request.auth.token": { sensitive: true },
"models.providers.*.request.proxy.url": { sensitive: true },
"skills.entries.*.env.GEMINI_API_KEY": { sensitive: true },
};

View File

@@ -330,6 +330,56 @@ describe("redactConfigSnapshot", () => {
expect(restored.models.providers.openai.request.auth.token).toBe("provider-secret-token");
});
it("redacts model provider request proxy URLs from config snapshots", () => {
const hints = buildConfigSchema().uiHints;
const raw = `{
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [],
request: {
proxy: {
mode: "explicit-proxy",
url: "http://alice:secret@proxy.example.internal:8080",
},
},
},
},
},
}`;
const snapshot = makeSnapshot(
{
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [],
request: {
proxy: {
mode: "explicit-proxy",
url: "http://alice:secret@proxy.example.internal:8080",
},
},
},
},
},
},
raw,
);
const result = redactConfigSnapshot(snapshot, hints);
const cfg = result.config as typeof snapshot.config;
expect(cfg.models.providers.openai.request.proxy.url).toBe(REDACTED_SENTINEL);
expect(result.raw).toContain(REDACTED_SENTINEL);
expect(result.raw).not.toContain("alice:secret@");
const restored = restoreRedactedValues(result.config, snapshot.config, hints);
expect(restored.models.providers.openai.request.proxy.url).toBe(
"http://alice:secret@proxy.example.internal:8080",
);
});
it("does not redact maxTokens-style fields", () => {
const snapshot = makeSnapshot({
maxTokens: 16384,

View File

@@ -1385,6 +1385,864 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
},
],
},
proxy: {
anyOf: [
{
type: "object",
properties: {
mode: {
type: "string",
const: "env-proxy",
},
tls: {
type: "object",
properties: {
ca: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
cert: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
key: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
passphrase: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
serverName: {
type: "string",
},
insecureSkipVerify: {
type: "boolean",
},
},
additionalProperties: false,
},
},
required: ["mode"],
additionalProperties: false,
},
{
type: "object",
properties: {
mode: {
type: "string",
const: "explicit-proxy",
},
url: {
type: "string",
minLength: 1,
},
tls: {
type: "object",
properties: {
ca: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
cert: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
key: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
passphrase: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
serverName: {
type: "string",
},
insecureSkipVerify: {
type: "boolean",
},
},
additionalProperties: false,
},
},
required: ["mode", "url"],
additionalProperties: false,
},
],
},
tls: {
type: "object",
properties: {
ca: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
cert: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
key: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
passphrase: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
serverName: {
type: "string",
},
insecureSkipVerify: {
type: "boolean",
},
},
additionalProperties: false,
},
},
additionalProperties: false,
},
@@ -21614,7 +22472,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
},
"models.providers.*.request": {
label: "Model Provider Request Overrides",
help: "Optional request overrides for model-provider requests. Use this only for header and auth overrides today; proxy and TLS transport settings are reserved for request paths that can carry them end to end.",
help: "Optional request overrides for model-provider requests, including extra headers, auth overrides, proxy routing, and TLS client settings. Use these only when your upstream or enterprise network path requires transport customization.",
tags: ["models"],
},
"models.providers.*.request.headers": {
@@ -21654,6 +22512,99 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
help: "Optional prefix prepended to request.auth.value when auth mode is header.",
tags: ["models"],
},
"models.providers.*.request.proxy": {
label: "Model Provider Request Proxy",
help: 'Optional proxy override for model-provider requests. Use "env-proxy" to honor environment proxy settings or "explicit-proxy" to route through a specific proxy URL.',
tags: ["models"],
},
"models.providers.*.request.proxy.mode": {
label: "Model Provider Request Proxy Mode",
help: 'Proxy override mode for model-provider requests: "env-proxy" or "explicit-proxy".',
tags: ["models"],
},
"models.providers.*.request.proxy.url": {
label: "Model Provider Request Proxy URL",
help: "Explicit proxy URL used when request.proxy.mode is explicit-proxy. Credentials embedded in the URL are treated as sensitive and redacted from snapshots.",
tags: ["models", "url-secret"],
},
"models.providers.*.request.proxy.tls": {
label: "Model Provider Request Proxy TLS",
help: "Optional TLS settings used when connecting to the configured proxy.",
tags: ["models"],
},
"models.providers.*.request.proxy.tls.ca": {
label: "Model Provider Request Proxy TLS CA",
help: "Custom CA bundle used to verify the proxy TLS certificate chain.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.proxy.tls.cert": {
label: "Model Provider Request Proxy TLS Cert",
help: "Client TLS certificate presented to the proxy when mutual TLS is required.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.proxy.tls.key": {
label: "Model Provider Request Proxy TLS Key",
help: "Private key paired with request.proxy.tls.cert for proxy mutual TLS.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.proxy.tls.passphrase": {
label: "Model Provider Request Proxy TLS Passphrase",
help: "Optional passphrase used to decrypt request.proxy.tls.key.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.proxy.tls.serverName": {
label: "Model Provider Request Proxy TLS Server Name",
help: "Optional SNI/server-name override used when establishing TLS to the proxy.",
tags: ["models"],
},
"models.providers.*.request.proxy.tls.insecureSkipVerify": {
label: "Model Provider Request Proxy TLS Skip Verify",
help: "Skips proxy TLS certificate verification. Use only for controlled development environments.",
tags: ["security", "models", "advanced"],
},
"models.providers.*.request.tls": {
label: "Model Provider Request TLS",
help: "Optional TLS settings used when connecting directly to the upstream model endpoint.",
tags: ["models"],
},
"models.providers.*.request.tls.ca": {
label: "Model Provider Request TLS CA",
help: "Custom CA bundle used to verify the upstream TLS certificate chain.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.tls.cert": {
label: "Model Provider Request TLS Cert",
help: "Client TLS certificate presented to the upstream endpoint when mutual TLS is required.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.tls.key": {
label: "Model Provider Request TLS Key",
help: "Private key paired with request.tls.cert for upstream mutual TLS.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.tls.passphrase": {
label: "Model Provider Request TLS Passphrase",
help: "Optional passphrase used to decrypt request.tls.key.",
tags: ["security", "models"],
sensitive: true,
},
"models.providers.*.request.tls.serverName": {
label: "Model Provider Request TLS Server Name",
help: "Optional SNI/server-name override used when establishing upstream TLS.",
tags: ["models"],
},
"models.providers.*.request.tls.insecureSkipVerify": {
label: "Model Provider Request TLS Skip Verify",
help: "Skips upstream TLS certificate verification. Use only for controlled development environments.",
tags: ["security", "models", "advanced"],
},
"models.providers.*.models": {
label: "Model Provider Model List",
help: "Declared model list for a provider including identifiers, metadata, and optional compatibility/cost hints. Keep IDs exact to provider catalog values so selection and fallback resolve correctly.",

View File

@@ -748,7 +748,7 @@ export const FIELD_HELP: Record<string, string> = {
"models.providers.*.authHeader":
"When true, credentials are sent via the HTTP Authorization header even if alternate auth is possible. Use this only when your provider or proxy explicitly requires Authorization forwarding.",
"models.providers.*.request":
"Optional request overrides for model-provider requests. Use this only for header and auth overrides today; proxy and TLS transport settings are reserved for request paths that can carry them end to end.",
"Optional request overrides for model-provider requests, including extra headers, auth overrides, proxy routing, and TLS client settings. Use these only when your upstream or enterprise network path requires transport customization.",
"models.providers.*.request.headers":
"Extra headers merged into provider requests after default attribution and auth resolution.",
"models.providers.*.request.auth":
@@ -763,6 +763,40 @@ export const FIELD_HELP: Record<string, string> = {
"Custom auth header value used when auth mode is header.",
"models.providers.*.request.auth.prefix":
"Optional prefix prepended to request.auth.value when auth mode is header.",
"models.providers.*.request.proxy":
'Optional proxy override for model-provider requests. Use "env-proxy" to honor environment proxy settings or "explicit-proxy" to route through a specific proxy URL.',
"models.providers.*.request.proxy.mode":
'Proxy override mode for model-provider requests: "env-proxy" or "explicit-proxy".',
"models.providers.*.request.proxy.url":
"Explicit proxy URL used when request.proxy.mode is explicit-proxy. Credentials embedded in the URL are treated as sensitive and redacted from snapshots.",
"models.providers.*.request.proxy.tls":
"Optional TLS settings used when connecting to the configured proxy.",
"models.providers.*.request.proxy.tls.ca":
"Custom CA bundle used to verify the proxy TLS certificate chain.",
"models.providers.*.request.proxy.tls.cert":
"Client TLS certificate presented to the proxy when mutual TLS is required.",
"models.providers.*.request.proxy.tls.key":
"Private key paired with request.proxy.tls.cert for proxy mutual TLS.",
"models.providers.*.request.proxy.tls.passphrase":
"Optional passphrase used to decrypt request.proxy.tls.key.",
"models.providers.*.request.proxy.tls.serverName":
"Optional SNI/server-name override used when establishing TLS to the proxy.",
"models.providers.*.request.proxy.tls.insecureSkipVerify":
"Skips proxy TLS certificate verification. Use only for controlled development environments.",
"models.providers.*.request.tls":
"Optional TLS settings used when connecting directly to the upstream model endpoint.",
"models.providers.*.request.tls.ca":
"Custom CA bundle used to verify the upstream TLS certificate chain.",
"models.providers.*.request.tls.cert":
"Client TLS certificate presented to the upstream endpoint when mutual TLS is required.",
"models.providers.*.request.tls.key":
"Private key paired with request.tls.cert for upstream mutual TLS.",
"models.providers.*.request.tls.passphrase":
"Optional passphrase used to decrypt request.tls.key.",
"models.providers.*.request.tls.serverName":
"Optional SNI/server-name override used when establishing upstream TLS.",
"models.providers.*.request.tls.insecureSkipVerify":
"Skips upstream TLS certificate verification. Use only for controlled development environments.",
"models.providers.*.models":
"Declared model list for a provider including identifiers, metadata, and optional compatibility/cost hints. Keep IDs exact to provider catalog values so selection and fallback resolve correctly.",
"models.bedrockDiscovery":

View File

@@ -169,6 +169,7 @@ describe("mapSensitivePaths", () => {
expect(hints["gateway.auth.token"]?.sensitive).toBe(true);
expect(hints["models.providers.*.headers.*"]?.sensitive).toBe(true);
expect(hints["models.providers.*.request.headers.*"]?.sensitive).toBe(true);
expect(hints["models.providers.*.request.proxy.tls.cert"]?.sensitive).toBe(true);
expect(hints["skills.entries.*.apiKey"]?.sensitive).toBe(true);
});
@@ -194,7 +195,7 @@ describe("collectMatchingSchemaPaths", () => {
expect(paths.has("mcp.servers.*.url")).toBe(true);
expect(paths.has("models.providers.*.baseUrl")).toBe(true);
expect(paths.has("models.providers.*.request.proxy.url")).toBe(false);
expect(paths.has("models.providers.*.request.proxy.url")).toBe(true);
expect(paths.has("tools.media.audio.request.proxy.url")).toBe(true);
});
});

View File

@@ -456,6 +456,24 @@ export const FIELD_LABELS: Record<string, string> = {
"models.providers.*.request.auth.headerName": "Model Provider Request Auth Header Name",
"models.providers.*.request.auth.value": "Model Provider Request Auth Header Value",
"models.providers.*.request.auth.prefix": "Model Provider Request Auth Header Prefix",
"models.providers.*.request.proxy": "Model Provider Request Proxy",
"models.providers.*.request.proxy.mode": "Model Provider Request Proxy Mode",
"models.providers.*.request.proxy.url": "Model Provider Request Proxy URL",
"models.providers.*.request.proxy.tls": "Model Provider Request Proxy TLS",
"models.providers.*.request.proxy.tls.ca": "Model Provider Request Proxy TLS CA",
"models.providers.*.request.proxy.tls.cert": "Model Provider Request Proxy TLS Cert",
"models.providers.*.request.proxy.tls.key": "Model Provider Request Proxy TLS Key",
"models.providers.*.request.proxy.tls.passphrase": "Model Provider Request Proxy TLS Passphrase",
"models.providers.*.request.proxy.tls.serverName": "Model Provider Request Proxy TLS Server Name",
"models.providers.*.request.proxy.tls.insecureSkipVerify":
"Model Provider Request Proxy TLS Skip Verify",
"models.providers.*.request.tls": "Model Provider Request TLS",
"models.providers.*.request.tls.ca": "Model Provider Request TLS CA",
"models.providers.*.request.tls.cert": "Model Provider Request TLS Cert",
"models.providers.*.request.tls.key": "Model Provider Request TLS Key",
"models.providers.*.request.tls.passphrase": "Model Provider Request TLS Passphrase",
"models.providers.*.request.tls.serverName": "Model Provider Request TLS Server Name",
"models.providers.*.request.tls.insecureSkipVerify": "Model Provider Request TLS Skip Verify",
"models.providers.*.models": "Model Provider Model List",
"models.bedrockDiscovery": "Bedrock Model Discovery",
"models.bedrockDiscovery.enabled": "Bedrock Discovery Enabled",

View File

@@ -42,4 +42,4 @@ export type ConfiguredProviderRequest = {
tls?: ConfiguredProviderRequestTls;
};
export type ConfiguredModelProviderRequest = Pick<ConfiguredProviderRequest, "headers" | "auth">;
export type ConfiguredModelProviderRequest = ConfiguredProviderRequest;

View File

@@ -288,13 +288,7 @@ const ConfiguredProviderRequestSchema = z
.strict()
.optional();
const ConfiguredModelProviderRequestSchema = z
.object({
headers: z.record(z.string(), SecretInputSchema.register(sensitive)).optional(),
auth: ConfiguredProviderRequestAuthSchema,
})
.strict()
.optional();
const ConfiguredModelProviderRequestSchema = ConfiguredProviderRequestSchema;
export const ModelDefinitionSchema = z
.object({

View File

@@ -79,7 +79,7 @@ function collectModelProviderAssignments(params: {
context: params.context,
active: providerIsActive,
inactiveReason: "provider is disabled.",
collectTransportSecrets: false,
collectTransportSecrets: true,
});
}
}

View File

@@ -282,6 +282,18 @@ function buildConfigForOpenClawTarget(entry: SecretRegistryEntry, envId: string)
"x-api-key",
);
}
if (entry.id.startsWith("models.providers.*.request.proxy.tls.")) {
setPathCreateStrict(
config,
["models", "providers", "sample", "request", "proxy", "mode"],
"explicit-proxy",
);
setPathCreateStrict(
config,
["models", "providers", "sample", "request", "proxy", "url"],
"http://proxy.example:8080",
);
}
return config;
}

View File

@@ -982,7 +982,7 @@ describe("secrets runtime snapshot", () => {
expect(second?.search.selectedProvider).toBe("gemini");
});
it("resolves model provider request secret refs for headers and auth", async () => {
it("resolves model provider request secret refs for headers, auth, and tls material", async () => {
const config = asConfig({
models: {
providers: {
@@ -996,6 +996,17 @@ describe("secrets runtime snapshot", () => {
mode: "authorization-bearer",
token: { source: "env", provider: "default", id: "OPENAI_PROVIDER_TOKEN" },
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: { source: "env", provider: "default", id: "OPENAI_PROVIDER_PROXY_CA" },
},
},
tls: {
cert: { source: "env", provider: "default", id: "OPENAI_PROVIDER_CERT" },
key: { source: "env", provider: "default", id: "OPENAI_PROVIDER_KEY" },
},
},
models: [],
},
@@ -1008,6 +1019,9 @@ describe("secrets runtime snapshot", () => {
env: {
OPENAI_PROVIDER_TENANT: "tenant-acme",
OPENAI_PROVIDER_TOKEN: "sk-provider-runtime", // pragma: allowlist secret
OPENAI_PROVIDER_PROXY_CA: "proxy-ca",
OPENAI_PROVIDER_CERT: "client-cert",
OPENAI_PROVIDER_KEY: "client-key",
},
agentDirs: ["/tmp/openclaw-agent-main"],
loadAuthStore: () => ({ version: 1, profiles: {} }),
@@ -1021,6 +1035,17 @@ describe("secrets runtime snapshot", () => {
mode: "authorization-bearer",
token: "sk-provider-runtime",
},
proxy: {
mode: "explicit-proxy",
url: "http://proxy.example:8080",
tls: {
ca: "proxy-ca",
},
},
tls: {
cert: "client-cert",
key: "client-key",
},
});
});

View File

@@ -708,6 +708,110 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.proxy.tls.ca",
targetType: "models.providers.request.proxy.tls.ca",
targetTypeAliases: ["models.providers.*.request.proxy.tls.ca"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.proxy.tls.ca",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.proxy.tls.cert",
targetType: "models.providers.request.proxy.tls.cert",
targetTypeAliases: ["models.providers.*.request.proxy.tls.cert"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.proxy.tls.cert",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.proxy.tls.key",
targetType: "models.providers.request.proxy.tls.key",
targetTypeAliases: ["models.providers.*.request.proxy.tls.key"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.proxy.tls.key",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.proxy.tls.passphrase",
targetType: "models.providers.request.proxy.tls.passphrase",
targetTypeAliases: ["models.providers.*.request.proxy.tls.passphrase"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.proxy.tls.passphrase",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.tls.ca",
targetType: "models.providers.request.tls.ca",
targetTypeAliases: ["models.providers.*.request.tls.ca"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.tls.ca",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.tls.cert",
targetType: "models.providers.request.tls.cert",
targetTypeAliases: ["models.providers.*.request.tls.cert"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.tls.cert",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.tls.key",
targetType: "models.providers.request.tls.key",
targetTypeAliases: ["models.providers.*.request.tls.key"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.tls.key",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "models.providers.*.request.tls.passphrase",
targetType: "models.providers.request.tls.passphrase",
targetTypeAliases: ["models.providers.*.request.tls.passphrase"],
configFile: "openclaw.json",
pathPattern: "models.providers.*.request.tls.passphrase",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
providerIdPathSegmentIndex: 2,
},
{
id: "skills.entries.*.apiKey",
targetType: "skills.entries.apiKey",