你可以在 Environment 的 config.storage.buckets[] 中声明自己的阿里云 OSS bucket。使用该 Environment 创建的 Session 会把每个 bucket(或其一个 prefix)挂载到沙箱的 /mnt/buckets/<name>,Agent 可以像本地目录一样读取和写入。
挂载是 Environment 级配置:Session 创建时会快照当时的配置,之后修改 Environment 不影响已经创建的 Session。
- 每个 Environment 最多声明 8 个 bucket。
- 只支持阿里云 OSS(
provider: "aliyun-oss")。
- 支持只读和读写。
- 读取通过预签名 URL 从 OSS 直接下载;写入通过分片直传直接上传到 OSS(小于 1 MiB 的写入经 Qoder 转发)。
- 用户的 RAM 角色或 AccessKey 不会进入沙箱。
该功能处于 Beta 阶段。创建或更新 Environment 时,只要结果中的 config.storage.buckets 非空,就必须携带请求头 x-qoder-beta: oss-bucket-2026-09-23。首次为 Environment 添加 bucket 还需要账号满足 Beta 资格(白名单或全量开放);不满足时返回 503 feature_not_available。删除 bucket(结果中不含 bucket)不需要该请求头。
配置字段
config.storage 只允许一个字段 buckets,且只对 cloud Environment 有效:
{
"type": "cloud",
"storage": {
"buckets": []
}
}
每个 bucket 的字段:
| 字段 | 类型 | 必填 | 说明 |
|---|
name | string | 是 | 挂载目录名,即 /mnt/buckets/<name>。1–63 个字符,只允许字母、数字、.、_、-,且以字母或数字开头;同一 Environment 内唯一 |
provider | string | 是 | 固定为 "aliyun-oss" |
region | string | 是 | OSS region ID,例如 cn-hangzhou、ap-southeast-1。只接受 region ID,不支持自定义 endpoint |
bucket | string | 是 | bucket 名称,不能包含 /、*、? |
prefix | string | 否 | 只挂载该前缀下的对象;首尾的 / 会被忽略;省略表示整个 bucket。不能包含 *、?、\、NUL,也不能包含空段、. 或 .. 段 |
access | string | 否 | read_only(默认)或 read_write |
accelerator_endpoint | string | 否 | OSS 加速器域名,格式为 <label>.oss-data-acc.aliyuncs.com;仅在 bucket 与沙箱同地域时使用 |
credential | object | 是 | 访问 bucket 的凭证,见下方凭证 |
同一次请求中,name 不能重复;region + bucket + prefix 相同的两个 bucket 会被拒绝。
请求字段是严格校验的:出现未知字段、大小写不匹配的键,或字段类型错误时返回 400。
创建带两个 bucket 的 Environment:
curl -s -X POST "https://api.qoder.com.cn/api/v1/cloud/environments" \
-H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "x-qoder-beta: oss-bucket-2026-09-23" \
-d '{
"name": "with-oss-buckets",
"config": {
"type": "cloud",
"storage": {
"buckets": [
{
"name": "datasets",
"provider": "aliyun-oss",
"region": "cn-hangzhou",
"bucket": "my-bucket",
"prefix": "team/data/",
"access": "read_only",
"credential": {
"type": "ram_role",
"role_arn": "acs:ram::1234567890123456:role/qoder-oss-mount"
}
},
{
"name": "outputs",
"provider": "aliyun-oss",
"region": "cn-hangzhou",
"bucket": "my-bucket",
"prefix": "team/outputs/",
"access": "read_write",
"credential": {
"type": "access_key",
"access_key_id": "LTAI5tExampleAccessKey",
"access_key_secret": "ExampleSecret"
}
}
]
}
}
}'
响应中的凭证只返回读取视图:
{
"id": "env_019e44eb66bb748cabcd1489f6fa4428",
"type": "environment",
"name": "with-oss-buckets",
"description": "",
"config": {
"type": "cloud",
"storage": {
"buckets": [
{
"name": "datasets",
"provider": "aliyun-oss",
"region": "cn-hangzhou",
"bucket": "my-bucket",
"prefix": "team/data/",
"access": "read_only",
"credential": {
"type": "ram_role"
}
},
{
"name": "outputs",
"provider": "aliyun-oss",
"region": "cn-hangzhou",
"bucket": "my-bucket",
"prefix": "team/outputs/",
"access": "read_write",
"credential": {
"type": "access_key",
"access_key_id": "LTAI****sKey"
}
}
]
}
},
"metadata": {},
"archived_at": null,
"created_at": "2026-10-21T08:00:00.000000Z",
"updated_at": "2026-10-21T08:00:00.000000Z"
}
credential.type 二选一:ram_role(推荐)或 access_key。
RAM 角色(推荐)
- 在你的阿里云账号创建一个供 Qoder 扮演的 RAM 角色。信任策略需要允许 Qoder 服务角色扮演;Qoder 服务角色信息请提工单获取。
- 信任策略通过
sts:ExternalId 限定调用方身份:
| 会话所有者 | sts:ExternalId |
|---|
| 个人用户 | 用户 ID |
| 企业(Service Account) | <组织 ID>@<workspace ID> |
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"RAM": ["<Qoder 服务角色 ARN>"]
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": ["<你的用户 ID 或 组织 ID@workspace ID>"]
}
}
}
]
}
- 把该角色的 ARN 填到
credential.role_arn。
Qoder 扮演该角色后,还会附带一条 session policy,把权限进一步收窄到声明的 bucket、prefix 和读写模式。角色自身也需要具备下方权限策略中的动作;实际权限是两者交集。
AccessKey
使用一个专用的 RAM 用户的 AccessKey,不要使用主账号 AccessKey,并给该 RAM 用户授予下方权限策略。
- AccessKey 随 Environment 配置保存,并会快照进之后创建的 Session。
- 读取 Environment 时只返回打码的
access_key_id(例如 LTAI****sKey),access_key_secret 永远不会返回。
- 轮换 AccessKey 只影响之后创建的 Session。
- 没有 STS session policy:prefix 与只读限制由 Qoder 保证,存储侧的最终权限由该 AccessKey 自身的 RAM 策略决定。
更新时保留凭证
config 是整体替换的,但 storage.buckets[].credential 是只写字段,按 bucket 的 name 保留:
- 更新时省略某个 bucket 的
credential,或原样回传读取到的打码凭证,会保留已保存的凭证。
- 修改某个 bucket 的
name 会被视为新 bucket,必须提供完整凭证。
- 把
credential.type 改成另一种类型,或显式传 "credential": null,必须提供完整的新凭证。
- 从数组中删除某个 bucket,或传
"buckets": [],之后创建的 Session 不再挂载它;已有 Session 使用创建时的快照,不受影响。
授权 bucket
bucket 的所有者需要显式允许 Qoder 挂载。授权由 bucket 标签和可选的访问名单文件组成;两处至少有一处指名允许谁挂载,否则挂载会被拒绝。
在 OSS 控制台给 bucket 打标签。用户 ID、组织 ID 和 workspace ID 可以在 Qoder 控制台查看:
| 标签 | 必填 | 说明 |
|---|
qoder-mount | 是 | 值必须为 enabled,表示允许 Qoder 挂载该 bucket |
qoder-mount-users | 否 | 允许挂载的个人用户 ID,英文逗号分隔 |
qoder-mount-organizations | 否 | 允许挂载的组织 ID,英文逗号分隔 |
qoder-mount-workspaces | 否 | 允许挂载的 workspace ID,英文逗号分隔 |
规则:
- 标签值最长 128 字符,只适合放少量 ID。
- 个人会话只匹配
qoder-mount-users;企业会话只匹配 qoder-mount-organizations。两者是不同命名空间,ID 拼写相同也不互认。
- 配置了
qoder-mount-workspaces 时,会话所属 workspace 必须命中。
- 只要存在限制标签,写了的维度都必须命中。
- 标签缓存约 1 分钟;读取失败时最多沿用 10 分钟;明确拒绝(403)立即生效。
访问名单(可选)
需要在用户 / 组织之外按 Agent 或 Session 做更细控制时,可以在挂载目录旁边放一份访问名单文件。它位于挂载目录之外,沙箱里看不到也改不了。
- 文件名:
<prefix 去掉末尾斜杠>.qoder-acl.json。例如 prefix 为 team/data/ 时,文件是 team/data.qoder-acl.json。
- 整 bucket 挂载(不填
prefix)没有访问名单,只能靠标签。
- 只看挂载目录本级这一份,不读取上层目录的名单。
{
"version": 1,
"grants": [
{"users": ["<用户 ID>"]},
{"organizations": ["<组织 ID>"], "workspaces": ["<workspace ID>"], "agents": ["<agent ID>"], "sessions": ["<session ID>"]}
]
}
规则:
- 支持
users、organizations、workspaces、agents、sessions 五个维度。
- 一条 grant 里写了的维度都必须命中;没写的维度不限;写了空列表表示谁都不匹配。
- 任意一条 grant 命中即允许;一条都不命中返回 403。
- 标签和名单都配置时,两处都要命中(取交集)。
- 文件超过 64 KB、不是合法的 version 1 JSON、包含未知键、grant 为
null、维度不是非空字符串数组时,一律拒绝。
agents / sessions 只在真正挂载时校验,保存 Environment 时不检查。
- 名单缓存约 1 分钟;过期后读取失败时最多沿用 10 分钟;403 不沿用。
- 文件不存在不算错误,但此时必须由标签指名允许谁挂载。
权限策略
授予 RAM 角色或 RAM 用户的最小权限:
| 动作 | 用途 | 需要 |
|---|
oss:ListObjects | 列目录 | 只读 / 读写 |
oss:GetBucketTagging | 读 bucket 标签(挂载许可) | 只读 / 读写 |
oss:GetObject | 读文件;读访问名单;改名时读源文件 | 只读 / 读写 |
oss:HeadObject | 查看文件信息(stat) | 只读 / 读写 |
oss:PutObject | 写文件、分片直传、新建目录、改名时写目标文件 | 仅读写 |
oss:DeleteObject | 删除文件和空目录、改名时删源文件 | 仅读写 |
oss:AbortMultipartUpload | 清理中断的直传分片 | 仅读写 |
oss:ListParts | 直传完成前核对分片;缺少会导致大文件写入失败 | 仅读写 |
只读挂载的示例策略(prefix 为 team/data/):
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": ["oss:ListObjects", "oss:GetBucketTagging"],
"Resource": ["acs:oss:*:*:my-bucket"]
},
{
"Effect": "Allow",
"Action": ["oss:GetObject", "oss:HeadObject"],
"Resource": ["acs:oss:*:*:my-bucket/team/data/*"]
},
{
"Effect": "Allow",
"Action": ["oss:GetObject"],
"Resource": ["acs:oss:*:*:my-bucket/team/data.qoder-acl.json"]
}
]
}
读写挂载还需要在第二个 statement 的 Action 中加入 oss:PutObject、oss:DeleteObject、oss:AbortMultipartUpload 和 oss:ListParts。
保存时校验
保存 Environment 时(创建,或更新中新增 / 改动了某个 bucket),Qoder 会以保存者身份、使用提交的凭证试挂一遍:
- 获取凭证(扮演角色或使用 AccessKey);
- 读取 bucket 标签;
- 读取访问名单(如果存在);
- 对 prefix 做一次列举(
max-keys=1)。
只有新增或改动的 bucket 会被检查,未改动的 bucket 不会重复检查。校验有 15 秒超时。
- 配置或权限被拒绝时返回 400
invalid_request_error,message 形如 Bucket '<name>' cannot be mounted: <原因>.。
- OSS / STS 故障时返回 503
api_error,可重试。
- 名单中的
agents / sessions 维度不在保存时校验,留到实际挂载。
沙箱中的行为
挂载点:/mnt/buckets/<name>。access 为 read_only 时它是只读文件系统;read_write 时可以创建、修改和删除文件。
- 目录:对象存储没有真正的目录。目录由对象 key 的前缀表示;空目录用一个 0 字节的
<path>/ 占位对象保持,和 OSS 控制台 / ossutil 的约定一致。
- 读取:Qoder 返回 302 到预签名 URL,沙箱直接从 OSS 下载。
- 写入:小于 1 MiB 的文件经 Qoder 普通上传;1 MiB 及以上的文件走分片直传(沙箱
PUT 各分片到预签名 URL,再提交)。
- 改名:单文件改名是 copy + delete,不原子;多个会话并发改同一路径时可能丢失并发写入,Qoder 不做跨会话仲裁。
- 目录改名:不提供原生支持,客户端会退化为逐文件移动。
- 删除目录:非空目录返回 409,需要先删除目录内文件。
- 列举上限:单个目录超过 100,000 个条目时返回 507,不会截断展示。
- 单文件上限:分片直传最多 10,000 片 × 64 MiB,即 625 GiB;bucket 自身的配额与规格限制仍然适用。
- 中断的上传:写入中断会在 bucket 中留下未完成的分片,建议给 bucket 配置生命周期规则自动清理。
- 地域:bucket 与沙箱同地域时走 OSS 内网 endpoint;跨地域走公网,延迟更高。OSS 加速器只在同地域可用。
- 网络:读取和 1 MiB 以上的写入由沙箱直接访问 OSS;如果 Environment 启用了受限网络,需要允许对应 OSS 域名。
错误码
| HTTP | type | 说明 |
|---|
| 400 | invalid_request_error | bucket 字段不合法、凭证缺失、缺少 x-qoder-beta 请求头,或保存时试挂被拒绝 |
| 401 | authentication_error | PAT 或 SAT 无效或过期 |
| 403 | permission_error | 无权访问目标 Environment |
| 503 | feature_not_available | 账号不满足 oss_bucket Beta 资格 |
| 503 | api_error | 保存时 OSS / STS 暂时不可用,可重试 |
挂载阶段的拒绝(标签未授权、访问名单未命中、凭证无权限等)表现为沙箱内的 EACCES,不会作为 Environment API 的错误返回。排查时优先检查 bucket 标签、访问名单和 RAM 权限。
- 只支持阿里云 OSS,不支持 S3、COS、MinIO 或自定义 endpoint。
- 不管理对象版本、生命周期、bucket ACL 等 OSS 自身配置。
- 一个 bucket 的一个
prefix 在一个 Environment 中只能出现一次;需要挂载同一 bucket 的多个目录时,使用不同的 name 和 prefix。
- 挂载使用环境配置的快照,修改 Environment 不会改变已创建的 Session。