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
2 changes: 1 addition & 1 deletion apps/web/e2e/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const FIXTURE_CORE_KEY = "fixture-core-key-3f9a2c71";
* Fixture state options: `fresh` is a new install (no project, Session or Runtime),
* `sandbox` the sandbox deployment, `nodes: "none"` a deployment no node has joined, and
* `installation` how config.json's public_url is set: "public" (HTTPS, the default), "local"
* (loopback: only the Core machine reaches the API, and E2B is rejected) or "stale" (public,
* (loopback: only the Core machine reaches the API, and every sandbox selection is rejected) or "stale" (public,
* with a node enrolled with an earlier address), `credentials: "none"` a Core without a
* credential encryption key, which cannot store a model provider's key, and
* `installers: "none"` a console without its node installation payload, so it serves neither
Expand Down
4 changes: 2 additions & 2 deletions apps/web/e2e/fixture-console.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,8 @@ async function sandboxRoute(request, response, path, url) {
if (!initialize && !state.deployment.provider) return error(response, 409, "The sandbox deployment is not configured.", "sandbox_deployment_conflict");
const e2b = input.provider === "e2b";
if (!e2b && (!input.resources || !input.runtime)) return error(response, 400, "resources and runtime are required.", "invalid_sandbox_configuration");
// As Core (ErrSandboxPublicURLUnreachable): E2B sandboxes reach Core over the internet, which a loopback public_url cannot serve.
if (e2b && state.installation === "local") return error(response, 409, "E2B sandboxes reach Core over the internet. Set an HTTPS public URL that is not loopback (public_url in config.json, OAC_PUBLIC_URL for Core).", "sandbox_configuration_error");
// As Core (ErrPublicURLUnreachable): sandboxes reach Core from outside its host, which a loopback public_url cannot serve.
if (state.installation === "local") return error(response, 409, "Sandboxes reach Core from outside its host. Set an HTTPS public URL that is not loopback (public_url in config.json, OAC_PUBLIC_URL for Core).", "sandbox_configuration_error");
// Synthetic classifier outcomes only; never persist or echo submitted keys.
if (e2b) {
if (!input.configuration?.template || (initialize && !input.credential?.api_key) || (Object.hasOwn(input, "credential") && !input.credential?.api_key)) return error(response, 400, "The E2B API key was rejected.", "sandbox_credential_invalid");
Expand Down
4 changes: 2 additions & 2 deletions apps/web/e2e/public-url.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ test("explains an E2B rejection in the wizard, with a link to domain setup", asy
await selectFixtureE2BBuild(page);
await page.getByRole("button", { name: "Next" }).click();
const address = page.getByRole("definition").filter({ hasText: "http://127.0.0.1:8091" });
await expect(address).toContainText("Set a public address before connecting remote nodes");
await expect(address).toContainText("Set an HTTPS public address before saving");
await page.getByRole("button", { name: "Save configuration" }).click();
const rejection = page.locator(".wizard-rejection");
await expect(rejection).toContainText("E2B sandboxes need a public HTTPS address.");
await expect(rejection).toContainText("Sandboxes need an HTTPS public address that is not loopback.");
await expect(rejection.getByRole("button", { name: "Managed in System" })).toBeVisible();
// Nothing was saved and nothing is uncertain: no dialog, and the wizard stays on its review.
await expect(page.getByRole("dialog")).toHaveCount(0);
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/features/sandbox/SandboxSetupWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function SandboxSetupWizard({ coreUrl, expectedGeneration, current, disab
const [dockerConfirmed, setDockerConfirmed] = useState(current?.provider === "docker");
const [confirmingDocker, setConfirmingDocker] = useState(false);
const keepMicrosandbox = useRef<HTMLButtonElement>(null);
// Core's reason for rejecting the saved configuration, such as E2B with a loopback public_url.
// 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);
const fieldError = (param: string) => coreFieldError(fieldRejection, param, tCommon);
Expand Down Expand Up @@ -379,7 +379,7 @@ export function SandboxSetupWizard({ coreUrl, expectedGeneration, current, disab
<dd>
{address ? <code>{address}</code> : "—"}
<span className="wizard-review-sub">{t("Managed in System")}</span>
{installation.data?.local_only ? <span className="wizard-review-caution">{t("Set a public address before connecting remote nodes; E2B sandboxes need an HTTPS one.")}</span> : null}
{installation.data?.local_only ? <span className="wizard-review-caution">{t("Set an HTTPS public address before saving; sandboxes and remote nodes can't reach this one.")}</span> : null}
</dd>
</div>
</dl>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/i18n/locales/en/core-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const coreErrors = {
"sandbox_credential_invalid": "The E2B API key was rejected. The saved configuration is unchanged.",
"sandbox_configuration_invalid": "Select a ready immutable E2B template build with matching resources.",
"sandbox_verification_unconfirmed": "E2B verification could not be confirmed. Refresh before submitting again.",
"sandbox_configuration_error": "E2B sandboxes need a public HTTPS address. Set OAC_PUBLIC_URL to an HTTPS origin.",
"sandbox_configuration_error": "Sandboxes need an HTTPS public address that is not loopback. Set OAC_PUBLIC_URL to an HTTPS origin.",
"sandbox_deployment_conflict": "The sandbox deployment cannot change in its current state. Refresh and check its reset and resource state.",
"sandbox_specification_mismatch": "The saved sandbox specification does not match the deployment. Refresh to check the configuration.",
"sandbox_operation_unsupported": "The selected sandbox provider does not support this operation.",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/i18n/locales/zh-CN/core-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const coreErrors = {
"sandbox_credential_invalid": "E2B API 密钥被拒绝。已保存的配置未改变。",
"sandbox_configuration_invalid": "请选择已就绪且资源匹配的不可变 E2B 模板构建。",
"sandbox_verification_unconfirmed": "无法确认 E2B 验证结果。请刷新后再提交。",
"sandbox_configuration_error": "E2B 沙箱需要可从互联网访问的 HTTPS 地址,请把 OAC_PUBLIC_URL 设为一个 HTTPS 源地址。",
"sandbox_configuration_error": "沙箱需要非回环的 HTTPS 公开地址,请把 OAC_PUBLIC_URL 设为一个 HTTPS 源地址。",
"sandbox_deployment_conflict": "沙箱部署在当前状态下无法更改。请刷新并检查重置和资源状态。",
"sandbox_specification_mismatch": "已保存的沙箱规格与部署不一致。请刷新检查配置。",
"sandbox_operation_unsupported": "所选沙箱提供商不支持此操作。",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/lib/locale-strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ export const chinese = {
"Change the sandbox configuration": "修改沙箱配置",
"The address nodes and sandboxes use to reach Core.": "节点和沙箱访问 Core 使用的地址。",
"Managed in System": "在系统中管理",
"Set a public address before connecting remote nodes; E2B sandboxes need an HTTPS one.": "连接远程节点前,请先设置公开地址;E2B 沙箱需要 HTTPS 地址。",
"Set an HTTPS public address before saving; sandboxes and remote nodes can't reach this one.": "保存前请设置 HTTPS 公开地址;沙箱和远程节点无法访问当前地址。",
"Enter the E2B key again to save.": "请重新输入 E2B key 后再保存。",
"Enter the key": "输入 key",
"{{name}} is still bound to an old Core address. Remove it and add it again.": "{{name}} 仍绑定在旧的 Core 地址上,需要移除后重新添加。",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/lib/sandbox-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function sandboxWriteUncertain(error: unknown): boolean {

/**
* Core's own reason when it rejects a deployment configuration it cannot serve,
* such as E2B with a loopback public_url; null for any other failure. Nothing
* such as a loopback public_url; null for any other failure. Nothing
* was saved, so the administrator corrects the cause and saves again.
*/
export function sandboxConfigurationRejection(error: unknown, locale: Locale = "en"): string | null {
Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/core-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ These codes have null `param` and no `details`. [Sandbox deployment](./sandbox-d
| 409 | `executor_credential_exists` | The executor credential ID already exists; rotate it to replace the secret |
| 409 | `sandbox_not_configured` | The sandbox deployment is not configured |
| 409 or 503 | `sandbox_reset_in_progress` | A sandbox reset is in progress |
| 409 | `sandbox_configuration_error` | The installation cannot serve the selected provider, such as E2B while the public URL is loopback |
| 409 | `sandbox_configuration_error` | The installation cannot serve the selected provider, such as any provider while the public URL is loopback or not https |
| 409 | `sandbox_deployment_conflict` | The sandbox deployment cannot change in its current state |
| 409 | `sandbox_specification_mismatch` | The saved deployment specification is no longer valid for its provider |
| 409 | `runtime_node_in_use` | The node still holds allocations, snapshots, reservations or pending cleanup |
Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/core.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6222,7 +6222,7 @@ paths:
post:
consumes:
- application/json
description: Selects a provider, enforced resource limits and pinned Runtime release. Core derives the deployment's core_url from the installation public URL and rejects a core_url member with 400. E2B returns 409 sandbox_configuration_error while the public URL is loopback. E2B credentials are write-only. E2B may omit resources to adopt the validated template build's CPU and memory, returned in specification.resources. Requires explicit expected_generation, including zero at first setup. Stale retries reject before provider validation. An identical selection at the current generation is a no-op; differing selections and file-managed deployments reject. This does not create compute or execute work.
description: Selects a provider, enforced resource limits and pinned Runtime release. Core derives the deployment's core_url from the installation public URL and rejects a core_url member with 400. Every provider returns 409 sandbox_configuration_error while the public URL is loopback or not https. E2B credentials are write-only. E2B may omit resources to adopt the validated template build's CPU and memory, returned in specification.resources. Requires explicit expected_generation, including zero at first setup. Stale retries reject before provider validation. An identical selection at the current generation is a no-op; differing selections and file-managed deployments reject. This does not create compute or execute work.
parameters:
- description: Deployment selection
in: body
Expand Down
2 changes: 2 additions & 0 deletions contracts/agents-api/node-generation-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Each operation carries its own arguments and returns the following result on suc
| `resume` | `Resume` | `resume` | `state` |
| `delete_snapshot` | `DeleteSnapshot` | `snapshot` | None |

`bootstrap` is the Provider's `sandbox.Bootstrap`, including the [Sandbox bootstrap](../../docs/sandbox-bootstrap.md) input in `SandboxIO`; Core validates it before it sends `create`.

A request whose `connection_id`, `owner_epoch` or `sequence` does not match closes the connection. A malformed request gets an `invalid` response. A node without generation management accepts only its enrolled `deployment_generation`; a generation-managing node runs the request on that generation's provider and answers `unconfirmed` when it cannot. Core sends `create` and a `resume` that is not observe-only only to a generation that is ready on that node, and keeps at most 32 requests pending per connection.

The budget is relative: the node anchors `timeout_ms` to its own clock on receipt and consumes it while the request waits in its queue, so the hosts' clocks need not agree. Core still bounds its own wait. A full node queue closes the connection.
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 @@ -39,7 +39,7 @@ POST and PUT take the same complete selection and require `expected_generation`
| `configuration` | The provider's public selectors. E2B: the immutable `template` build and the optional paired `api_url` and `domain`. Docker and microsandbox accept only `{}` or omission |
| `credential` | The provider's write-only credential. E2B: `{api_key}`, required at first setup and omitted on PUT to keep the current key; a null or empty key is invalid. Docker and microsandbox reject it |

The request has no Core address. Core derives the deployment's `core_url` from the installation public URL (`public_url` in `config.json`, `OAC_PUBLIC_URL` for Core): the origin nodes and sandbox guests use to reach Core. A request that contains `core_url` is rejected with 400 `invalid_request` like any other unknown member. E2B guests reach Core from E2B's cloud, so an E2B selection is rejected with 409 `sandbox_configuration_error` while the public URL is loopback. Docker and microsandbox selections accept a loopback public URL, which serves only local development because a guest's loopback address does not reach its host. Changing the public URL is an installation change: nodes enrolled with the old address receive no new sandboxes and must be removed and added again.
The request has no Core address. Core derives the deployment's `core_url` from the installation public URL (`public_url` in `config.json`, `OAC_PUBLIC_URL` for Core): the origin nodes and sandbox guests use to reach Core. A request that contains `core_url` is rejected with 400 `invalid_request` like any other unknown member. Every hosted sandbox runs outside Core's network namespace and dials the [sandbox Link](../../docs/configuration.md#changing-the-public-url), so every selection is rejected with 409 `sandbox_configuration_error` until the public URL is https on a host that is not loopback: a loopback host names the sandbox's own namespace, and an http origin elsewhere has no Link. Changing the public URL is an installation change: nodes enrolled with the old address receive no new sandboxes and must be removed and added again.

### Resources

Expand Down
4 changes: 2 additions & 2 deletions contracts/agents-api/zh/core-errors.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Core 管理错误"
source: contracts/agents-api/core-errors.md
source_hash: 50b9624c14d3d600f991fcc9da741b6c4c4722831568c28e849a574bb34a4b06
source_hash: 56fd21c7a7be2ddbc7a3c4163473d03fd6d72de05ba79f57d0389ae768f5d534
---

`/core/v1` 上的错误使用此封装结构。`message` 是安全的英文文本;`code` 和 `param` 可以为 null。客户端依据稳定的 `code` 和可选的 `param` 进行处理,对未知代码显示 `message`,绝不解析消息,也绝不自动重试被拒绝的写操作。
Expand Down Expand Up @@ -88,7 +88,7 @@ Web 的控制台服务器在 `/core` 路径上发生自身故障时使用此封
| 409 | `executor_credential_exists` | 该执行器凭证 ID 已存在;要替换密钥,请轮换它 |
| 409 | `sandbox_not_configured` | 沙箱部署尚未配置 |
| 409 或 503 | `sandbox_reset_in_progress` | 沙箱正在重置 |
| 409 | `sandbox_configuration_error` | 当前安装无法支持所选提供商,例如公开 URL 为 loopback 时选择 E2B |
| 409 | `sandbox_configuration_error` | 当前安装无法支持所选提供商,例如公开 URL 为 loopback 或不是 https 时的任何提供商 |
| 409 | `sandbox_deployment_conflict` | 沙箱部署在当前状态下无法更改 |
| 409 | `sandbox_specification_mismatch` | 已保存的部署规格对其提供商不再有效 |
| 409 | `runtime_node_in_use` | 节点仍有资源分配、快照、预留资源或待清理项 |
Expand Down
4 changes: 3 additions & 1 deletion contracts/agents-api/zh/node-generation-protocol.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "沙箱节点协议"
source: contracts/agents-api/node-generation-protocol.md
source_hash: 1ee43dfcdd0eec0806ea3bc8a4c1227e10bd8ac5e69486505cb113a98e3f548a
source_hash: 1562f69c8c7a5937a10b98c8b7dea2518bfbf1f875c2bd67ff0461467ed72cb9
---

沙箱节点在其主机上运行 Docker 或 microsandbox Provider,并通过一个 WebSocket 与 Core 相连。Core 通过该连接发送 Provider 操作;节点针对本地 Provider 执行这些操作,并报告就绪状态、主机测量值及其持有的部署代次。Core 始终是唯一的生命周期所有者:节点绝不重试变更操作或调度工作。帧和校验器位于 [`services/core/internal/sandbox/node`](https://github.com/MiniMax-AI/OpenAgentCore/tree/main/services/core/internal/sandbox/node)(`wire.go`、`generation_wire.go`);节点用于注册和读取配置的 HTTP 路由位于[机器连接 API](machine-api.md#node-routes)。
Expand Down Expand Up @@ -55,6 +55,8 @@ Core 发送包含以下内容的 `request` 帧:
| `resume` | `Resume` | `resume` | `state` |
| `delete_snapshot` | `DeleteSnapshot` | `snapshot` | 无 |

`bootstrap` 是 Provider 的 `sandbox.Bootstrap`,其 `SandboxIO` 包含[沙箱引导](../../../docs/zh/sandbox-bootstrap.md)输入;Core 在发送 `create` 前完成校验。

只要 `connection_id`、`owner_epoch` 或 `sequence` 中任一值不匹配,请求就会关闭连接。格式错误的请求会得到 `invalid` 响应。未启用代次管理的节点仅接受其登记的 `deployment_generation`;支持代次管理的节点在对应代次的 Provider 上运行请求,无法运行时回复 `unconfirmed`。Core 仅向节点上已就绪的代次发送 `create` 和非 observe-only 的 `resume`,并且每条连接最多保留 32 个待处理请求。

预算采用相对计时:节点收到请求时以自己的时钟为基准锚定 `timeout_ms`,并在请求排队等待期间持续消耗该预算,因此各主机的时钟无需保持一致。Core 仍会限制自身等待时长。节点队列已满时会关闭连接。
Expand Down
Loading
Loading