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 contracts/agents-api/sandbox-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ PUT accepts the same provider while no reset is active. Send the observed genera

Omit `credential` on PUT to keep the current key; an identical selection without it is a no-op. Submitting a key, even the same one, always verifies it and advances the generation; a null or empty key is invalid. A key-only change uses the same full body (provider, existing configuration, optional resources, `expected_generation` and the new `credential`), with no separate route or implicit reset.

Initial setup requires the selected template to appear in the key's team-owned template listing; public readability is not enough. Before an online change, Core verifies that the committed key owns the current template, then requires the candidate key to own that exact template, which anchors team ownership. It also reads the candidate build and every retained build at its original endpoint with the candidate key, and confirms each settled live receipt in the installation-labelled sandbox listing.
Initial setup requires the selected template to appear in the key's team-owned template listing; public readability is not enough. Every online change verifies the committed key's ownership of the current template and the candidate key's ownership and readiness of the candidate template/build. If `credential` is omitted and the normalized key, API URL and domain are unchanged, a template or resource update performs only these selection checks; retained generations and allocation references are not scanned. Existing allocations continue using their original generation and endpoint, with the unchanged credential and normal lifecycle ownership checks. Explicitly submitting a credential, including the same key, or changing either connection selector also verifies the candidate key against the current template, reads every retained build at its original endpoint with the candidate key, and confirms each settled live receipt in the installation-labelled sandbox listing. A failed required check leaves the committed selection unchanged.

| Result | Meaning |
| --- | --- |
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: 3f24f44e53649f44dbd91addeafdcbcdd3cb3ba82679aacff822ebc757809729
source_hash: 618bc3dcf7a027a1629e7c2cb998df759038a3d0804de8c92b2f64b73d124929
---

沙箱部署为 Core 管理的 `openai_hosted` 执行选择沙箱提供商、每个沙箱的资源以及不可变的 Runtime 发行版。PostgreSQL 为每个安装维护一个当前有效选择;Web 和 Core API 写入同一配置。节点文件保存其已安装副本和特定于主机的路径,且不能覆盖其资源或 Runtime。该选择独立于 Harness。部署可以保持未配置状态,没有节点;此时它拒绝托管准入。
Expand Down Expand Up @@ -118,7 +118,7 @@ POST 会在持久保存候选配置之前对其进行验证,并且不会创建

在 PUT 中省略 `credential` 可保留当前密钥;不提供该字段的完全相同选择是无操作。提交密钥时,即使密钥相同,也始终会验证密钥并推进代次;null 或空密钥无效。仅更改密钥时使用相同的完整请求体(提供商、现有 configuration、可选 resources、`expected_generation` 和新 `credential`),无需单独路由,也不会隐式重置。

初始设置要求所选模板出现在该密钥所属团队的模板列表中;仅可公开读取并不足够。在线更改之前,Core 会验证已提交密钥拥有当前模板,然后要求候选密钥拥有完全相同的模板,从而确立团队所有权。Core 还会使用候选密钥在其原始端点读取候选构建和每个保留构建,并确认带安装标签的沙箱列表中每个已结算的实时回执。
初始设置要求所选模板出现在该密钥所属团队的模板列表中;仅可公开读取并不足够。每次在线更改都会验证已提交密钥对当前模板的所有权,以及候选密钥对候选模板/构建的所有权和就绪状态。省略 `credential` 且规范化后的密钥、API URL 和 domain 均未变化时,模板或资源更新只进行这些选择校验,不扫描保留代次和 allocation 引用。已有 allocation 继续使用原代次和原端点、未变的凭据及正常生命周期所有权校验。显式提交凭据(包括同一密钥)或更改任一连接 selector 时,还会验证候选密钥对当前模板的访问,使用候选密钥在原端点读取每个保留构建,并确认带安装标签的沙箱列表中每个已结算的实时回执。任何必需校验失败都保留已提交的选择。

| 结果 | 含义 |
| --- | --- |
Expand Down
4 changes: 2 additions & 2 deletions docs/sandbox-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ A new provider takes these steps:

