Skip to content
Merged
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
1 change: 0 additions & 1 deletion apps/web/src/features/sandbox/NetworkSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useState } from "react";
import type { SandboxDeployment, SandboxSpecification } from "@oac/agents-client";
import { useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { HelpTip, Section } from "../../components/console-ui";
import { useToast } from "../../components/Toast";
Expand Down
36 changes: 3 additions & 33 deletions apps/web/src/features/sandbox/SandboxSetupWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import { useQuery } from "@tanstack/react-query";
import { AnimatePresence } from "motion/react";
import * as m from "motion/react-m";
import { ArrowLeft, ArrowRight, Cloud, ExternalLink, Server, SlidersHorizontal, type LucideIcon } from "lucide-react";
import { useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from "react";
import { createPortal } from "react-dom";
import { useEffect, useId, useState, type FormEvent, type ReactNode } from "react";
import { useTranslation } from "react-i18next";

import { HelpTip } from "../../components/console-ui";
import { Modal } from "../../components/Modal";
import { useConsoleNavigation } from "../../lib/console-navigation";
import { coreFieldError } from "../../lib/core-error";
import { formatBytes } from "../../lib/format";
Expand Down Expand Up @@ -73,14 +71,12 @@ function presetOf(provider: SandboxProvider, resources: SandboxResources): Prese
* which backend (own machines, the recommended one preselected) or the
* Provider's connection, how big each sandbox is (only for a Provider that
* declares a default size), then a review. A Provider's setup declaration
* decides its connection fields, services, cautions and discovered options; the
* decides its connection fields, services and discovered options; the
* Provider's resource declarations decide the size and disk inputs. Advanced
* settings hold the complete size form.
* `current` pre-selects the saved choices when a deployment changes. Keeping
* the backend keeps its saved size; another backend starts from its defaults.
* An update can retain the saved credential.
* A Provider that declares cautions takes a confirmation, once per wizard
* session; a saved deployment has already made it.
* Core's address is `OAC_PUBLIC_URL`: the review only shows it, and
* a configuration Core rejects for it is explained here, where it was saved.
*/
Expand Down Expand Up @@ -115,9 +111,6 @@ export function SandboxSetupWizard({ coreUrl, expectedGeneration, current, disab
const [replacementRequested, setReplacementRequested] = useState(false);
const [checking, setChecking] = useState(false);
const [busy, setBusy] = useState(false);
const [confirmed, setConfirmed] = useState<SandboxProvider | null>(current?.provider ?? null);
const [confirming, setConfirming] = useState<SandboxProvider | null>(null);
const goBack = useRef<HTMLButtonElement>(null);
// Core's reason for rejecting the saved configuration, such as a loopback public_url.
const [rejection, setRejection] = useState<string | null>(null);
const [fieldRejection, setFieldRejection] = useState<unknown>(null);
Expand Down Expand Up @@ -171,15 +164,10 @@ export function SandboxSetupWizard({ coreUrl, expectedGeneration, current, disab
setProvider(next);
}

function proceed(next: SandboxProvider) {
setConfirming(null);
function select(next: SandboxProvider) {
choose(next);
setStep(deploymentContract.providers[next].mode === "direct" ? "connection" : "size");
}
function select(next: SandboxProvider) {
if (sandboxProviderSetup(next).cautions.length > 0 && confirmed !== next) setConfirming(next);
else proceed(next);
}

async function save(event?: FormEvent) {
event?.preventDefault();
Expand Down Expand Up @@ -395,24 +383,6 @@ export function SandboxSetupWizard({ coreUrl, expectedGeneration, current, disab
{page}
</m.div>
</AnimatePresence>
{/* Portaled: the sliding page's transform would otherwise contain the fixed backdrop. */}
{createPortal(
<Modal
open={confirming !== null}
title={t("Use {{provider}}?", { provider: confirming ? sandboxProviderSetup(confirming).label : "" })}
initialFocus={goBack}
onClose={() => setConfirming(null)}
footer={<>
<button type="button" className="button outline" onClick={() => { if (confirming) { setConfirmed(confirming); proceed(confirming); } }}>{t("Use {{provider}}", { provider: confirming ? sandboxProviderSetup(confirming).label : "" })}</button>
<button ref={goBack} type="button" className="button primary" onClick={() => setConfirming(null)}>{t("Back")}</button>
</>}
>
<ul className="wizard-cautions">
{confirming ? sandboxProviderSetup(confirming).cautions.map((caution: Parameters<typeof text>[0]) => <li key={caution.en}>{text(caution)}</li>) : null}
</ul>
</Modal>,
document.body,
)}
</section>
);
}
Expand Down
15 changes: 0 additions & 15 deletions apps/web/src/features/sandbox/sandbox-wizard.css
Original file line number Diff line number Diff line change
Expand Up @@ -350,18 +350,3 @@
.wizard-choice-heading .pill {
margin: 0;
}

/* The risks a Provider declares, in its confirmation. */
.wizard-cautions {
display: grid;
gap: 12px;
margin: 0;
padding: 0;
font-size: 13px;
line-height: 1.5;
list-style: none;
}

.wizard-cautions li {
color: var(--ink-2);
}
2 changes: 0 additions & 2 deletions apps/web/src/i18n/locales/zh-CN/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,6 @@ export const sandbox = {
"Where should sandboxes run?": "沙箱在哪里运行?",
"Which sandbox backend?": "用哪个沙箱提供商?",
"Recommended": "推荐",
"Use {{provider}}?": "使用 {{provider}}?",
"Use {{provider}}": "使用 {{provider}}",
"{{cpus}} CPU · {{memory}}": "{{cpus}} 核 · {{memory}}",
"Save configuration": "保存配置",
"Change the sandbox configuration": "修改沙箱配置",
Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/sandbox-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Select the filesystem configuration before the microsandbox deployment. Selectin
| `public` | Boolean. `true` allows public addresses; `false` allows only Core, the model provider and `domains` |
| `domains` | At most 100 exact host names of at most 253 characters, in lowercase letters, digits, hyphens and dots, whose last label starts with a letter. Wildcards, IP literals, ports and URLs are rejected. Core stores them lowercase, deduplicated and sorted. Every address a name resolves to is allowed, private addresses included |

The default is `{"public": true, "domains": []}`. Under every cap, loopback, link-local and cloud-metadata addresses and all other private addresses stay blocked. Addresses are resolved when a sandbox starts and when it resumes, so a name whose answer changes in between is not followed. A sandbox keeps the cap of the generation that created it.
The default is `{"public": true, "domains": []}`. Under every cap, loopback, link-local and cloud-metadata addresses and all other private addresses stay blocked, except that E2B cannot block the metadata address 169.254.169.254 and its sandboxes cannot reach private networks at all. Addresses are resolved when a sandbox starts and when it resumes, so a name whose answer changes in between is not followed. A sandbox keeps the cap of the generation that created it.

Each Provider declares what it enforces ([Sandbox Provider protocol](../../docs/sandbox-provider.md#network-egress)): microsandbox supports both fields, E2B supports `public: false` but not `domains` because E2B sandboxes cannot reach private networks. A cap the selected Provider does not declare returns 400 `invalid_sandbox_configuration` with `param` `network.public` or `network.domains`.

Expand Down
4 changes: 2 additions & 2 deletions contracts/agents-api/zh/sandbox-deployment.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "沙箱部署"
source: contracts/agents-api/sandbox-deployment.md
source_hash: 7b92833b5d4b9c66df5e93c34dfd58d6be5c8cc1223ba5922abbe31228185525
source_hash: ad85ff2713327c6b1e171088b84829308a50d6b0bca93cae531252745e3c6b4e
---

沙箱部署为 Core 管理的 `openai_hosted` 执行选择沙箱提供商和每个沙箱的资源。PostgreSQL 为每个安装维护一个当前有效选择;Web 和 Core API 写入同一配置。节点文件保存其已安装副本和特定于主机的路径,且不能覆盖其资源。Runtime 不属于该选择:每个节点运行其所属安装验证过的 Runtime。该选择独立于 Harness。部署可以保持未配置状态,没有节点;此时它拒绝托管准入。
Expand Down Expand Up @@ -73,7 +73,7 @@ microsandbox 会配置 CPU、内存和托管根磁盘,并在挂载于 `/enviro
| `public` | 布尔值。`true` 允许公开地址;`false` 只允许 Core、模型提供商和 `domains` |
| `domains` | 最多 100 个精确主机名,每个不超过 253 个字符,由小写字母、数字、连字符和点组成,且最后一个标签以字母开头。通配符、IP 字面量、端口和 URL 会被拒绝。Core 以小写、去重并排序的形式存储。名称解析出的每个地址都被允许,包括私有地址 |

默认值为 `{"public": true, "domains": []}`。无论哪种上限,回环、链路本地、云元数据地址以及其他所有私有地址始终被阻止。地址在沙箱启动和恢复时解析,因此两次解析之间应答变化的名称不会被跟随。沙箱保留创建它的代次的上限。
默认值为 `{"public": true, "domains": []}`。无论哪种上限,回环、链路本地、云元数据地址以及其他所有私有地址始终被阻止,但 E2B 无法阻止元数据地址 169.254.169.254,且 E2B 沙箱根本无法访问私有网络。地址在沙箱启动和恢复时解析,因此两次解析之间应答变化的名称不会被跟随。沙箱保留创建它的代次的上限。

每个 Provider 声明自己能强制执行什么([Sandbox Provider 协议](../../../docs/zh/sandbox-provider.md#network-egress)):microsandbox 支持两个字段,E2B 支持 `public: false` 但不支持 `domains`,因为 E2B 沙箱无法访问私有网络。所选 Provider 未声明的上限会返回 400 `invalid_sandbox_configuration`,`param` 为 `network.public` 或 `network.domains`。

Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Add node shows no command: see [Before you add a node](./nodes.md#before-you-add

A microsandbox sandbox reaches a private address only for the model provider's host and for the names in the deployment's [network cap](../../contracts/agents-api/sandbox-deployment.md#network) `domains`. Any other name that resolves to a private address does not resolve in the sandbox, because DNS rebind protection discards the answer, and a Turn that depends on it fails once the Harness stops retrying. This includes an MCP server at a private address.

Add the server's exact host name to `domains` with `PUT /core/v1/sandbox/deployment`. The node resolves those names with the nameservers in the host's `/etc/resolv.conf` when a sandbox starts and when it resumes. A name that does not resolve there gets no rule, and the node logs `sandbox network names without an address` with the names.
Add the server's exact host name to the domains in **Settings** → **Sandbox and scheduling** → **Network**. The node resolves those names with the nameservers in the host's `/etc/resolv.conf` when a sandbox starts and when it resumes. A name that does not resolve there gets no rule, and the node logs `sandbox network names without an address` with the names.

### Connection failures

Expand Down
2 changes: 1 addition & 1 deletion docs/sandbox-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ The backend fingerprint identifies a native resource namespace, not capacity. Co

### Distribution artifacts and process paths

Each node adapter's registration owns its typed `NodeArtifacts` declaration, the shared node artifacts plus the native files its package exports: logical distribution path, release filename suffix and installation role (`node`, `runtime`, `policy` or `image`). Registration rejects missing declarations, unsafe paths and unknown roles. `go run ./services/core/cmd/provider-artifacts -write` generates the shared Web catalog and Python projection. Run the command without `-write` to check freshness. Distribution packaging, Web availability and node installation read this projection; adding a provider's payload does not add a provider-name branch to those consumers.
Each node adapter's registration owns its typed `NodeArtifacts` declaration, the shared node artifacts plus the native files its package exports: logical distribution path, release filename suffix and installation role (`node`, `runtime` or `image`). Registration rejects missing declarations, unsafe paths and unknown roles. `go run ./services/core/cmd/provider-artifacts -write` generates the shared Web catalog and Python projection. Run the command without `-write` to check freshness. Distribution packaging, Web availability and node installation read this projection; adding a provider's payload does not add a provider-name branch to those consumers.

The launcher supplies `sandbox.ProcessPaths` from the [derived process environment](./configuration.md). Core reads these paths once and passes them to direct construction and setup operations. They are fixed distribution properties, not deployment settings or user-selectable helper paths. Each adapter resolves its own relative helper and state locations; E2B uses `e2b/oac-e2b-provider` and `e2b/`. Missing or nonabsolute roots fail before helper execution. Provider construction and discovery never read process environment variables.

Expand Down
4 changes: 2 additions & 2 deletions docs/zh/getting-started/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "故障排查"
source: docs/getting-started/troubleshooting.md
source_hash: d5191995fac17c30e4d6235b3d5e7d5c91d7d95f8a1600591b5faef8ba7ae3dc
source_hash: c77f0eb6c07c2f262183d690928e03bf51442e59fb04b0acca955dd0177d3d31
---

按症状查找安装、访问、节点和 Session 问题的原因与解决方法。本页面向 Core 安装的运维人员。
Expand Down Expand Up @@ -44,7 +44,7 @@ source_hash: d5191995fac17c30e4d6235b3d5e7d5c91d7d95f8a1600591b5faef8ba7ae3dc

microsandbox 沙箱只能访问模型提供商的主机,以及部署[网络上限](../../../contracts/agents-api/zh/sandbox-deployment.md#network) `domains` 中的名称所对应的私有地址。任何其他解析为私有地址的名称在沙箱内都无法解析,因为 DNS rebind 保护会丢弃该应答,依赖它的 Turn 会在 Harness 停止重试后失败,私有地址上的 MCP 服务器也是如此。

用 `PUT /core/v1/sandbox/deployment` 把该服务器的精确主机名加入 `domains`。节点在沙箱启动和恢复时,用主机 `/etc/resolv.conf` 中的域名服务器解析这些名称。无法解析的名称没有规则,节点会记录 `sandbox network names without an address` 并列出这些名称。
在 **设置** → **沙箱与调度** → **网络** 中把该服务器的精确主机名加入域名列表。节点在沙箱启动和恢复时,用主机 `/etc/resolv.conf` 中的域名服务器解析这些名称。无法解析的名称没有规则,节点会记录 `sandbox network names without an address` 并列出这些名称。

### 连接失败 {#connection-failures}

Expand Down
4 changes: 2 additions & 2 deletions docs/zh/sandbox-provider.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "沙箱提供商协议"
source: docs/sandbox-provider.md
source_hash: 1e6284f09a11fa6ca6ba433537ed2b09135c10e473c4173fd4a6f4ca858e1f0e
source_hash: 45f9a9c4f99c0e764390d832c547ab1303cacb090fc7159d3fb0e5b5c3b25db8
---

沙箱提供商协议连接 Core 与为 Core 管理的(`openai_hosted`)Environment 提供计算资源的 adapter,并由该 adapter 运行启动 Runtime daemon 的有界引导流程。本页面向 adapter 作者:说明提供商必须实现什么,以及可以依赖 Core 做什么。microsandbox 和 E2B 实现了该协议。
Expand Down Expand Up @@ -162,7 +162,7 @@ backend fingerprint 标识原生资源命名空间,不表示容量。Core 保

### 发行产物与进程路径 {#distribution-artifacts-and-process-paths}

每个 node adapter 注册负责其类型化 `NodeArtifacts` 声明,即共享 node artifact 加上其包导出的原生文件:逻辑发行路径、release filename suffix 和安装角色(`node`、`runtime`、`policy` 或 `image`)。注册拒绝缺失声明、不安全路径和未知角色。`go run ./services/core/cmd/provider-artifacts -write` 生成共享 Web catalog 和 Python projection。不带 `-write` 运行可检查是否最新。发行打包、Web availability 和 node 安装读取此投影;添加提供商 payload 不在这些消费者中增加按提供商名称的分支。
每个 node adapter 注册负责其类型化 `NodeArtifacts` 声明,即共享 node artifact 加上其包导出的原生文件:逻辑发行路径、release filename suffix 和安装角色(`node`、`runtime` 或 `image`)。注册拒绝缺失声明、不安全路径和未知角色。`go run ./services/core/cmd/provider-artifacts -write` 生成共享 Web catalog 和 Python projection。不带 `-write` 运行可检查是否最新。发行打包、Web availability 和 node 安装读取此投影;添加提供商 payload 不在这些消费者中增加按提供商名称的分支。

launcher 从[派生进程环境](configuration.md)提供 `sandbox.ProcessPaths`。Core 读取这些路径一次,并传给 direct construction 和 setup 操作。它们是固定发行属性,不是部署设置或用户可选 helper 路径。每个 adapter 解析自己的相对 helper 和 state 位置;E2B 使用 `e2b/oac-e2b-provider` 和 `e2b/`。root 缺失或不是绝对路径时,在执行 helper 前失败。提供商构造与发现不读取进程环境变量。

Expand Down
Loading
Loading