> ## Documentation Index
> Fetch the complete documentation index at: https://cndoc-langchain.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 沙箱认证代理

> 在不硬编码密钥的情况下，将凭据注入来自沙箱的出站 API 请求。

<Warning>
  沙盒功能目前处于私密预览阶段。随着我们的迭代，API 和功能可能会发生变化。[注册等待列表](https://www.langchain.com/langsmith-sandboxes-waitlist?ref=docs.langchain.com)以获取访问权限。
</Warning>

认证代理允许沙箱代码调用外部 API（OpenAI、Anthropic、GitHub 等），而无需硬编码凭据。当在沙箱上配置后，代理 sidecar 会使用你的工作区密钥，自动将认证头注入到匹配的出站请求中。

<Warning>
  在创建引用这些密钥的沙箱之前，你必须在 LangSmith [工作区](/langsmith/administration-overview#workspaces) 设置中配置你的密钥（例如 `OPENAI_API_KEY`）。
</Warning>

## 配置认证代理规则

在创建沙箱时添加 `proxy_config`。每条规则指定：

| 字段            | 描述                                    |
| ------------- | ------------------------------------- |
| `match_hosts` | 要拦截的主机（支持类似 `*.github.com` 的通配符）      |
| `match_paths` | 要匹配的路径（空值 = 所有路径）                     |
| `headers`     | 要注入的头部，每个头部包含 `name`、`type` 和 `value` |
| `no_proxy`    | 完全绕过代理的主机（例如 `localhost`）             |

### 头部类型

每个头部都有一个 `type`，控制其值的存储和显示方式：

| 类型                 | 描述                            |
| ------------------ | ----------------------------- |
| `workspace_secret` | 使用 `{KEY}` 语法引用工作区密钥。在推送时解析。  |
| `plaintext`        | 值按原样存储和返回。用于非敏感头部。            |
| `opaque`           | 仅写入。值在静态存储时加密，且永远不会通过 API 返回。 |

## 单个 API 示例

创建一个沙箱，自动将 OpenAI API 密钥注入到出站请求中：

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
curl -X POST "$LANGSMITH_ENDPOINT/api/v2/sandboxes/boxes" \
  -H "x-api-key: $LANGSMITH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "snapshot_id": "<snapshot-uuid>",
    "name": "openai-sandbox",
    "wait_for_ready": true,
    "proxy_config": {
      "rules": [
        {
          "name": "openai-api",
          "match_hosts": ["api.openai.com"],
          "headers": [
            {
              "name": "Authorization",
              "type": "workspace_secret",
              "value": "Bearer {OPENAI_API_KEY}"
            }
          ]
        }
      ]
    }
  }'
```

沙箱现在可以调用 OpenAI，无需设置 API 密钥——代理会自动注入它。

## 多个 API 示例

添加多条规则以同时向多个服务进行身份验证：

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
curl -X POST "$LANGSMITH_ENDPOINT/api/v2/sandboxes/boxes" \
  -H "x-api-key: $LANGSMITH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "snapshot_id": "<snapshot-uuid>",
    "name": "multi-api-sandbox",
    "wait_for_ready": true,
    "proxy_config": {
      "rules": [
        {
          "name": "openai-api",
          "match_hosts": ["api.openai.com"],
          "headers": [
            {
              "name": "Authorization",
              "type": "workspace_secret",
              "value": "Bearer {OPENAI_API_KEY}"
            }
          ]
        },
        {
          "name": "anthropic-api",
          "match_hosts": ["api.anthropic.com"],
          "headers": [
            {
              "name": "x-api-key",
              "type": "workspace_secret",
              "value": "{ANTHROPIC_API_KEY}"
            },
            {
              "name": "anthropic-version",
              "type": "plaintext",
              "value": "2023-06-01"
            }
          ]
        },
        {
          "name": "github-api",
          "match_hosts": ["api.github.com"],
          "match_paths": ["/repos/*", "/user"],
          "headers": [
            {
              "name": "Authorization",
              "type": "workspace_secret",
              "value": "Bearer {GITHUB_TOKEN}"
            }
          ]
        }
      ],
      "no_proxy": ["localhost", "127.0.0.1"]
    }
  }'
```

## 通过 SDK 配置

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from langsmith.sandbox import SandboxClient

  client = SandboxClient()

  client.create_sandbox(
      snapshot_id=SNAPSHOT_ID,
      name="openai-sandbox",
      proxy_config={
          "rules": [
              {
                  "name": "openai-api",
                  "match_hosts": ["api.openai.com"],
                  "headers": [
                      {
                          "name": "Authorization",
                          "type": "workspace_secret",
                          "value": "Bearer {OPENAI_API_KEY}",
                      }
                  ],
              }
          ]
      },
  )
  ```

  ```ts TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { SandboxClient } from "langsmith/experimental/sandbox";

  const client = new SandboxClient();

  await client.createSandbox(snapshotId, {
    name: "openai-sandbox",
    proxyConfig: {
      rules: [
        {
          name: "openai-api",
          matchHosts: ["api.openai.com"],
          headers: [
            {
              name: "Authorization",
              type: "workspace_secret",
              value: "Bearer {OPENAI_API_KEY}",
            },
          ],
        },
      ],
    },
  });
  ```
</CodeGroup>

## 使用回调的动态凭据

静态规则在沙箱创建时从你的工作区密钥中提取凭据。对于需要按请求解析的凭据——短期 OAuth 令牌、按用户范围限定的令牌、由你自己的认证服务颁发的令牌——请改用**回调**。代理会向你提供的 URL 发送 POST 请求，你的端点返回要注入的头部，代理会缓存结果。

回调与规则一起在 `proxy_config` 下配置：

| 字段                | 描述                                                                                              |
| ----------------- | ----------------------------------------------------------------------------------------------- |
| `match_hosts`     | 要拦截的主机（语法与规则相同；支持类似 `*.github.com` 的通配符）。                                                       |
| `url`             | 你的回调端点。必须是代理可访问的 `http://` 或 `https://` URL。                                                    |
| `request_headers` | 附加到代理 → 回调请求的头部，例如你的端点用于验证请求的 HMAC 或共享密钥。仅允许 `plaintext` 和 `opaque` 类型（不允许 `workspace_secret`）。 |
| `ttl_seconds`     | 解析后的头部在重新调用回调前缓存的时间。必须在 60 到 3600 之间。                                                           |

**静态规则优先。** 如果 `rules` 中的任何规则匹配主机，则跳过该主机的回调。在规则内，首次匹配优先；如果多个回调匹配，同样适用首次匹配优先。

### 回调契约

当代理需要为匹配的主机解析凭据且缓存未命中时，会发出以下请求：

```
POST <callback.url>
Content-Type: application/json
<request_headers from your config, attached verbatim>

{"host": "api.example.com", "port": 443}
```

你的端点必须以 `2xx` 状态码和 JSON 主体响应：

```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
  "headers": {
    "Authorization": "Bearer <token>",
    "X-Org-Id": "..."
  }
}
```

代理将响应中的每个头部注入到沙箱的出站请求中，并将响应缓存 `ttl_seconds`。任何非 2xx 响应、传输错误或格式错误的 JSON 都会导致失败关闭：沙箱的请求将被拒绝，并返回 `502 callback resolution failed`（不注入头部，不缓存响应）。

### 示例

当你的 OAuth 令牌由你自己的服务按需颁发时，使用回调：

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
curl -X POST "$LANGSMITH_ENDPOINT/api/v2/sandboxes/boxes" \
  -H "x-api-key: $LANGSMITH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "snapshot_id": "<snapshot-uuid>",
    "name": "callback-sandbox",
    "wait_for_ready": true,
    "proxy_config": {
      "callbacks": [
        {
          "match_hosts": ["api.github.com", "*.githubusercontent.com"],
          "url": "https://auth.your-app.example.com/sandbox-credentials",
          "request_headers": [
            {
              "name": "X-Integrator-Secret",
              "type": "opaque",
              "value": "<shared-secret-your-endpoint-verifies>"
            }
          ],
          "ttl_seconds": 300
        }
      ]
    }
  }'
