Approval(一次性审批,ctx.approval)

回答一个问题:这个具体动作能否进行。调用方消费封闭结果,非 allowed-once 一律 fail closed。

  • ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';allowed-once 只授予被问的那一次动作。answerer 缺失/抛错/非本 agent/返回不合规 → unavailable(不放行)。
  • ApprovalPolicy = 'ask' | 'never'(per-session):ask 委派组合的 answerer 链;never 确定性 rejected,不派发任何 answerer——严格 headless 姿态(CI/无人值守)。有效值 = session log 最后一条 approval/policy 事件 fallback 到服务配置;never 在服务内部、waterfall 之前强制执行,prepend answerer 也无法绕过。
  • approval/asked + approval/decided 审计事件成对、log-only 不进模型 transcript。
  • ApprovalRequest 刻意不含工具参数(answerer 通过 callId 挂到已流式的工具调用上);request() 要求会话处于 open turn。
  • 应答者来源:UI 通道的人类 answerer;ACP automation bridge 的机器一次性决策。

Permission Presets(ctx.permissionPresets)

把两个独立强制旋钮打包成命名 preset:sandbox mode(sandbox/mode)+ approval policy(approval/policy),客户端呈现为 Permissions 选择器。

  • 默认表:workspace-write = (workspace-write + ask);danger-full-access = (danger-full-access + never)。custom 保留给派生的非 preset 状态。
  • 服务本身不做强制:切换只记录意图(log-only permission/preset 事件),再分别写穿 setSandboxMode / setApprovalPolicy。
  • 组合要求:必须有 confining 的 ctx.shell executor 与 ctx.approval;bash executor 缺 sandboxMode 能力事实时加载即抛错。
  • 另有 per-scope 的 ToolRestriction(对继承的全局工具取交集过滤,被过滤的工具从 prompt 消失且拒绝执行,与不存在无法区分)与 ToolGuard(单调 deny,见 tool-system 管线第 4 阶段)。
  • 模型可通过 sandbox_permissions + justification 请求一次性更宽重试——必须先经 approval 批准该次确切调用才执行。

Sandbox(进程隔离 seam,ctx.sandbox)

SandboxProvider.confine(argv, policy) 返回 ConfinedArgv 或抛 SandboxUnavailableError。静默无隔离 passthrough 永不合法(fail closed)。容器/microVM/远程执行是整条 seam 的兄弟实现(如 e2b),不是 ctx.sandbox provider。

  • 模式(只管文件系统效果,网络与进程可见性不在词表内):read-only | workspace-write | danger-full-access(后者 consumer 直接 spawn,不调 sandbox)。
  • SandboxEnforcement = 'full' | 'partial'(旧 Landlock ABI、Windows ACL 边界属 partial)。
  • 策略按调用解析:ctx.sandboxPolicy.resolve() 管优先级:批准的显式 mode > 会话最后一条 sandbox/mode 事件 > 部署默认。它是部署默认 mode + workspace root 的唯一归属,bash 与 fs 两族执行者都读它(不能被限制到不同 root)。并发会话可各持不同边界,provider 无状态。

后端(dsh-sandbox-local)

平台后端denial 方言
Linuxbwrap / LandlockEROFS(bwrap 只读绑定)/ EACCES(Landlock)
macOSSeatbeltEPERM
WindowsACL restricted-token(报告 partial)—

consumer 只匹配本后端方言,不做跨后端并集。ConfinedArgv 还携带 enforcement 事实与 runnerFailureRules(区分”runner 没跑起来”与”命令被拦”)。

landlock-run(native/)

@deepseek-ai/node-addon-landock-run:一个 self-restrict-then-exec 启动器——约 300 行 C11,直接基于内核 UAPI,musl 静态链接。它在自身安装 Landlock ruleset 然后 exec 被包裹命令;ruleset 跨 execve 继承,命令及其所有子进程均受限,而调用方不受限。内核无法强制时不执行命令直接退出(fail closed)。

  • 发布为预编译平台包(linux-x64/arm64)+ 薄 JS 入口;刻意无安装期编译 fallback——缺平台包则 probe 报 unusable,consumer fail closed。
  • 极小 API:launcherPath()、probe() → full|partial|unusable、grantArgs({readOnly, readWrite})、LAUNCHER_FAILURE_EXIT = 125。支持 kernel 5.13+。
  • 其它平台刻意无包——consumer 在那里跑别的隔离后端。

衔接:sandboxed bash 报告 ShellSandboxInfo { mode, denied, enforcement, runnerFailed };模型收到 denial 事实后可申请一次性更宽重试(需审批)。filesystem 侧 dsh-fs-sandbox 策略拒绝码 FS_SANDBOX_DENIED(区别于内核拒绝 FS_PERMISSION_DENIED)。

Credentials(秘密不进配置)

  • settings 段与 cordis.yml 只存引用(POSIX env 名形式的 branded CredentialRef);provider(如 credentials-local)拥有真实值;consumer 每次操作重新解析(LLM adapter 每次模型请求解析一次)——轮换后的凭据无需重启即生效于下一次请求。
  • seam 铁律:空存储值处处视为缺席,空白永不冒充已配置秘密。
  • API(ctx.credentials):resolve(ref)(禁止跨操作缓存)、describe(ref)(面向配置 UI,绝不暴露值;被只读源遮蔽报 writable: false)、set/unset(被只读源遮蔽时拒绝;set 拒绝空值)。
  • 本地 source 层:env、file、project-env、user-env。credentials/updated 事件供 UI 刷新徽标(进程环境变化不可观测不发事件)。
  • 配套:subprocess seam 提供 scrubbedParentEnv——子进程环境默认洗掉凭据形状变量(*KEY*/*SECRET*/*TOKEN*/*PASSWORD*),显式传入才算刻意保留;DSH_* 管理变量同样丢弃 ambient 值。
  • Web UI 侧:keys are write-only——页面只拿 redacted descriptor,密钥存 $DSH_HOME/.credentials.yaml,settings 里只存 credential reference(见 user-guide)。

相关页面