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
8 changes: 4 additions & 4 deletions apps/web/e2e/sandbox-lifecycle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ test("edits the sandbox network cap for new sandboxes", async ({ page, request }
const area = page.getByRole("region", { name: "Network" });
const domains = area.getByLabel("Extra allowed domains");
await expect(area.getByRole("switch", { name: "Allow public internet" })).toBeChecked();
await domains.fill("Git.example.com\n*.example.com");
await expect(area.getByText("*.example.com is not a valid host name.")).toBeVisible();
await domains.fill("Git.example.com\n*.com");
await expect(area.getByText("*.com is not a valid host name.")).toBeVisible();
await expect(area.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
await domains.fill("gitlab.example.com");
await domains.fill("gitlab.example.com\n*.corp.example");
await area.getByRole("switch", { name: "Allow public internet" }).uncheck();
const response = page.waitForResponse((response) => response.request().method() === "PUT" && response.url().endsWith("/sandbox/deployment"));
await area.getByRole("button", { name: "Save", exact: true }).click();
expect((await response).request().postDataJSON().network).toEqual({ public: false, domains: ["gitlab.example.com"] });
expect((await response).request().postDataJSON().network).toEqual({ public: false, domains: ["gitlab.example.com", "*.corp.example"] });
await expect(page.getByText("New sandboxes will run with this policy")).toBeVisible();
});

Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/features/sandbox/NetworkSettings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const deployment = (provider: "microsandbox" | "e2b") => ({ provider, specificat

describe("network policy", () => {
it("validates domains with the generated rule", () => {
expect(domainIssues(parseDomains("gitlab.example.com\n\n a.b-c.example \nGit.Example.com"))).toEqual([]);
expect(domainIssues(["*.example.com", "-x.com", "a.com", "a.com", "a".repeat(254)]).map((issue) => issue.problem)).toEqual(["invalid", "invalid", "duplicate", "too_long"]);
expect(domainIssues(parseDomains("gitlab.example.com\n\n a.b-c.example \nGit.Example.com\n*.corp.example"))).toEqual([]);
expect(domainIssues(["a.*.example.com", "*.com", "-x.com", "a.com", "a.com", "a".repeat(254)]).map((issue) => issue.problem)).toEqual(["invalid", "invalid", "invalid", "duplicate", "too_long"]);
expect(domainIssues(Array.from({ length: 101 }, (_, index) => `h${index}.example.com`))).toEqual([{ problem: "too_many" }]);
});

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/features/sandbox/NetworkSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function NetworkForm({ deployment, disabled, onSave }: { deployment: Sand
</div>
</div>
<div className="settings-row">
<div className="settings-row-label"><label htmlFor="sandbox-network-domains">{t("Extra allowed domains")}</label><HelpTip id="sandbox-network-domains-help">{support.private_domains ? t("One host name per line, such as gitlab.example.com, up to {{count}}. Sandboxes can reach these names, including names on your private network. Wildcards are not accepted.", { count: maxDomains }) : t("{{provider}} sandboxes run in the provider's cloud and cannot reach your private network.", { provider })}</HelpTip></div>
<div className="settings-row-label"><label htmlFor="sandbox-network-domains">{t("Extra allowed domains")}</label><HelpTip id="sandbox-network-domains-help">{support.private_domains ? t("One host name per line, such as gitlab.example.com, up to {{count}}. Sandboxes can reach these names, including names on your private network. *.example.com allows its subdomains, not example.com itself.", { count: maxDomains }) : t("{{provider}} sandboxes run in the provider's cloud and cannot reach your private network.", { provider })}</HelpTip></div>
<div className="settings-row-control field">
<textarea id="sandbox-network-domains" className="network-domains" rows={4} spellCheck={false} autoCapitalize="none" value={text ?? current.domains.join("\n")} disabled={disabled || !support.private_domains} aria-invalid={issues.length > 0} aria-describedby="sandbox-network-domains-help" onChange={(event) => setText(event.target.value)} />
{issues.map((issue, index) => <span className="field-error" key={index}>{message(issue)}</span>)}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/i18n/locales/zh-CN/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const sandbox = {
"When off, sandboxes reach only the domains listed below.": "关闭后,沙箱只能访问下方列出的域名。",
"{{provider}} cannot restrict outbound traffic, so sandboxes always reach the public internet.": "{{provider}} 无法限制出站流量,沙箱始终可以访问公网。",
"Extra allowed domains": "额外允许的域名",
"One host name per line, such as gitlab.example.com, up to {{count}}. Sandboxes can reach these names, including names on your private network. Wildcards are not accepted.": "每行一个主机名,例如 gitlab.example.com,最多 {{count}} 个。沙箱可以访问这些域名,包括内网域名。不支持通配符。",
"One host name per line, such as gitlab.example.com, up to {{count}}. Sandboxes can reach these names, including names on your private network. *.example.com allows its subdomains, not example.com itself.": "每行一个主机名,例如 gitlab.example.com,最多 {{count}} 个。沙箱可以访问这些域名,包括内网域名。*.example.com 放行其子域名,不含 example.com 本身。",
"{{provider}} sandboxes run in the provider's cloud and cannot reach your private network.": "{{provider}} 的沙箱运行在提供商的云端,无法访问你的内网。",
"Add at most {{count}} domains.": "最多添加 {{count}} 个域名。",
"{{domain}} is listed twice.": "{{domain}} 重复了。",
Expand Down
7 changes: 3 additions & 4 deletions contracts/agents-api/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,12 @@ Core may start a hosted Environment from the result of an earlier preparation in
A hosted Session's effective `network` narrows the deployment's [network cap](./sandbox-deployment.md#network), and the policy holds for the sandbox's whole life, including package installation and setup commands:

- `enabled`: the sandbox reaches what the cap allows.
- `disabled`: the sandbox reaches only Core, the Session's model provider and the host's DNS server on port 53. The model provider stays reachable on its `base_url` port at public addresses; a private model provider host is reachable only when the cap lists its exact name in the extra domains. Package installation and setup commands that need the network fail as ordinary setup errors.
- `restricted`: the sandbox reaches the listed domains, Core and the model provider (on the same terms as `disabled`). A domain is allowed on public addresses when the cap allows the public internet, and on its resolved addresses when it is one of the cap's extra domains. Any other domain is rejected at Session creation with `sandbox_network_outside_cap`, whose `param` is that `allowed_domains` entry. If the cap shrinks later, the sandbox attempt fails with the same error.
- `disabled`: the sandbox reaches only Core, the Session's model provider and the host's DNS server on port 53. The model provider stays reachable on its `base_url` port at public addresses; a private model provider host is reachable only when the cap's extra domains cover its name. Package installation and setup commands that need the network fail as ordinary setup errors.
- `restricted`: the sandbox reaches the listed domains, Core and the model provider (on the same terms as `disabled`). A domain is allowed on public addresses when the cap allows the public internet, and on its resolved addresses when the cap's extra domains cover it, by name or through a `*.` entry. Any other domain is rejected at Session creation with `sandbox_network_outside_cap`, whose `param` is that `allowed_domains` entry. If the cap shrinks later, the sandbox attempt fails with the same error.

Limits ([adapter rules](../../docs/sandbox-provider.md#network-egress)):

- A model provider host that the cap lists is reachable like a listed domain, not only on its port.
- On microsandbox, once a sandbox without public internet (`disabled`, `restricted`, or `enabled` under a cap without it) has resumed, a domain allowed on public addresses, the model provider included, is reachable only at the addresses its name had at resume, except over plain HTTP; other sites on those addresses are reachable too.
- A model provider host that the cap lists, by name or through a `*.` entry, is reachable like a listed domain, not only on its port.
- On E2B, a restricted domain is reachable only on ports 80 and 443, and a model provider on another port is reachable on every port of its public addresses.

`disabled` and `restricted` return `sandbox_network_unsupported` when the deployment's sandbox Provider cannot restrict egress. A `self_hosted` Environment accepts only `enabled`, because Core cannot enforce a policy on a machine it does not run.
Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Each item is Core's deliberate or native behavior where the official service beh

- Hosted workspace admission is limited by the [tenant capacity policy](../../docs/configuration.md#tenant-capacity) and the [input reservation deadline](./environments.md#reservations).
- MiniMax Code does not declare `retained_native_history`, so it cannot run on microsandbox, which keeps every Environment on workspace storage; Core rejects the Session ([Harness capabilities](./harness-capabilities.md)).
- The model provider's port stays reachable at public addresses from the sandbox because the Harness runs there, and a private model provider host only when the network cap lists its name; under `disabled` the host's DNS server stays reachable on port 53; MCP connections from the Environment follow the deployment's [network cap](./sandbox-deployment.md#network). After a microsandbox sandbox without public internet resumes, other sites on the addresses of its allowed domains are reachable too ([limits](./environments.md#restricted-network)). E2B sandboxes cannot reach private networks, and `169.254.169.254` answers inside an E2B sandbox under every cap.
- The model provider's port stays reachable at public addresses from the sandbox because the Harness runs there, and a private model provider host only when the network cap lists its name or a `*.` entry that covers it; under `disabled` the host's DNS server stays reachable on port 53; MCP connections from the Environment follow the deployment's [network cap](./sandbox-deployment.md#network). E2B sandboxes cannot reach private networks, and `169.254.169.254` answers inside an E2B sandbox under every cap.
- `disabled` and `restricted` networking run on hosted Environments whose sandbox Provider can restrict egress, within the deployment's network cap; `self_hosted` Environments accept only `enabled` ([network policy](./environments.md#restricted-network)). The official API accepts an `allowed_domains` entry with a leading dot, such as `.example.com`, with unspecified meaning; Core rejects it.
- `packages.system` other than null or an empty list is rejected; system packages must be preinstalled.
- `Environment.files` lists the files recorded at Session creation; files created later through the Environment files API do not appear.
Expand Down
8 changes: 4 additions & 4 deletions contracts/agents-api/sandbox-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ Select the filesystem configuration before the microsandbox deployment. Selectin

### Network

`network` caps what every sandbox of the deployment may reach when its Environment's `network.access` is `enabled` or omitted. The connection to Core and the Session's model provider port on public addresses are always reachable, because the Harness runs inside the sandbox. To reach a model provider at a private address, list its exact host name in `domains`. MCP connections from the Environment follow the cap like any other traffic.
`network` caps what every sandbox of the deployment may reach when its Environment's `network.access` is `enabled` or omitted. The connection to Core and the Session's model provider port on public addresses are always reachable, because the Harness runs inside the sandbox. To reach a model provider at a private address, list its host name, or a `*.` entry that covers it, in `domains`. MCP connections from the Environment follow the cap like any other traffic.

| Field | Accepted value |
| --- | --- |
| `public` | Boolean. `true` allows public addresses; `false` allows only Core, the model provider and `domains` ([microsandbox limit after a resume](./environments.md#restricted-network)) |
| `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 |
| `public` | Boolean. `true` allows public addresses; `false` allows only Core, the model provider and `domains` |
| `domains` | At most 100 entries of at most 253 characters, each an exact host name or `*.` and a host name of at least two labels, in lowercase letters, digits, hyphens and dots, whose last label starts with a letter. `*.example.com` covers the subdomains of `example.com` at any depth but not `example.com` itself, which needs its own entry; other 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, 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.
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. 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
9 changes: 4 additions & 5 deletions contracts/agents-api/zh/environments.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Environment 与模板"
source: contracts/agents-api/environments.md
source_hash: 4175f4a847bf337d1b5c233a56e054218588408429107afa59fbadfc46bfab1e
source_hash: b0587966b17ac30e6626b3ba094005de114fc04c0190a7adc96830f92acb11c3
---

Environment 是 Session 的执行资源,包括 Harness 运行所在的机器、工作区以及已完成准备的能力。Session 通过其 `environment` 配置创建 Environment;不存在独立的 create 调用。Environment 模板是 Session 创建时解析的可复用准备配置。本契约涵盖这两类资源、两种放置方式、输入接纳、能力准备、Skills、Plugins 和 MCP 连接来源。
Expand Down Expand Up @@ -308,13 +308,12 @@ Core 可以用同一 Project 中较早一次准备的结果启动托管 Environm
托管 Session 的有效 `network` 在部署的[网络上限](sandbox-deployment.md#network)内收窄,并且该策略在沙箱的整个生命周期内生效,包括包安装和设置命令:

- `enabled`:沙箱可访问上限允许的范围。
- `disabled`:沙箱只能访问 Core、Session 的模型提供商和主机 53 端口上的 DNS 服务器。模型提供商仅在公开地址上的 `base_url` 端口可达;私有的模型提供商主机只有在上限的额外域名列出其精确名称时才可达。需要网络的包安装和设置命令按普通设置错误失败。
- `restricted`:沙箱可访问所列域名、Core 和模型提供商(条件同 `disabled`)。上限允许公网时,域名在公开地址上被允许;域名属于上限的额外域名时,在其解析出的地址上被允许。其他域名在创建 Session 时被拒绝,错误码为 `sandbox_network_outside_cap`,`param` 指向该 `allowed_domains` 条目。上限之后缩小时,沙箱尝试以同一错误失败。
- `disabled`:沙箱只能访问 Core、Session 的模型提供商和主机 53 端口上的 DNS 服务器。模型提供商仅在公开地址上的 `base_url` 端口可达;私有的模型提供商主机只有在上限的额外域名覆盖其名称时才可达。需要网络的包安装和设置命令按普通设置错误失败。
- `restricted`:沙箱可访问所列域名、Core 和模型提供商(条件同 `disabled`)。上限允许公网时,域名在公开地址上被允许;上限的额外域名按名称或 `*.` 条目覆盖该域名时,在其解析出的地址上被允许。其他域名在创建 Session 时被拒绝,错误码为 `sandbox_network_outside_cap`,`param` 指向该 `allowed_domains` 条目。上限之后缩小时,沙箱尝试以同一错误失败。

限制([适配器规则](../../../docs/zh/sandbox-provider.md#network-egress)):

- 上限列出的模型提供商主机与所列域名一样可达,不限于其端口。
- 在 microsandbox 上,不能访问公网的沙箱(`disabled`、`restricted`,或上限不允许公网时的 `enabled`)恢复后,在公网地址上放行的域名(包括模型提供商)除明文 HTTP 外只能经其名称在恢复时的地址访问;同一地址上的其他站点也可访问。
- 上限按名称或 `*.` 条目列出的模型提供商主机与所列域名一样可达,不限于其端口。
- 在 E2B 上,受限域名只能通过 80 和 443 端口访问;模型提供商使用其他端口时,其公网地址的所有端口均可达。

部署的沙箱 Provider 无法限制出站时,`disabled` 和 `restricted` 返回 `sandbox_network_unsupported`。`self_hosted` Environment 只接受 `enabled`,因为 Core 无法在不由它运行的机器上强制策略。
Expand Down
Loading
Loading