1. Implement the operation contracts in the adapter package, with native contract tests.
2. Add its specification and resource validators.
3. Implement `sandbox.ConfigurationAdapter` over a typed native configuration. `DecodeInput` strictly parses the separate public `configuration` and write-only `credential` objects of a request. `Encode` produces whitelisted public selectors, read-only observations and separate secret bytes, and never passes request JSON through. `Decode` restores stored selectors, and keeps access to owned resources, without remote admission or new template validation. `Normalize` copies its input before changing it. `ResolveChange`, `Equal` and `WithCredential` own inheritance, identity and credential composition. `Requirements` declares whether a credential and a public Core origin are required, and which setup operations are supported: `Discovery` for `DiscoverConfiguration`, `SelectionDiscovery` for `DiscoverSelection` and `CredentialVerification` for `VerifyCredential`. `DiscoverConfiguration` validates the query and returns a safe catalog, never a mutation or an admission decision, while Core keeps authorization, input limits and deadlines. `DiscoverSelection` resolves a candidate's omitted native values before commit, and `VerifyCredential` verifies a credential's access to owned resources without mutation. Both receive the candidate's `sandbox.DirectConfig` and build any native client for that call only. A node provider accepts only an empty public object, rejects credentials and returns Unsupported for every setup operation and for credential replacement.
3. Implement `sandbox.ConfigurationAdapter` over a typed native configuration. `DecodeInput` strictly parses the separate public `configuration` and write-only `credential` objects of a request. `Encode` produces whitelisted public selectors, read-only observations and separate secret bytes, and never passes request JSON through. `Decode` restores stored selectors, and keeps access to owned resources, without remote admission or new template validation. `Normalize` copies its input before changing it. `ResolveChange`, `Equal` and `WithCredential` own inheritance, identity and credential composition. `CredentialAccessEqual` compares normalized credentials and every connection selector that determines resource access, excluding templates, capacity and discovery metadata; it validates both configurations and returns an error for invalid inputs. The caller separately preserves explicit credential-submission intent. `Requirements` declares whether a credential and a public Core origin are required, and which setup operations are supported: `Discovery` for `DiscoverConfiguration`, `SelectionDiscovery` for `DiscoverSelection` and `CredentialVerification` for `VerifyCredential`. `DiscoverConfiguration` validates the query and returns a safe catalog, never a mutation or an admission decision, while Core keeps authorization, input limits and deadlines. `DiscoverSelection` resolves a candidate's omitted native values before commit, and `VerifyCredential` verifies a credential's access to owned resources without mutation. Both receive the candidate's `sandbox.DirectConfig` and build any native client for that call only. A node provider accepts only an empty public object, rejects credentials and returns Unsupported for every setup operation and for credential replacement.
4. For a node adapter, export from its package the `BuildLocal` constructor, the typed `native` object it decodes and the native files it adds to the shared node artifacts. Node-local settings, such as host paths, live only in that object; resources and the Runtime release are read from the node configuration's `specification`.
5. Register its constructor, policies, configuration adapter and operation declaration in `providers/registry.go`. Its key is the provider kind, which also labels the Provider's observations, and suspension support reads this entry. The generated projections combine each registered mode and deployment policy with the shared field bounds in `sandbox/deployment_contract.go`: the installer reads them from `deploy/node/node_spec.py`, and the TypeScript client and Web from `packages/agents-client/src/deployment-contract.ts`, so Web reads these declarations instead of comparing provider kinds. Regenerate both with `go run ./services/core/cmd/specification-contract -write`.
6. Supply the distribution artifacts for the adapter and its helper, and offer the provider to operators through the registered configuration contract.
Expand All @@ -134,7 +134,7 @@ The configuration adapter must be non-nil, including its concrete value. Every `

Preview and persistence use `providers.Normalize` and `providers.Describe`. `providers.DiscoverSelection` resolves omitted native values before commit, and the complete specification is validated again at persistence. `providers.ResolveChange` owns configuration inheritance, and comparisons use normalized selectors, so preview, retry and commit share the same defaults. The store owns transactions, credential encryption, generation fencing, resource ownership and generic object storage: only the adapter interprets `provider_config` and `provider_metadata`, and `provider_credential` holds ciphertext bound to the installation and generation. Retained generations keep their original public configuration and metadata and compose the current credential through the adapter, so a credential replacement never rewrites a retained selector. Database constraints check object structure, not the registration list.

A direct adapter with a credential verifies all retained generations and allocation references before a key is replaced. The common `sandbox.CallFence` excludes native calls and waits for helper completion, including calls whose callers timed out; execution invokes the prepared verification and fencing callbacks without branching on a vendor.
A direct adapter with a credential always verifies the committed ownership anchor and the candidate selection. It additionally verifies all retained generations and allocation references when a credential is explicitly submitted or `CredentialAccessEqual` reports changed access; otherwise template and resource updates retain the original resource routing without a full scan. The common `sandbox.CallFence` excludes native calls and waits for helper completion, including calls whose callers timed out; execution invokes the prepared verification and fencing callbacks without branching on a vendor.

Vendor deployment validation and SDK setup stay at the construction boundary, and construction never creates an Environment. For node-local adapters `sandbox.Built` returns the provider, probe, installation identity, backend fingerprint and specification digest, and a `Quiescent` check when a helper can outlive its caller; generation collection waits for it. The factory also returns its close function. `execution.RuntimeProvider` binds the adapter to its kind, installation ID, backend fingerprint, generation, mode and node ownership; the database owns the selection, and the in-memory copy is never another authority. Docker and microsandbox run on nodes, and E2B is constructed directly. The node proxy exposes suspension operations only for a backend whose registered declaration supports them, and common lifecycle code admits suspension through the suspension declaration, never through a provider name.

Expand Down
6 changes: 3 additions & 3 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: 54a0f998bf8d851e52229b3e06b6feeb1f21c1c922f4c1649bc093c6c724c6bf
source_hash: 551e0c9a91255e4126456a93971c04791fd662fc0ee27dfd1023bc0541db4b88
---

**沙箱提供商** 为 Core 管理的 Environment 提供 Runtime daemon 运行所需的外层计算资源,以及启动 daemon 的有界引导流程。本指南说明如何添加提供商,并作为 Core 驱动提供商的参考。接口为 [`SandboxProvider`](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/services/core/internal/sandbox/sandbox_provider.go)。
Expand Down Expand Up @@ -112,7 +112,7 @@ Microsandbox 在返回创建不存在回执前永久关闭初始 Create 准入

1. 在 adapter 包中实现 operation 契约,并编写原生契约测试。
2. 添加 specification 和 resource validator。
3. 基于类型化原生配置实现 `sandbox.ConfigurationAdapter`。`DecodeInput` 严格解析请求中独立的公开 `configuration` 与只写 `credential` 对象。`Encode` 生成白名单公开 selector、只读观测和独立 secret bytes,不透传请求 JSON。`Decode` 恢复已存储 selector 并保留对所属资源的访问,不做远程 admission 或新模板验证。`Normalize` 修改前复制输入。`ResolveChange`、`Equal` 和 `WithCredential` 负责继承、身份与凭据组合。`Requirements` 声明是否需要凭据和公开 Core origin,以及支持哪些 setup 操作:`Discovery` 对应 `DiscoverConfiguration`,`SelectionDiscovery` 对应 `DiscoverSelection`,`CredentialVerification` 对应 `VerifyCredential`。`DiscoverConfiguration` 验证 query 并返回安全 catalog,不做 mutation 或 admission decision;Core 保留授权、输入限制与 deadline。`DiscoverSelection` 在提交前解析候选项省略的原生值,`VerifyCredential` 验证凭据对所属资源的访问,不修改资源。两者都接收候选项的 `sandbox.DirectConfig`,原生 client 只为该次调用构造。node provider 仅接受空公开对象,拒绝凭据,对每项 setup 操作和 credential replacement 返回 Unsupported。
3. 基于类型化原生配置实现 `sandbox.ConfigurationAdapter`。`DecodeInput` 严格解析请求中独立的公开 `configuration` 与只写 `credential` 对象。`Encode` 生成白名单公开 selector、只读观测和独立 secret bytes,不透传请求 JSON。`Decode` 恢复已存储 selector 并保留对所属资源的访问,不做远程 admission 或新模板验证。`Normalize` 修改前复制输入。`ResolveChange`、`Equal` 和 `WithCredential` 负责继承、身份与凭据组合。`CredentialAccessEqual` 比较规范化的凭据和所有决定资源访问的连接 selector,排除模板、容量和发现 metadata;它校验两个配置,对无效输入返回错误。调用方另外保留显式提交凭据的意图。`Requirements` 声明是否需要凭据和公开 Core origin,以及支持哪些 setup 操作:`Discovery` 对应 `DiscoverConfiguration`,`SelectionDiscovery` 对应 `DiscoverSelection`,`CredentialVerification` 对应 `VerifyCredential`。`DiscoverConfiguration` 验证 query 并返回安全 catalog,不做 mutation 或 admission decision;Core 保留授权、输入限制与 deadline。`DiscoverSelection` 在提交前解析候选项省略的原生值,`VerifyCredential` 验证凭据对所属资源的访问,不修改资源。两者都接收候选项的 `sandbox.DirectConfig`,原生 client 只为该次调用构造。node provider 仅接受空公开对象,拒绝凭据,对每项 setup 操作和 credential replacement 返回 Unsupported。
4. node adapter 从自己的包中导出 `BuildLocal` constructor、它解码的类型化 `native` 对象,以及它在共享 node artifact 之外添加的原生文件。node-local 设置(如主机路径)只存放在该对象中;resources 和 Runtime release 从 node 配置的 `specification` 读取。
5. 在 `providers/registry.go` 中注册 constructor、policy、configuration adapter 和 operation 声明。其键即 provider kind,也用于标记该 Provider 的观测;暂停支持读取此项。生成的投影组合每个已注册的部署模式和 deployment policy 与 `sandbox/deployment_contract.go` 中的共享 field bound:installer 从 `deploy/node/node_spec.py` 读取,TypeScript 客户端和 Web 从 `packages/agents-client/src/deployment-contract.ts` 读取,因此 Web 读取这些声明,而不比较 provider kind。通过 `go run ./services/core/cmd/specification-contract -write` 重新生成两者。
6. 提供 adapter 和 helper 的发行产物,通过已注册 configuration 契约向运维人员提供 provider。
Expand All @@ -136,7 +136,7 @@ configuration adapter 必须非 nil,包括其具体值。每个 `Configuration

