WeKnora 沙箱、技能与个人变量 API 完全指南:从配置到安装的完整实战
【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora
沙箱是 WeKnora 中为智能体技能提供隔离执行环境的远程计算设施,技能目录则是技能包的空间级管理中枢。本文基于 沙箱与技能 API 参考 展开,覆盖沙箱配置(Docker / CubeSandbox / E2B)、网络策略、连通性探测、技能目录、沙箱内技能安装管理以及个人环境变量五类/api/v1接口,并结合仓库源码说明权限边界、错误语义与底层实现,帮助读者用 REST 调用完成「创建沙箱配置 → 添加技能 → 安装到沙箱 → 配置个人凭据 → 供智能体使用」的完整闭环。
权限模型:谁可以读写什么
所有接口路径均以/api/v1为前缀,示例中的$BASE为服务地址、$TOKEN为当前用户的 Bearer token。权限分为「JWT 用户角色」与「API Key 能力」两个维度,两者叠加生效:
| 资源 | 读 | 写 / 检测 |
|---|---|---|
| 沙箱配置列表/详情 | Viewer+;API Key 必须 full-access | Admin+;API Key 必须 full-access |
| 沙箱实例清单、已安装技能及文件 | Admin+ | Admin+;API Key 必须 full-access |
| 技能目录列表 | Viewer+,JWT | 目录增删、安装、包文件浏览需 Admin+;写组的 API Key 必须 full-access |
/skills可用技能 | Viewer+,JWT | 只读,查询参数sandbox_config_id |
/me/env-vars* | 已登录的当前调用者 | 仅修改自己,服务端推导身份;无额外管理员门槛,使用 Bearer JWT |
跨空间 ID 不授予访问权——所有查询都以当前认证空间(tenant)为作用域,传入其他空间的配置或技能 ID 只会得到 404 而非越权数据。个人变量接口不能替代空间技能管理接口:前者只能设置「当前调用者自己的」覆盖值,后者才是管理员为整个空间维护技能默认值的入口。
上述映射在路由注册代码中有清晰体现。在 routes_infra.go 中,/sandbox-configs整组通过apiKeyGroup(..., apiKeyFullAccess())限定 API Key 必须 full-access,再逐路由叠加g.Viewer()或g.Admin()守卫;注释还解释了为什么已安装技能的读取也要 Admin+:「上传会驱动一个 root shell,其输出被烘焙进该配置每个会话启动的镜像,列表则暴露了镜像携带的内容」——技能读写与沙箱镜像安全深度绑定,因此不能向普通成员放开。
沙箱配置:创建、查询与生命周期管理
端点一览
| 方法 | 路径 | 请求 / 响应 |
|---|---|---|
| GET | /sandbox-configs | 200{success,data:[ConfigResponse],workspace_scripts_disabled} |
| POST | /sandbox-configs | {name,description?,config};201{success,data:ConfigResponse} |
| GET | /sandbox-configs/:id | 200{success,data:ConfigResponse} |
| PUT | /sandbox-configs/:id | {name,description?,config},name 必填;200 同详情 |
| DELETE | /sandbox-configs/:id | 可选force=true;200{success:true} |
| GET | /sandbox-configs/:id/sandboxes | 200{success,data:SandboxInventory},包含占用和关联智能体 |
| PUT | /sandbox-configs/workspace-policy | {"scripts_disabled":true};返回 success/workspace_scripts_disabled |
| POST | /sandbox-configs/templates/query | {config,config_id?,ensure_standard?,replace_standard?};查询远端模板,可创建/替换标准模板 |
ConfigResponse 结构为{id,name,description,sandbox_type,config,created_at,updated_at},凭据一律脱敏。编辑时以保存的配置为基础进行更新;响应中的脱敏占位值保留旧凭据,skill_image由安装服务维护、客户端不能替换。这一「响应即脱敏投影」的约束在 sandbox_config.go 中由sandboxConfigResponse类型强制保证——注释明确指出它是已存配置唯一的外向投影,新读取路径不可能意外返回解密凭据;SandboxConfigForResponse(e.Config, true)负责执行脱敏转换。
config 字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
sandbox_type | string | docker / cube / e2b;local 已移除 |
default_timeout_sec | int | 执行超时,0 使用内置默认 |
allow_private_endpoints | bool | 允许私网集群地址,不放行 link-local/云元数据 |
env_vars | map[string]string | 该沙箱配置的环境,值加密保存 |
skill_rollout | string | next_turn(默认)/new_session |
network | object | Cube/E2B 网络策略,见下节 |
cube/e2b/docker | object | 与sandbox_type对应的连接配置 |
volume_mount | object | 可选卷配置;后端能力决定是否生效,不能仅凭字段存在认定支持 |
skill_image | object | 安装服务维护的快照信息,只读 |
volume_mount字段包括enabled、mount_path、provider、volume_id、volume_name;volume_owner_fingerprint由服务端管理。卷配置是否真正挂载由「卷配置 + 后端支持能力」共同决定,编辑时需保留服务端返回的归属信息。
按后端区分的连接字段
| 后端 | 字段 |
|---|---|
| cube | api_url、proxy_url、sandbox_domain、template_id;api_key按集群需要;http_timeout_sec、cube_sandbox_ttl_seconds、dns_servers |
| e2b | api_key、template_id必填;api_url、sandbox_domain、proxy_url用于自托管;http_timeout_sec、e2b_sandbox_ttl_seconds |
| docker | image必填;host(空为本机 socket)、tls_cert_path(TCP 必需)、cpu_limit、memory_limit_mb、pids_limit、network_mode(bridge/none)、runtime、idle_ttl_seconds、http_timeout_sec |
创建配置与查看库存
curl -X POST "$BASE/api/v1/sandbox-configs" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"name":"E2B 工作环境","config":{"sandbox_type":"e2b","e2b":{"api_key":"<e2b-key>","template_id":"<template-id>"}}}' curl "$BASE/api/v1/sandbox-configs/cfg-1/sandboxes" \ -H "Authorization: Bearer $TOKEN"GET /sandbox-configs/:id/sandboxes返回的 SandboxInventory 包含「占用和关联智能体」,用于定位哪些会话、哪些智能体正在使用该配置——这是管理员删除或变更后端身份前的必要预检手段。
冲突与锁定:409 / 423 错误语义
修改后端身份和删除配置前,系统会查询远端实例(运行中/暂停的沙箱)。存在占用时返回 409,其error.code可取以下值:
sandboxes_still_live:该配置仍有运行中或已暂停的沙箱,需先结束/删除相关会话,或新建一份配置(响应error.data携带完整库存,避免服务端重算造成不一致,见 sandbox_config.go);sandbox_inventory_unverifiable:无法连接后端核实是否仍有沙箱;skill_snapshot_release_failed:无法销毁该配置下的技能快照,已中止删除以免快照继续计费(附snapshot_ids);skill_snapshot_blocks_template:该配置已安装 Skill,不能更换连接、DNS 或重建运行模板,需新建一份沙箱后再装 Skill。
423 表示配置正在被另一个请求修改(sandbox_config_cordoned,相当于乐观锁)。force=true仅允许在库存无法核实时删除,不会跳过已确认的活跃/暂停实例——这是安全设计,避免强制删除留下无人管理的远端资源。
模板查询与工作区脚本策略
POST /sandbox-configs/templates/query用于通过「尚未保存的连接参数」探测远端集群可用的模板:config传未保存的连接,config_id复用已保存配置(可恢复脱敏凭据);ensure_standard=true在集群没有可用 WeKnora 标准模板时才触发构建;replace_standard=true重建标准模板使新的 DNS/镜像规格生效(该选项必须带config_id),实现细节见 sandbox_config.go。
PUT /sandbox-configs/workspace-policy以{"scripts_disabled":true}全局开关本空间脚本执行能力。该开关会体现在GET /sandbox-configs响应顶层的workspace_scripts_disabled字段中;当空间关闭脚本或所需后端能力不可用时,智能体的执行工具不会注册,提示词无法绕过这些条件。
网络策略:出站管控与 L7 规则
network字段同时作用于该配置创建的对话沙箱、技能安装和完整验证三种场景,是管理员控制沙箱网络面的核心配置:
| 字段 | 说明 |
|---|---|
deny_egress_by_default | false 默认允许出站;true 默认拒绝 |
allow_out | IPv4/CIDR/域名或单标签通配域名,域名用于默认拒绝模式 |
deny_out | IPv4/CIDR 拒绝清单 |
cube_rules | Cube 的 name/scheme/sni/host/methods/path/deny/audit/inject 规则,顺序有意义 |
e2b_host_rules | E2B 的 host/headers 规则;host 也须列入allow_out |
allow_public_inbound | 兼容旧输入,保存时清除,入站始终要求凭据 |
cube_rules[].inject为[{header,secret,format}](其中 secret 是加密保存、响应脱敏的凭据),e2b_host_rules[].headers为 header→secret 映射。Docker 后端用docker.network_mode整体控制出网,不支持这些 L7 规则。
{ "deny_egress_by_default": true, "allow_out": ["pypi.org", "files.pythonhosted.org"], "deny_out": [] }源码级校验规则
网络策略并非简单透传,在 sandbox_network_policy.go 中有完整的服务端校验,值得注意的约束包括:
- Docker 后端不能混用:若
sandbox_type=docker却填写了 allow_out/deny_out/cube_rules/e2b_host_rules 任意一项,直接拒绝保存; deny_out只支持 IPv4/CIDR,不支持域名:拒绝判定是目的 IP 的最长前缀匹配,域名无法参与;- 有域名白名单必须同时兜底拒绝其余流量:
allow_out含域名时,必须开启默认拒绝或在deny_out加入0.0.0.0/0,否则未经 DNS 学习的目的 IP 仍会默认放行,白名单形同虚设; - Cube 规则必须有 host 或 sni:只写 method/path 的规则永远到不了 CubeEgress,会被直接拒绝;
audit只能是 none/metadata/full;拒绝规则上配 inject 也会被拒绝; - E2B 规则必须出现在
allow_out:host 规则只做 header 注入、不授权出网,所以 host 必须同时被 allow 列表覆盖(精确名或*.example.com通配后缀匹配);每沙箱最多 10 个规则域名、每规则最多 20 个 header,见 sandbox_network_policy.go。
秘密字段通过CloneWithSecrets统一走加密/解密/脱敏三条路径(sandbox_network_policy.go),保证「哪些字段算秘密」在读写两侧不会漂移。
连通性探测:POST /system/sandbox-check
在把配置保存为生产配置之前,建议先用探测接口验证后端连通性:
curl -X POST "$BASE/api/v1/system/sandbox-check" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"config_id":"cfg-1","deep":true}'请求体为{config,config_id?,deep?}。config_id可用于恢复已保存的脱敏凭据(服务端先读取已存配置再合并传入字段,见 sandbox_check.go);deep=false只做连接检查,deep=true还会执行临时脚本——远端后端会实际创建并销毁一个沙箱,可能产生后端用量,使用需谨慎。
成功执行探测返回{success:true,data:{ok,provider,checks,capabilities}};探测失败也可能返回 HTTP 200,必须检查data.ok;参数错误才返回 400 的 code/msg。每个 check 含name/ok/message/reason/latency_ms,其中ok=null表示跳过。典型例子:默认拒绝出网时外网探针会被策略跳过,ok=null不等于「外网访问验证通过」。
技能目录:空间级技能包管理
技能目录保存的是「技能定义」(安装包),与「某个沙箱上的安装记录」分离:同一技能包可安装到多个沙箱,安装进度和失败原因分别记录。
| 方法 | 路径 | 请求 / 响应 |
|---|---|---|
| GET | /skills/catalog | 200{success,data:[CatalogItem]} |
| POST | /skills/catalog | multipartfile或 JSON{"source":"@owner/slug"};201{success,data:{id,name,version,description}} |
| POST | /skills/catalog/:id/install | {"sandbox_config_ids":["cfg-1","cfg-2"]};202{success,data:{installs,errors?}} |
| GET | /skills/catalog/:id/files | 200{success,data:[FileEntry]} |
| GET | /skills/catalog/:id/files/content?path=SKILL.md | 200{success,data:FileContent} |
| DELETE | /skills/catalog/:id | 无安装引用时删除;200{success:true} |
安装到多沙箱可能部分受理:务必检查data.installs与data.errors,HTTP 202 不表示全部安装完成(skill_catalog.go 中success字段即取决于errors是否为空)。目录删除不会隐式卸载各沙箱——删除被拒的条件是「任何沙箱仍有安装引用」。
curl -X POST "$BASE/api/v1/skills/catalog" \ -H "Authorization: Bearer $TOKEN" -F 'file=@skill.zip' curl -X POST "$BASE/api/v1/skills/catalog/catalog-1/install" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"sandbox_config_ids":["cfg-1"]}'支持的来源写法与包大小限制
来源写法的完整说明见 技能目录与沙箱,要点包括:@owner/slug、@owner/slug@1.2.0、ClawHub slug、GitHub/GitLab 仓库或目录 URL、skills.sh页面、直接 ZIP 或 SKILL.md URL 等;来源必须可匿名读取,下载不会附带用户的私有仓库凭据,私有技能应先导出 ZIP;裸的owner/slug有歧义,应改用@owner/slug或完整 URL。
技能包上限独立于普通文档上传:MAX_SKILL_BUNDLE_SIZE_MB默认 256 MiB,未设置时至少为MAX_FILE_SIZE_MB,最高 512 MiB;GitHub 下载按整个仓库压缩包计算(monorepo 即使技能子目录很小也可能被拒),见 filesize.go。调整后需重启 app 与 frontend,使应用与 Nginx 上限一致。
沙箱内技能:安装、更新、停止与卸载
以下路径前缀是/sandbox-configs/:id/skills,所有操作(含读)都需 Admin+。这是安全设计的必然结果:上传会驱动 root shell,且列表会暴露镜像内容(见 routes_infra.go)。
| 方法 | 后缀 | 行为 |
|---|---|---|
| GET | 空 | {success,data:[SkillResponse]} |
| POST | 空 | ZIP file 或 source JSON 安装;202{success,data:{skill_id}} |
| GET | /:skillId | {success,data:SkillResponse} |
| POST | /:skillId/reinstall | 复用包重装;202{success,data:{skill_id}} |
| POST | /:skillId/stop | 停止安装;200 返回技能状态,已 failed 可重复调用,其他非 installing 状态拒绝 |
| PATCH | /:skillId | enabled?、envs?;省略不修改 |
| DELETE | /:skillId | 从此沙箱卸载,保留目录包 |
| GET | /:skillId/files | 文件清单 |
| GET | /:skillId/files/content?path=SKILL.md | 读取相对路径文件,拒绝路径穿越 |
| GET | /:skillId/install-events | SSE 安装进度 |
| GET | /:skillId/transcript | SSE 安装过程事件 |
SkillResponse 包含id/name/version/description/enabled/status/error/bundle_sha256/installed_snapshot_id/install_session_id/install_message_id/created_at/updated_at,以及envs:[{name,description,required,is_set}]——is_set只回答「是否已设置」而永不回显明文,技能环境变量的秘密只在写入时进入系统、由需要它的沙箱读取(sandbox_skill.go)。文件响应可为 UTF-8 文本、小图片的 base64 或二进制元数据,不能假定所有文件都有文本内容。
PATCH 的部分更新语义
PATCH /:skillId支持enabled与envs二选一或同时提交(sandbox_skill.go):
enabled用指针类型区分「未提及」与「显式 false」,缺省即不修改;envs只更新已声明变量,未声明名称直接忽略;空字符串清除值、保留声明;- 两个字段在一次服务调用内完成,保证「开启技能 + 轮换凭据」的原子性——两次调用可能先持久化开关再失败于凭据写入,造成半完成的凭据轮换。
curl -X PATCH "$BASE/api/v1/sandbox-configs/cfg-1/skills/skill-1" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"enabled":true,"envs":{"API_TOKEN":"<workspace-token>"}}' curl -N "$BASE/api/v1/sandbox-configs/cfg-1/skills/skill-1/install-events" \ -H "Authorization: Bearer $TOKEN"SSE 安装进度与 transcript
进度载荷为{percent,stage,log?,status?,done},done显式携带、客户端无需与服务端保持 stage 名同步。流式语义有几个关键细节(sandbox_skill.go):
- 断开连接不取消安装;
done可能只是「无法继续跟随进度」,最终结果以技能status为准; - 无 Redis 时实时进度不可用,流退化为「单帧声明持久状态 + 轮询状态」;
- 流内每 5 秒重读一次持久状态以兜底「运行结束但没发布任何事件」的边界情况(如重复卸载提前返回);
- 流最长跟随 60 分钟,超时后发送
stage=detached帧结束——detached不是安装判定,只表示「停止跟随仍在进行的运行」; GET /:skillId/transcript以 SSE 回放安装 agent 的完整过程(提示词、思考、执行的命令与输出),帧结构与聊天流相同;204 表示安装刚开始、事件定位尚未准备,404 可表示无日志或日志已过期,不能据此判定安装失败。
注意
GET /skills?sandbox_config_id=cfg-1返回的是「可供智能体使用」的技能元数据{success,data:[{name,description}],skills_available},与管理员看到的完整安装记录(/sandbox-configs/:id/skills)是两套不同视图。
个人环境变量:成员自己的凭据覆盖
个人变量接口是普通成员为「自己的」技能/沙箱执行提供凭据的唯一入口。调用者身份由当前认证上下文确定,不接收 user_id——请求结构体里刻意没有 principal 字段,防止「某天被某段代码顺手尊重」导致任意成员改写他人凭据(me_env_var.go)。个人接口只返回变量名、来源及更新时间,不返回明文。
| 方法 | 路径 | 请求 |
|---|---|---|
| GET | /me/env-vars | 返回按sandbox_config_id分组的配置变量与技能变量 |
| PUT | /me/env-vars/skill | {skill_id,name,value},名称须为技能已声明项 |
| DELETE | /me/env-vars/skill | {skill_id,name} |
| PUT | /me/env-vars/sandbox | {sandbox_config_id,name,value} |
| DELETE | /me/env-vars/sandbox | {sandbox_config_id,name} |
读取响应为{success,data:[{sandbox_config_id,sandbox_config_name,description,vars,skills}]},source标明 user/workspace/unset 等状态。设置/删除成功返回 success;删除未设置项返回 404(me_env_var.go)。个人沙箱变量拒绝WEKNORA_前缀和PATH等保留名;技能声明变量则可以使用技能所需的WEKNORA_*凭据名。
curl -X PUT "$BASE/api/v1/me/env-vars/skill" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"skill_id":"skill-1","name":"API_TOKEN","value":"<my-token>"}' curl -X DELETE "$BASE/api/v1/me/env-vars/skill" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"skill_id":"skill-1","name":"API_TOKEN"}'覆盖顺序与解析实现
变量解析的覆盖顺序为:个人技能值 > 个人沙箱值 > 空间技能值;未设置的名称才向下回退。写入个人值不会更改管理员配置;删除个人覆盖后自动重新使用下层值;关闭技能不会删除个人凭据。
该顺序在 user_env_resolver.go 中由ResolveEnv严格实现:先装入技能声明的管理员空间值(空值视为未设置、不注入),再叠加调用者的沙箱级覆盖,最后叠加技能级覆盖。identity 取自上下文中的 Principal,并固定使用「加载技能行时的 tenantID/configID」作为作用域,避免查询跨入其他空间或配置。解析失败不会降级为管理员值——用别人的密钥运行技能比不运行更糟。
实现参考与延伸阅读
- 接口实现:
internal/handler/sandbox_config.go、sandbox_skill.go、skill_catalog.go、me_env_var.go - 路由与权限:
internal/router/routes_infra.go、routes_agent.go、routes_auth_tenant.go - 网络策略校验:
internal/types/sandbox_network_policy.go - 技能安装服务:
internal/application/service/tenant_skill_install.go - 个人变量解析:
internal/application/service/user_env_resolver.go - 界面操作步骤与后端选型细节见 技能目录与沙箱,其中还包含沙箱中
skill://文件读取、/workspace/input|output目录约定、skill_rollout镜像更新策略等运行时行为说明。
实战建议:用 API 驱动时先以sandbox-check(浅探测)验证连通性,再创建配置;安装技能后通过install-events跟随进度并以技能status为最终判定;为成员开放个人凭据时只用/me/env-vars系列接口,切勿放宽沙箱技能接口的 Admin+ 权限门槛。
【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考