跳到主要内容

Policy CLI 使用指南

使用 agent-sec-cli 经 asc-daemon 管理带 revision 的 Policy Template 和不可变的 Scope 分配。Scope 保存所选 Policy revision 的完整内容;daemon 发现匹配的进程实例, 自动创建 Binding 并协调下发。Binding 仅支持查询。

这些命令由 V2 CLI 提供,已发布的 Python CLI 尚未包含。Policy、Scope 和 Binding 状态保存在 SQLite 中,daemon 重启后恢复; Scope 受理不代表保护已生效。CLI 和 daemon 需一起升级:Scope update、Scope revision 参数和手动 Binding mutation 已移除。

连接 daemon​

使用部署管理员提供的绝对 socket 路径。以下示例假设 SOCKET 已设置为该路径, 且当前用户已获得策略管理权限。未授权用户会收到 permission_denied;CLI 不负责 启动 daemon 或授予权限。

agent-sec-cli --socket "$SOCKET" policy list

Policy 数据库位置​

未设置非空 AGENT_SEC_DATA_DIR 时,daemon 检查两处 Policy 数据库: A 为 /var/log/agent-sec/policy-state.db,B 为 /var/lib/agent-sec/policy/policy-state.db。A 的目录安全时选择 A, 仅在 A 的祖先不安全且 B 的目录安全时选择 B。这兼容 Ubuntu /var/log 等组可写祖先, 不修改系统目录权限。仅 Policy 存储可回退,安全事件和可观测存储路径保持原状。

创建任何文件前,daemon 检查两处是否已有数据库:

  • 两处都有数据库时拒绝启动,不自动选择或合并。
  • 目录检查的选址会绕过另一处已有数据库时拒绝启动,包括使用 B 后 A 又恢复安全的情况。
  • 其余情况打开所选位置的已有数据库或初始化新库。坏库、未知 schema、锁占用和 I/O 错误 不会触发换库。

祖先检查通过后,daemon 会先把自己拥有的已有叶目录权限修复为 0700,再完成选址。 例如 /var/log/agent-sec 为 0755 时修复并继续使用 A;/var/log 为 0775 时才允许回退 B。 不修改祖先目录权限。叶目录属主错误或权限修复失败均报错,不作为回退理由。

目录诊断包含失败路径、实际 UID/GID/mode 和权限要求;冲突诊断列出两处数据库路径, 要求恢复原目录配置或显式执行离线迁移。不自动移动、删除或重建已有状态。 路径不可读取或仅剩 SQLite sidecar 均报错,不能当成数据库不存在。

在 daemon 环境中显式设置非空 AGENT_SEC_DATA_DIR 时,使用该固定数据目录, 不参与默认 A/B 发现或回退。数据目录必须由 daemon UID 拥有且为 0700, 数据库及辅助文件为 0600。祖先必须归 root 或 daemon UID 所有且不可组写或其他用户写, root 所有的 sticky 目录除外。路径中的目录不能是 symlink。

关联本地日志​

V2 使用原生 OpenTelemetry 关联本地请求日志。--trace-context 保留原有扁平 Agent metadata 输入,应放在命令名及其它选项的非选项值之前。可选的 --otel-context 接收 version 1 JSON carrier,包含 traceparent、tracestate、baggage 可选字段。 两者同时提供时,显式扁平 Agent 字段优先。

RUST_LOG=info agent-sec-cli --trace-context '{"session_id":"session-123","agent_name":"openclaw"}' \
--otel-context '{"version":1,"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}' \
--socket "$SOCKET" policy list

RUST_LOG=info 开启 stderr 上的有界 JSON 关联诊断;daemon 需要单独设置其进程环境。 默认 warn 不产生这些记录。背压下允许丢诊断;命令结果、错误及退出码保持原有语义。 当前不提供公开 OTLP exporter 或导出/采样/batch 配置;OTEL_* 设置不能开启导出或 改变固定的本地采样策略。--otel-context 是入站上下文,不是导出目标。 CLI 要求支持 carrier 的新 daemon,应先升级 daemon。

管理 Policy​

准备 JSON 模板文件,例如 policy.json:

{
"specVersion": "0.1",
"rules": [
{
"effect": "block",
"category": "file",
"action": "write",
"target": {"type": "file", "path": "/workspace/important/**"},
"where": {"operation": {"eq": "delete"}},
"because": "Protect important files from deletion"
}
]
}

PolicyTemplate 是可被多个 Scope 按 ID/revision 选用的可复用策略。rules 表达动作、 带类型的目标、决策、条件和可选理由。当前资源格式为 {"type":"file","path":"..."}, 每条规则一个路径。合法的文件 read/write/exec 规则、逻辑条件、历史条件以及 block/allow/require_confirmation 决策均可保存;Network 和 AgentHook 的目标格式尚未定义, 目前不能保存。

AgentSight Adapter 当前只执行不带历史条件的 block + file/write + operation=delete 规则。 任意规则不支持,整个 Binding 失败,例如返回有界错误码 RULE_1_UNSUPPORTED_EFFECT, 不发送受支持的规则子集。目标 DSL 不支持转义语法,because 包含引号、反斜杠或控制字符时 转换失败,普通 Unicode 文本保持原样;省略理由时使用 Adapter 默认值。 创建策略成功不代表执行后端支持这些规则。

生成的 DSL 表达禁止删除;真实内核是否仅阻止删除仍需单独验证,当前 ActPlane 的 unlink/write 共用底层操作映射。旧 kind JSON 和包含该格式的开发期数据库不提供迁移, 也不自动重建;已有部署应先用兼容的旧二进制完成清理,再安排新库。

