You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 797499a
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: docs/design/trusted-private-voice-base-urls.md
+7-3Lines changed: 7 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ Voice transcription rejects non-loopback HTTP endpoints and endpoints that resol
10
10
11
11
## Design
12
12
13
-
Add `security.allowedInsecureVoiceBaseUrls`, an empty-by-default list of complete base URLs. A configured voice provider receives the exception only when its normalized base URL exactly matches a list entry, including scheme, host, port, and path. Wildcards and hostname suffix matching are not supported.
13
+
Add `security.allowedInsecureVoiceBaseUrls`, an empty-by-default list of complete base URLs. Every entry must include an explicit `http://` or `https://` scheme and the full provider path. A configured voice provider receives the exception only when its normalized base URL exactly matches a list entry, including scheme, host, port, and path; URL serialization and trailing slashes are normalized, but missing schemes or path segments such as `/v1` are not inferred. Wildcards and hostname suffix matching are not supported.
14
14
15
15
The setting is trusted configuration. User, System, and SystemDefaults scopes may provide it; Workspace values are ignored and reported as a settings warning. This prevents a cloned repository from granting itself access to an insecure or private endpoint.
16
16
@@ -22,23 +22,27 @@ The exact-match result travels with the resolved voice configuration so every eg
22
22
23
23
An exact match permits cleartext transport and private RFC 1918, CGNAT, or IPv6 unique-local addresses. Loopback aliases, unspecified addresses, link-local ranges, and known cloud metadata addresses remain blocked. Explicit localhost behavior remains unchanged.
24
24
25
-
Desktop voice merges SystemDefaults, User, and System settings with the same trusted-scope precedence as the CLI; it never reads Workspace settings for this exception. It resolves the selected voice model before credentials and accepts exactly one provider entry with the same model ID. A non-DashScope provider can be selected only when its base URL is present in the exact allowlist, preventing an unrelated model or region from supplying the endpoint and API key.
25
+
Desktop voice merges SystemDefaults, User, and System settings with the same trusted-scope precedence as the CLI; it never reads Workspace settings for this exception. It resolves the selected voice model before credentials and accepts exactly one provider entry with the same model ID, preventing an unrelated model or region from supplying the endpoint and API key. Public HTTPS providers do not require an insecure allowlist entry; cleartext or private-network providers still require an exact match.
26
26
27
27
## Configuration ownership
28
28
29
29
The operator that provisions a regional gateway owns the allowlist entry. Managed deployments should render the provider `baseUrl` and the allowlist entry from the same declarative endpoint value. Adding a region therefore requires no Qwen Code change and cannot drift into a hostname-wide exception.
30
30
31
31
## Failure and rollback behavior
32
32
33
-
Malformed entries and non-matches fail closed. Removing the entry immediately restores the existing HTTPS/public-network requirement after settings reload or process restart. There is no migration because the default list is empty and existing settings retain their behavior.
33
+
Malformed entries and non-matches fail closed. Removing the entry immediately restores the existing HTTPS/public-network requirement after settings reload or process restart.
34
+
35
+
Desktop now treats a provider whose ID exactly matches the selected voice model as authoritative. Duplicate providers, a provider without a complete explicit base URL, an incomplete provider, or an unresolved provider key fail instead of silently falling back to environment credentials. Operators with such an existing entry must either complete it or remove it so the legacy DashScope/environment fallback can apply. This fail-closed behavior prevents an accidental fallback to a different provider or region.
34
36
35
37
## Verification
36
38
37
39
- Preserve default rejection for non-localhost HTTP and private endpoints.
40
+
- Require allowlist entries to include an explicit scheme and full provider path on both CLI and Desktop.
38
41
- Accept two unrelated regional private gateway URLs only when the selected URL exactly matches an entry.
39
42
- Reject scheme, port, host, or path mismatches.
40
43
- Reject non-HTTP(S) URL schemes even when exactly listed.
41
44
- Ignore and warn about Workspace-scoped entries.
42
45
- Continue rejecting link-local and cloud metadata addresses, including AWS IMDS IPv6, after an exact match.
46
+
- Decode IPv4-mapped IPv6 literals consistently so trusted private addresses are accepted while mapped loopback and metadata addresses remain blocked.
43
47
- Match Desktop credentials to one unambiguous provider with the selected voice model ID.
44
48
- Exercise both CLI and Desktop resolution and DNS guard paths.
|`security.folderTrust.enabled`| boolean | Setting to track whether Folder trust is enabled. |`false`|
536
-
|`security.auth.selectedType`| string | The currently selected authentication type. |`undefined`|
537
-
|`security.auth.enforcedType`| string | The required auth type (useful for enterprises). |`undefined`|
538
-
|`security.auth.useExternal`| boolean | Whether to use an external authentication flow. |`undefined`|
539
-
|`security.auth.apiKey`| string |**Deprecated.** API key for OpenAI-compatible authentication. Migrate to `modelProviders` with `envKey` instead — see [Model Providers](./model-providers). |`undefined`|
540
-
|`security.auth.baseUrl`| string |**Deprecated.** Base URL for the OpenAI-compatible API. Migrate to `modelProviders` instead — see [Model Providers](./model-providers). |`undefined`|
541
-
|`security.allowedInsecureVoiceBaseUrls`| array of strings |Exact normalized voice provider base URLs that may use HTTP or resolve to private-network addresses. Wildcards are not supported; metadata and link-local addresses remain blocked. Only User, System, and SystemDefaults scopes are honored. Use only for trusted endpoints in managed private networks. |`[]`|
|`security.folderTrust.enabled`| boolean | Setting to track whether Folder trust is enabled. |`false`|
536
+
|`security.auth.selectedType`| string | The currently selected authentication type. |`undefined`|
537
+
|`security.auth.enforcedType`| string | The required auth type (useful for enterprises). |`undefined`|
538
+
|`security.auth.useExternal`| boolean | Whether to use an external authentication flow. |`undefined`|
539
+
|`security.auth.apiKey`| string |**Deprecated.** API key for OpenAI-compatible authentication. Migrate to `modelProviders` with `envKey` instead — see [Model Providers](./model-providers). |`undefined`|
540
+
|`security.auth.baseUrl`| string |**Deprecated.** Base URL for the OpenAI-compatible API. Migrate to `modelProviders` instead — see [Model Providers](./model-providers). |`undefined`|
541
+
|`security.allowedInsecureVoiceBaseUrls`| array of strings |Complete voice provider base URLs that may use HTTP or resolve to private-network addresses. Each entry must include an explicit `http://` or `https://` scheme and the full path (for example, `/v1`); only URL serialization and trailing slashes are normalized. Wildcards are not supported; metadata and link-local addresses remain blocked. Only User, System, and SystemDefaults scopes are honored. Use only for trusted endpoints in managed private networks. |`[]`|
Copy file name to clipboardExpand all lines: packages/cli/src/config/settingsSchema.ts
+3-2Lines changed: 3 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -2947,11 +2947,12 @@ const SETTINGS_SCHEMA = {
2947
2947
requiresRestart: false,
2948
2948
default: []asstring[],
2949
2949
description:
2950
-
'Exact normalized voice base URLs that may use HTTP or private-network addresses. Wildcards are not supported, and metadata/link-local addresses remain blocked. Only honored from User, System, and SystemDefaults settings scopes; values set in Workspace settings are ignored. Enable only for trusted endpoints in managed private networks.',
2950
+
'Complete voice base URLs that may use HTTP or private-network addresses. Entries must include an explicit http:// or https:// scheme and the full provider path; only URL serialization and trailing slashes are normalized. Wildcards are not supported, and metadata/link-local addresses remain blocked. Only honored from User, System, and SystemDefaults settings scopes; values set in Workspace settings are ignored. Enable only for trusted endpoints in managed private networks.',
2951
2951
showInDialog: false,
2952
2952
items: {
2953
2953
type: 'string',
2954
-
description: 'Complete voice provider base URL (no wildcards)',
2954
+
description:
2955
+
'Complete voice provider base URL with explicit scheme and full path (no wildcards)',
`Voice model '${voiceConfig.model}' resolved to a private-network address.`,
@@ -343,10 +356,11 @@ export async function assertVoiceBaseUrlNetworkAllowed(
343
356
}
344
357
constrecords=Array.isArray(result) ? result : [result];
345
358
if(
346
-
records.some((record)=>
347
-
voiceConfig.allowInsecureBaseUrl
348
-
? isAlwaysBlockedVoiceAddress(record.address)
349
-
: isPrivateNetworkIp(record.address),
359
+
records.some(
360
+
(record)=>
361
+
isAlwaysBlockedVoiceAddress(record.address)||
362
+
(!voiceConfig.allowInsecureBaseUrl&&
363
+
isPrivateNetworkIp(record.address)),
350
364
)
351
365
){
352
366
thrownewError(
@@ -432,14 +446,13 @@ export function resolveVoiceTranscriptionConfig({
432
446
!allowInsecureBaseUrl
433
447
){
434
448
thrownewError(
435
-
`Voice model '${voiceModel}' must use an https baseUrl. Voice audio must not be transmitted in cleartext.`,
449
+
`Voice model '${voiceModel}' must use an https baseUrl. Voice audio must not be transmitted in cleartext. To trust this managed endpoint, add its exact complete URL to security.allowedInsecureVoiceBaseUrls.`,
0 commit comments