```

### 通过 SDK 配置

<CodeGroup>
  ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from langsmith.sandbox import SandboxClient

  client = SandboxClient()

  client.create_sandbox(
      snapshot_id=SNAPSHOT_ID,
      name="callback-sandbox",
      proxy_config={
          "callbacks": [
              {
                  "match_hosts": ["api.github.com", "*.githubusercontent.com"],
                  "url": "https://auth.your-app.example.com/sandbox-credentials",
                  "request_headers": [
                      {
                          "name": "X-Integrator-Secret",
                          "type": "opaque",
                          "value": "<shared-secret-your-endpoint-verifies>",
                      }
                  ],
                  "ttl_seconds": 300,
              }
          ]
      },
  )
  ```

  ```ts TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { SandboxClient } from "langsmith/experimental/sandbox";

  const client = new SandboxClient();

  await client.createSandbox(snapshotId, {
    name: "callback-sandbox",
    proxyConfig: {
      callbacks: [
        {
          matchHosts: ["api.github.com", "*.githubusercontent.com"],
          url: "https://auth.your-app.example.com/sandbox-credentials",
          requestHeaders: [
            {
              name: "X-Integrator-Secret",
              type: "opaque",
              value: "<shared-secret-your-endpoint-verifies>",
            },
          ],
          ttlSeconds: 300,
        },
      ],
    },
  });
  ```
</CodeGroup>

***

<div className="source-links">
  <Callout icon="terminal-2">
    [通过 MCP 将这些文档](/use-these-docs)连接到 Claude、VSCode 等，以获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/langsmith/sandbox-auth-proxy.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