agent-sec-cli --socket "$SOCKET" policy create --name "protect files" --file policy.json
agent-sec-cli --socket "$SOCKET" policy get --policy-id "$POLICY_ID" --revision 1
agent-sec-cli --socket "$SOCKET" policy list --limit 100 --offset 0
agent-sec-cli --socket "$SOCKET" policy update --policy-id "$POLICY_ID" --name "protect files v2" --file policy-v2.json
agent-sec-cli --socket "$SOCKET" policy delete --policy-id "$POLICY_ID" --revision 2

ID 和 revision 使用成功命令返回的值。这些是参考示例,并非顺序执行脚本;应在目标 Policy revision 仍为 current 时创建 Scope。create/update 均要求名称和完整模板, 不是部分更新。名称或模板变化才增加 revision,相同内容不增版。只保留 current 完整 记录;get/delete 要求精确的当前 revision,旧版本返回 not_found。文件相对路径按 CLI 工作目录解析。

更新或删除 Policy 不改变已有 Scope 和 Binding。撤销分配应删除 Scope。已删除的已知 Policy ID 保留 revision 计数器;update 可按下一 revision 重建内容,未知 ID 不可 update。

管理 Scope​

创建时提供 Policy ID、精确 revision 和一个 selector:--process-name 精确匹配 Linux 进程名(1–15 bytes),--executable 精确匹配规范化绝对可执行路径,或 --pid 选择 一个进程实例。名称/路径持续发现已有和后续进程;PID 固定第一次观察到的实例,不跟随 PID 复用。目前拒绝 cgroup assignment。

agent-sec-cli --socket "$SOCKET" scope create --process-name openclaw --policy-id "$POLICY_ID" --policy-revision 1
agent-sec-cli --socket "$SOCKET" scope get --scope-id "$SCOPE_ID"
agent-sec-cli --socket "$SOCKET" scope list --limit 100 --offset 0
agent-sec-cli --socket "$SOCKET" scope delete --scope-id "$SCOPE_ID"
agent-sec-cli --socket "$SOCKET" scope retry --scope-id "$SCOPE_ID"

Scope 没有 revision,不可 update;policySnapshots 保存所选 Policy 的完整内容。 若并发 Policy 更新/删除先于 Scope 准入提交,创建返回 not_found,不会静默替换版本。 准入成功后,后续匹配的新实例也使用该快照。CLI 每次分配一个 Policy,RPC 支持每个 Scope 1–32 个不同 Policy。

替换分配时,先创建新 Scope,查询所需实例的 Binding 达到 READY 后再删除旧 Scope; 这不是原子切换。

删除先关闭发现准入并停止 worker,再异步清理所属部署。未完成时返回 {"scopeId":"...","completed":false},完成后返回 completed:true。 清理期间 Scope 保留 DELETING 状态,失败 Binding 仍可查询。重复 delete 不重置重试 预算;排除故障后使用 scope retry 重试终态失败,不修改分配或重启正在运行的工作。 已完成的删除及未知 Scope ID 的删除均返回 completed:true,daemon 重启后也可重复。 其它 Scope 和来源 Policy 不受影响。

查询 Binding​

daemon 为每个 Scope、Policy 和具体进程实例创建一条 Binding,包含策略快照、Scope ID 和 selector、固定的进程身份及当前状态。通过 spec.scope.scopeId 识别所属分配。

agent-sec-cli --socket "$SOCKET" binding list --limit 100 --offset 0
agent-sec-cli --socket "$SOCKET" binding get --binding-id "$BINDING_ID"

应用从 PENDING_APPLY 经 APPLYING 到 READY。永久或预算耗尽的失败显示为 APPLY_FAILED/DELETE_FAILED,原因在 status.error;查询命令仍成功退出。 进程退出或 selector 失配与 Scope 删除使用同一清理路径。部署结果不确定时,责任保留到 确认目标不存在。自动重试有界,scope retry 重试所属失败工作。目前没有 --wait。

公共参数​

参数用途
--socket PATH必填的 daemon 绝对 socket 路径,可放在子命令前后
--timeout-ms N正 32 位无符号整数,默认 5000;连接、发送、接收共用一次时间预算
--limit N列表每页条数,1..=1000,默认 100
--offset N列表偏移量,32 位无符号整数,默认 0
--help顶层、命令组和具体操作的帮助,不连接 daemon
--version顶层版本信息,不连接 daemon

支持 --key value 和 --key=value。带空格的名称、路径需使用引号; 以 -- 开头的值使用 --key=--value。重复参数会被拒绝。 列表命令每次返回一页 {items,total},total 是分页前总数。后续页需显式请求, offset 按实际返回条数推进;达到 3 MiB 编码条目预算时可能提前结束一页。

输出与错误​

结果输出退出码
成功stdout 输出格式化的结果 JSON,stderr 为空0
daemon 拒绝stderr 输出 JSON {requestId,error:{code,message}},stdout 为空1
文件、连接、响应或输出失败stderr 输出错误说明1
命令行参数错误stderr 输出用法错误2

模板文件最大为 4 MiB,必须是有效 JSON,不能包含未知或重复字段。 完整编码后的请求和响应各自另有 4 MiB 上限,包含行结束符。文件大小检查通过不保证 组装后的请求符合上限;请求超限时不会发送。daemon 另在保存前拒绝编码后超过 1 MiB 的 Policy 或完整 Scope。

超时预算覆盖 daemon 通信,包括等待 daemon 处理请求;不包含文件读取、请求编码、 响应解码和结果输出。CLI 不自动重试。发送后超时、响应缺失或非法,不代表请求没有 执行;再次提交变更前应先查询当前状态。