预览和持久化使用 `providers.Normalize` 与 `providers.Describe`。`providers.DiscoverSelection` 在提交前解析省略的原生值,持久化时再次验证完整 specification。`providers.ResolveChange` 负责配置继承,比较使用 normalized selector,使预览、重试和提交共享默认值。store 负责事务、凭据加密、generation fencing、资源所有权和通用对象存储:仅 adapter 解释 `provider_config` 与 `provider_metadata`,`provider_credential` 保存绑定到安装实例与 generation 的密文。保留 generation 保持原公开配置和 metadata,通过 adapter 组合当前凭据,因此替换凭据不重写保留 selector。数据库约束检查对象结构,不检查注册列表。

具有凭据的 direct adapter 在替换 key 前验证全部保留 generation 与 allocation reference。公共 `sandbox.CallFence` 排除原生调用并等待 helper 完成,包括调用方已超时的调用;execution 调用已准备的 verification 和 fencing callback,不按厂商分支。
具有凭据的 direct adapter 始终验证已提交的所有权锚点和候选选择。显式提交凭据或 `CredentialAccessEqual` 表明访问发生变化时,还会验证全部保留 generation 与 allocation reference;否则模板和资源更新保留原有资源路由,不做全量扫描。公共 `sandbox.CallFence` 排除原生调用并等待 helper 完成,包括调用方已超时的调用;execution 调用已准备的 verification 和 fencing callback,不按厂商分支。

