> ## Documentation Index
> Fetch the complete documentation index at: https://ppio.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 在 Sandbox 中使用

本页展示在沙箱中使用 Secret 的端到端示例：创建 Secret，创建引用它的沙箱，在沙箱内运行向外部服务进行身份验证的代码，并在完成后销毁沙箱。沙箱始终只能看到不透明的占位符——对于发往 Secret 允许列表中主机的请求，出站代理会将其替换为真实值。

## 端到端示例

该示例创建一个 Secret，使用 `secret_envs` 启动一个沙箱，在沙箱内运行使用占位符调用上游 API 的命令，最后销毁沙箱。

<CodeGroup>
  ```python Python theme={null}
  import uuid

  from ppio_sandbox import PPIO

  ppio = PPIO()

  secret_name = f"openai-example-{uuid.uuid4().hex[:12]}"
  openai_api_key = "your-openai-api-key"

  # 1. 创建一个团队级 Secret。真实值会被加密存储；
  #    只有允许列表中的主机才会进行替换。
  ppio.secret.create(
      name=secret_name,
      value=openai_api_key,
      hosts=["api.openai.com"],
      description="Sandbox Secrets example",
  )

  # 2. 创建 Sandbox，并将环境变量映射到该 Secret 名称。
  #    环境变量中保存的是占位符，而不是真实值。
  sandbox = ppio.sandbox.create(
      secret_envs={"OPENAI_API_KEY": secret_name},
  )

  try:
      # 3. 在 Sandbox 内运行代码。它从环境变量读取占位符，
      #    并将其放入 HTTPS 请求头发送；由于 api.openai.com 在允许列表中，
      #    代理会在传输过程中替换为真实值。
      result = sandbox.commands.run(
          'curl -s https://api.openai.com/v1/models '
          '-H "Authorization: Bearer $OPENAI_API_KEY"'
      )
      print(result.stdout)
  finally:
      # 4. 完成后终止 Sandbox。
      sandbox.kill()
  ```

  ```javascript JavaScript & TypeScript theme={null}
  import 'dotenv/config'
  import { randomUUID } from 'crypto'
  import { PPIO } from 'ppio-sandbox'

  const ppio = new PPIO()

  const secretName = `openai-example-${randomUUID().slice(0, 12)}`
  const openaiApiKey = "your-openai-api-key"

  // 1. 创建一个团队级 Secret。真实值会被加密存储；
  //    只有允许列表中的主机才会进行替换。
  await ppio.secret.create({
    name: secretName,
    value: openaiApiKey,
    hosts: ['api.openai.com'],
    description: 'Sandbox Secrets example',
  })

  // 2. 创建 Sandbox，并将环境变量映射到该 Secret 名称。
  //    环境变量中保存的是占位符，而不是真实值。
  const sandbox = await ppio.sandbox.create({
    secretEnvs: { OPENAI_API_KEY: secretName },
  })

  try {
    // 3. 在 Sandbox 内运行代码。它从环境变量读取占位符，
    //    并将其放入 HTTPS 请求头发送；由于 api.openai.com 在允许列表中，
    //    代理会在传输过程中替换为真实值。
    const result = await sandbox.commands.run(
      'curl -s https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY"'
    )
    console.log(result.stdout)
  } finally {
    // 4. 完成后终止 Sandbox。
    await sandbox.kill()
  }
  ```

  ```bash CLI theme={null}
  # 1. 创建一个团队级 Secret（--from-env 从本地环境变量读取真实值，
  #    避免密钥进入 shell 历史；--host 为允许列表主机）
  ppio secret create openai-example \
    --host api.openai.com \
    --from-env OPENAI_API_KEY \
    --description "Sandbox Secrets example"

  # 2. 创建 Sandbox，并将环境变量映射到该 Secret 名称（--secret-env 可重复传入）。
  #    环境变量中保存的是占位符，而不是真实值。
  ppio sandbox create base -d --secret-env OPENAI_API_KEY=openai-example

  # 3. 在 Sandbox 内运行命令。发往允许列表主机的请求头中的占位符
  #    会在代理层被替换为真实值。
  ppio sandbox exec <sandbox_id> 'curl -s https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY"'

  # 4. 完成后终止 Sandbox。
  ppio sandbox kill <sandbox_id>
  ```
</CodeGroup>

<Note>
  沙箱内的代码始终只能读到占位符（例如 `ppio_secret_<random_hex>`）。真实密钥在代理层被替换，绝不会出现在沙箱的环境变量、文件系统或进程参数中。
</Note>

## 各步骤的工作原理

| 步骤                                     | 说明                                                                                                                     |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `ppio.secret.create(...)`              | 创建团队级 Secret 及其第一个不可变版本。返回 `SecretBinding` 元数据（不含真实值）。`hosts` 允许列表控制真实值可以被替换到哪些主机。如果已存在同名的活跃 Secret，返回 `409 Conflict`。 |
| `ppio.sandbox.create(secret_envs=...)` | 将环境变量名映射到 Secret 名称。环境变量被设置为占位符，而不是真实值。如果任何被引用的 Secret 无法解析，沙箱创建会失败。                                                   |
| `sandbox.commands.run(...)`            | 在沙箱内运行命令。当它向允许列表中的主机发起 HTTPS 请求且请求头中携带占位符时，代理会在请求发出前将占位符替换为真实值。                                                        |
| `sandbox.kill()`                       | 关闭并移除沙箱。Secret 本身不受影响，可被其他沙箱复用。                                                                                        |

<Note>
  `secret_envs` 与常规 `envs` 的区别：`envs` 直接注入给定的值，而 `secret_envs` 将值解释为 Secret 名称并注入其占位符。同一个环境变量名不能同时出现在两者中。替换仅发生在发往允许列表主机的 HTTPS 请求头的值中——请求体、URL 查询参数、纯 HTTP 和 WebSocket 内容会按原样转发。
</Note>
