Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,25 @@ export namespace Config {
.describe(
"Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
),
tls: z
.object({
rejectUnauthorized: z
.boolean()
.optional()
.describe("Set to false to accept self-signed certificates. Default is true."),
cert: z.string().optional().describe("Path to client certificate file (PEM format) for mTLS."),
key: z.string().optional().describe("Path to client private key file (PEM format) for mTLS."),
ca: z
.union([z.string(), z.array(z.string())])
.optional()
.describe("Path(s) to CA certificate file(s) (PEM format) to trust."),
})
.optional()
.describe("TLS configuration for secure connections, including mTLS and custom CA certificates."),
proxy: z
.string()
.optional()
.describe("Proxy URL for this provider (e.g., http://proxy.example.com:8080). Overrides environment variables."),
})
.catchall(z.any())
.optional(),
Expand Down
30 changes: 29 additions & 1 deletion packages/opencode/src/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -982,9 +982,17 @@ export namespace Provider {
if (existing) return existing

const customFetch = options["fetch"]
const tlsConfig = options["tls"] as
| {
rejectUnauthorized?: boolean
cert?: string
key?: string
ca?: string | string[]
}
| undefined
const proxyUrl = options["proxy"] as string | undefined

options["fetch"] = async (input: any, init?: BunFetchRequestInit) => {
// Preserve custom fetch if it exists, wrap it with timeout logic
const fetchFn = customFetch ?? fetch
const opts = init ?? {}

Expand Down Expand Up @@ -1016,10 +1024,30 @@ export namespace Provider {
}
}

let tls: BunFetchRequestInit["tls"] | undefined
if (tlsConfig) {
tls = {}
if (tlsConfig.rejectUnauthorized !== undefined) {
tls.rejectUnauthorized = tlsConfig.rejectUnauthorized
}
if (tlsConfig.cert) {
tls.cert = Bun.file(tlsConfig.cert)
}
if (tlsConfig.key) {
tls.key = Bun.file(tlsConfig.key)
}
if (tlsConfig.ca) {
const caFiles = Array.isArray(tlsConfig.ca) ? tlsConfig.ca : [tlsConfig.ca]
tls.ca = caFiles.map((path) => Bun.file(path))
}
}

return fetchFn(input, {
...opts,
// @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682
timeout: false,
...(tls && { tls }),
...(proxyUrl && { proxy: proxyUrl }),
})
}

Expand Down
123 changes: 123 additions & 0 deletions packages/web/src/content/docs/network.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,131 @@ For proxies requiring advanced authentication like NTLM or Kerberos, consider us

If your enterprise uses custom CAs for HTTPS connections, configure OpenCode to trust them.

### System certificate store

Use certificates from your operating system's trusted certificate store (macOS Keychain, Windows Certificate Store, or Linux system certificates):

```bash
# Option 1: Set for all Bun executions
export BUN_OPTIONS="--use-system-ca"

# Option 2: Set via NODE_OPTIONS
export NODE_OPTIONS="--use-system-ca"
```

This is ideal for enterprise environments where IT has pre-installed certificates in the system store.

### Extra CA certificates

To add additional CA certificates without replacing the default bundle:

```bash
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
```

This works for both proxy connections and direct API access.

---

## Per-provider proxy

Override the global proxy settings for a specific provider:

```json
{
"provider": {
"anthropic": {
"options": {
"proxy": "http://proxy.example.com:8080"
}
}
}
}
```

This takes precedence over the `HTTPS_PROXY` and `HTTP_PROXY` environment variables for that provider.

---

## Per-provider TLS configuration

For more granular control over TLS settings, you can configure TLS options per provider in your `opencode.json`:

### Self-signed certificates

Accept self-signed certificates for a specific provider:

```json
{
"provider": {
"my-proxy": {
"api": "@ai-sdk/openai-compatible",
"options": {
"baseURL": "https://my-internal-proxy.example.com",
"tls": {
"rejectUnauthorized": false
}
}
}
}
}
```

:::caution
Disabling certificate validation (`rejectUnauthorized: false`) bypasses TLS security checks. Only use this for trusted internal services.
:::

### Custom CA certificates

Trust a custom CA certificate for a provider:

```json
{
"provider": {
"my-proxy": {
"options": {
"baseURL": "https://my-internal-proxy.example.com",
"tls": {
"ca": "/path/to/ca-cert.pem"
}
}
}
}
}
```

You can also specify multiple CA certificates:

```json
{
"tls": {
"ca": ["/path/to/ca1.pem", "/path/to/ca2.pem"]
}
}
```

---

## mTLS (Mutual TLS)

For services requiring client certificate authentication, configure both the certificate and private key:

```json
{
"provider": {
"mtls-proxy": {
"api": "@ai-sdk/anthropic",
"options": {
"baseURL": "https://llm-proxy-mtls.internal.example.com",
"tls": {
"cert": "/path/to/client-cert.pem",
"key": "/path/to/client-key.pem",
"ca": "/path/to/ca-cert.pem"
}
}
}
}
}
```

This is useful for enterprise environments with mTLS proxies that provide access to LLM APIs.