厂商部署验证和 SDK setup 留在构造边界,构造不创建 Environment。node-local adapter 的 `sandbox.Built` 返回 provider、probe、installation identity、backend fingerprint 和 specification digest;helper 可能比调用方存活更久时,还返回 `Quiescent` 检查,generation 回收会等待它。factory 还返回 close 函数。`execution.RuntimeProvider` 将 adapter 绑定到 kind、installation ID、backend fingerprint、generation、mode 和 node ownership;选择由数据库负责,内存副本不构成另一权限来源。Docker 与 microsandbox 在 node 上运行,E2B 直接构造。node proxy 仅对注册声明支持 暂停的 backend 暴露暂停操作,公共 lifecycle 通过暂停声明准入 suspension,不通过 provider name。

Expand Down
12 changes: 12 additions & 0 deletions services/core/cmd/server/managed_generations.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ func (s *managedSetup) routeGenerations(candidate execution.PreparedRuntimeDeplo
}
return err
}
adapter, err := s.registry.Lookup(setup.Provider)
if err != nil {
return err
}
sameAccess, err := adapter.Configuration.CredentialAccessEqual(current.Configuration, setup.Configuration)
if err != nil {
return err
}
if sameAccess && !setup.Configuration.ReplacesCredential() {
phase, generation = "candidate_selection", setup.Generation
return verify(setup, nil)
}
withCandidateKey := func(value deployment.Setup, refs []sandbox.Reference) error {
value, err := s.deployment.WithCredential(value, setup)
if err != nil {
Expand Down
Loading