SkillSec V2
SkillSec 扫描 Skill 内容、签名扫描结果、保留可恢复的版本,并向 SkillFS 发布选定版本。
内置扫描全部在本地执行,不调用模型。Rust CLI 保留 agent-sec-cli skill-ledger 入口,
由一个 root daemon 执行业务操作。
本文说明 V2 核心能力。Skill Ledger 指南说明 V1 及其 Agent 集成。 V2 Agent Hook 迁移单独交付;本次核心迁移不更新现有 Hook 的初始化检查、默认策略或启用状态。
安装边界
正式发布组件的常规入口为 sudo anolisa --install-mode system install sec-core,Alinux 用户还可使用
安装指南中的 RPM 方式。这些入口选择已发布产物,不会选择尚未发布的 V2
迁移分支。安装正式版本不代表已获得 SkillSec V2。
V2 RPM 需要在 Linux 上按仓库配方从本分支构建。完整配方还会构建保持现状的 sandbox 和插件包, 需要 Node.js 22.14 或更新版本、Rust 1.93 或更新版本、RPM 工具、systemd RPM 宏、C 编译器、 pkg-config 和 OpenSSL 开发头文件:
./scripts/rpm-build.sh agent-sec-core-v2
sudo yum install ./scripts/rpmbuild/RPMS/x86_64/agent-sec-cli-*.rpm
aarch64 使用对应架构目录。本阶段安装核心 CLI 包;元包还会拉入本次尚未迁移的 Agent 集成。 V1 和 V2 CLI 使用相同包名和可执行文件路径,安装 V2 会替换 V1。先停止 V1 写入者并保留匹配备份, 具体见下文部署回退。
仅从源码安装核心时,使用 Linux 和 Rust 1.93 或更新版本,在仓库根目录执行:
make -C src/agent-sec-core build-cli-v2
sudo make -C src/agent-sec-core install-core-v2
产物为 src/agent-sec-core/target/v2/bin/agent-sec-cli 和 agent-sec-daemon。
核心二进制不依赖 Python Ledger 运行时。install-core-v2 将两个二进制安装到 /usr/bin,
系统 unit 安装到 /usr/lib/systemd/system,初始配置以 0600 安装到 /etc/agent-sec/skillsec.json。
重装保留已有配置;RPM 使用 %config(noreplace)。安装过程不会初始化签名密钥或启用 Agent Hook。
RPM 遵循发行版的 systemd preset,可能启用该服务的开机启动。检查配置后,显式启动 root 系统服务:
sudo systemctl daemon-reload
sudo systemctl enable --now agent-sec-core.service
sudo systemctl status agent-sec-core.service
agent-sec-cli skill-ledger status
使用系统级 systemctl,不带 --user。unit 创建运行、状态和日志目录,保持密钥和日志私有,
只向 daemon 授予 CAP_DAC_OVERRIDE、CAP_CHOWN 和 CAP_FOWNER。
保留 NoNewPrivileges、SystemCallFilter=@system-service、native syscall 架构、
MemoryDenyWriteExecute 和内核保护,同时保留 HOME、/tmp、系统 Skill 目录及共享挂载的访问能力,
以完成扫描、元数据发布和回滚。daemon 不具有 CAP_SYS_ADMIN。自定义 SkillFS 挂载须对服务可见。
评估时使用隔离部署;V1 和 V2 不得同时写同一份 Skill 元数据。 实现状态与实际 Linux 交付验收分别记录在迁移文档。
V2_CARGO_TARGET_DIR 指定共享 Cargo 构建缓存,V2_BIN_BUILD_DIR 指定二进制暂存目录。
构建和安装时传入相同的暂存目录设置。已有 Linux 构建可用以下命令检查暂存安装布局及配置保留:
bash src/agent-sec-core/tests/packaging/test-skillsec-install.sh
该暂存检查不会启动 systemd,也不代表真实安装验收通过。
以前台进程运行开发 daemon 时,先创建 root 所有、模式 0755 的运行目录,以及 root 所有、模式
0700 的空状态目录。通过 agent-sec-daemon serve --socket /run/agent-sec-core/daemon.sock
指定绝对 socket 路径,可用 --skillsec-config /etc/agent-sec/skillsec.json 指定配置。
进程必须以 root 运行。运行目录必须预先存在;私有状态目录不存在时由 daemon 创建。
原位 SkillFS backing 挂载要求内核支持 open_tree、move_mount,并可访问 /proc/self/fd。
操作不支持或被拒绝时直接失败;daemon 无需增加挂载 capability。
系统配置与信任
daemon 读取 root 所有的 /etc/agent-sec/skillsec.json;--skillsec-config 可指定其他绝对路径。
父目录必须可信,配置文件不得允许组或其他用户写入,也不得是符号链接或有多个硬链接。
默认文件缺失时使用以下默认值;格式错误或权限不安全会导致启动失败。
{
"stateDir": "/var/lib/agent-sec/skillsec",
"managedSkillDirs": []
}
managedSkillDirs 定义普通目录的授权范围,保留 V1 的三种绝对路径形式:
| 配置项 | 授权及发现的 Skill |
|---|---|
/srv/skills/demo | 该精确 Skill |
/srv/skills/* | 直接、非隐藏 Skill 子目录 |
/srv/skills/** | 任意深度的 Skill;若 /srv/skills 自身有 SKILL.md,也包括自身 |
通配模式跳过隐藏后代,不跟随符号链接。可以显式指定 /home/alice/.hermes/skills/* 这样的宿主目录。
不展开其他 glob 语法或 ~,系统配置使用绝对路径。空列表不允许访问任何普通 Skill 目录。
SkillFS 认证挂载继续通过已有的 root 所有挂载配置授权。
root 使用 sudoedit /etc/agent-sec/skillsec.json 编辑现有配置,保留 stateDir、scanner 和
SkillFS 设置。例如,将目录配置设为:
"managedSkillDirs": ["/srv/skills/*", "/home/alice/.hermes/skills/**"]
执行 sudo systemctl restart agent-sec-core.service 应用配置修改。已有通配范围下新增 Skill
无需改配置或重启。随后普通用户均可扫描、认证、查询和修改范围内的 Skill,不增加用户 ACL。
范围外的路径返回 PermissionDenied,包括 analyze,也不能通过扫描自动扩大登记范围。
init、scan --all 和 check --all 合并配置模式发现、仍在授权范围内的登记目录及调用端发现。
调用端传入的路径在 Skill I/O 前校验;批量请求只要含越界路径,整个请求就会在处理任一 Skill 前被拒绝。
可以改用已授权 Skill 的显式路径,或由 root 增加所需父目录模式。登记仅保存精确路径历史,不能扩大配置范围。
status 查询配置模式及仍受授权的历史目录,不依赖调用者 HOME。移除模式不会删除该 Skill 已有元数据;
启动恢复也会拒绝对当前范围之外的目录执行业务操作。
daemon 启动后在后台扫描配置中的普通 Skill 及仍受授权的历史目录,包括尚未登记的 Skill。 同一个 worker 处理已登记的 SkillFS Skill 和通知,不通过遍历 FUSE 发现挂载内的 Skill。 socket 开放不等待扫描完成;内容及扫描结果齐备且未变化时保持 noop。
scan --all 和 init 基线处理中,如果已授权 Skill 是 /usr/share/anolisa/skills 或
/usr/local/share/anolisa/skills 的直接子目录,且 daemon 自身也无法写入其普通宿主账本位置
(包括只读文件系统),则返回 status: skipped、reasonCode: readonly_system_skill、
persisted: false,不代表已认证为安全。显式扫描、SkillFS 失败、越界路径及其他存储错误仍报错。
默认发现跳过隐藏子目录,包括账本快照;.hermes/skills 等宿主目录下的正常 Skill 仍受支持。
发现范围包括 $XDG_DATA_HOME/anolisa/skills 下的直接 Skill 子目录,环境变量来自 CLI 调用者。
XDG_DATA_HOME 未设置、为空、为相对路径或含 ./.. 路径段时,使用
$HOME/.local/share/anolisa/skills,与 ANOLISA 用户安装目录规则一致。HOME 未设置或为空时,
使用 CLI 当前用户的系统账户主目录。合法的自定义目录不存在时
直接跳过,不再回退。显式 Skill 路径及 init --no-baseline 不触发自动发现。
状态目录归 root 所有,模式为 0700;signing-key.pk8 为 0600。第一阶段允许所有本地用户操作所有
受管 Skill,文件归属不作为用户访问 ACL。仅内核 UID 0 可以换钥。系统密钥签名的是 daemon 计算的记录,
不是调用者提交的任意字节。
不导入 V1 历史、加密密钥或 keyring。新 manifest 绑定 Skill 的规范绝对路径身份。 移动到另一个身份后,需要重新建立信任;不同位置的同名 Skill 具有独立身份。
核心流程
以下命令由管理该 Skill 的普通用户执行。init --no-baseline 仅初始化共享密钥。
scan 也会初始化缺失的密钥;两者均不会静默替换损坏的密钥。
agent-sec-cli skill-ledger init --no-baseline
agent-sec-cli skill-ledger list-scanners
agent-sec-cli skill-ledger analyze /path/to/skill
agent-sec-cli skill-ledger scan /path/to/skill
agent-sec-cli skill-ledger check /path/to/skill
agent-sec-cli skill-ledger show /path/to/skill
agent-sec-cli skill-ledger audit /path/to/skill --verify-snapshots
agent-sec-cli skill-ledger status --verbose
analyze 要求路径在授权范围内,但仍只读,不初始化密钥,也不写账本。默认内置扫描器为 code-scanner 和 static-scanner。
可用 scan --scanners code-scanner,static-scanner 显式选择。内容变化时创建新版本;
内容不变时补齐缺少的扫描结果。scan --force 可替换相同内容的扫描结果,不凭空增加内容版本。
完整性状态共有六种:
| 状态 | 含义 |
|---|---|
none | 内容尚无通过认证的扫描判定 |
pass | 通过认证的内容具有通过扫描结果 |
warn | 通过认证的内容具有警告发现项 |
deny | 通过认证的内容具有拒绝发现项 |
drifted | 当前内容与通过认证的记录不一致 |
tampered | 账本元数据、签名或版本绑定未通过认证 |
执行错误和 unmanaged 诊断不属于额外的完整性状态。扫描成功表示扫描完成,不表示安全判定一定为 pass。
首次初始化密钥前,检查没有账本的 Skill 仍返回 none、退出 0,审计其空历史也成功。
两种查询均不创建密钥或元数据。已有账本但密钥缺失或无效时,不会按空历史处理。
外部结果、人工决策与恢复
通过 CLI 导入外部 JSON findings 报告。自定义 skill、cli 或 api 扫描器仅用于导入结果;
root daemon 不会执行其配置的命令。
agent-sec-cli skill-ledger certify /path/to/skill --findings /path/to/findings.json --scanner skill-vetter
agent-sec-cli skill-ledger decide /path/to/skill --action allow --reason 'Reviewed this version'
agent-sec-cli skill-ledger decide /path/to/skill --action always_allow
agent-sec-cli skill-ledger decide /path/to/skill --action block
agent-sec-cli skill-ledger decide /path/to/skill --action rollback --version v000001
agent-sec-cli skill-ledger decide /path/to/skill --clear
agent-sec-cli skill-ledger export /path/to/skill --version v000001 --output /path/to/empty-export
agent-sec-cli skill-ledger activate /path/to/skill
allow 批准选定版本;always_allow 保存持续的人工批准。block 隐藏暴露内容。
rollback 恢复选定的已认证快照并记录决策;--clear 清除人工决策。
回滚后的文件归 Skill 根目录所有者所有,普通用户仍可继续编辑;特权文件模式位会被清除。
导出向调用者所有的空目录写入 snapshot/、manifest.json 和 findings.json。
缺少的导出目录由 CLI 以调用者身份按 0700 创建;已有目录权限保持不变,非空目标目录会被拒绝。
--delete-findings 仅在认证成功且确认本地报告未变化后,以 CLI 调用者权限删除该报告。
成功删除返回 findingsDeleted: true;清理失败返回 findingsDeleted: false 和
findingsDeleteError,保留已提交的认证及其成功退出码。daemon 失败或响应格式错误时不会删除输入。
show 展示 active、pending 或 hidden 选择。在 pass_warn_only 下,符合条件的已认证
pass 或 warn 版本可以自动暴露;最新内容不安全时可保留符合条件的旧版本,或呈现待审视图。
人工决策参与版本选择。activation.activationPending=true 表示账本已提交,但发布尚未完成。
检查 show,修复所报告的原因后用 activate 重试。daemon 重启会恢复支持的中断发布和回滚状态,
不承诺持久化通知队列。
启动恢复覆盖已登记 Skill。首次扫描若在版本写入后报告登记失败,应修复失败原因并对该 Skill
重试 scan;内容未变化时复用已验证版本,完成登记后再发布激活。首次请求失败时,不保证重启后
自动发现该目录。
SkillFS 绑定
SkillFS 与普通 CLI 共用公共 socket。notify 握手和消息保留现有 HMAC 合同,普通 V2 JSON 仍严格解析。 公共 socket 必须归 root 所有、模式为 0666,直接父目录归 root 所有、模式为 0755。 SkillFS 还会检查祖先目录安全性与真实内核对端 UID。权限调整不允许明文降级或绕过 HMAC。
daemon 显式配置规范路径与 backing 路径的映射:
{
"stateDir": "/var/lib/agent-sec/skillsec",
"managedSkillDirs": ["/home/alice/.openclaw/skills/demo"],
"skillfs": {
"authKeyFile": "/etc/agent-sec/skillfs-hmac.key",
"mounts": [{
"controlSocket": "/run/user/1000/skillfs/control.sock",
"canonicalRoot": "/home/alice/.openclaw/skills",
"liveRoot": "/home/alice/.openclaw/live-skills",
"peerUid": 1000
}]
}
}
使用独立的随机 HMAC 密钥,长度 32–4096 字节,不得使用签名密钥。
daemon 的副本必须归 root 所有、模式 0600;向 SkillFS 用户提供内容相同、归其所有的独立 0600 文件,
不要将 daemon 的私有文件改为所有人可读。以上 UID 1000 示例中,SkillFS notify 的 socket_path
设为 /run/agent-sec-core/daemon.sock,auth_key_file 指向其用户私有副本。
control socket 仍归 UID 1000 所有,父目录 0700、socket 0600;control 的
trusted_peer_uid=0,trusted_peer_key_file 指向同一用户私有副本。必须使用 HMAC 认证,
不设置 trusted_peer_exe,因为可执行文件认证是另一种互斥的认证模式。
按 SkillFS 运行时参考
配置 activation 和 backing root;其中旧示例的 daemon 端点和对端身份应替换为上述 V2 配置。
两个进程必须能看到配置中的绝对路径及相同 backing 文件系统对象,因此容器部署需要匹配的共享卷路径。 同一普通非原地挂载的 canonical/live 根可以完全相等,否则必须互不包含。 规范根目录和 live 根目录不得与其他映射交叠。resolver 失败时不会回退为扫描 FUSE 视图; 目录 inode 不匹配时会拒绝操作,不会给替换后的内容签发记录。
通知按 Skill 合并,然后依次触发扫描与激活。notify 确认仅表示已接受入队。
启用 SkillFS 时,status 包含 skillfs 的 queue/running/processed/failed 计数。
通过计数、公共审计和真实 FUSE 读取,分别确认受理、处理完成和可见发布;启动时会重新调度已注册的挂载 Skill。
输出、审计与限制
CLI 向 stdout 输出业务 JSON。check 遇到 deny、tampered 或执行错误时返回 1;
已完成的 scan/certify 和完整 analyze 即使发现风险也返回 0。
analyze 覆盖不足返回 1,输入无效返回 2。传输失败写入 stderr 并返回 1。
必须同时检查 verdict、activation 字段和进程退出码。
用 --socket 或 AGENT_SEC_DAEMON_SOCKET 指定端点。SkillSec 默认超时 60 秒;
--timeout-ms 在服务端最大为 120 秒。超时不能证明写入是否已提交,重试修改前先查询状态和历史。
响应过大时返回 ResponseTooLarge 和 operationMayHaveCommitted=true,不会截断业务数据。
公共审计使用共享 Action Runtime 和私有 /var/log/agent-sec 事件存储。
投影包含操作、计数、判定、版本和错误类型,排除源代码、原始 findings、本地路径、人工原因和密钥。
CLI 响应保留完整业务数据。内置扫描限制为 2,000 个文件、总计 50 MiB、深度 32;
findings 导入限制为 2 MiB。覆盖失败会明确呈现,不会为部分内容签发完整扫描结论。
换钥与部署回退
sudo agent-sec-cli skill-ledger rotate-keys
agent-sec-cli skill-ledger scan --all
换钥先撤回已注册 Skill 的暴露,再替换密钥。此前签名随之失去信任,包括此前的 V2 签名。 重新扫描并激活以建立信任,不提供旧公钥验签回退。撤回失败时保留旧密钥,普通账本写入被阻止, 直到 root 重试或启动恢复完成。
替换已有部署前,停止旧 daemon 和 SkillFS 写入者,保留匹配的二进制、配置,并备份私有密钥和状态,
以及每个 Skill 的内容和 .skill-meta。使用副本开展迁移评估。
回退 V1 时先停止 V2,恢复对应的 V1 文件、密钥、配置和元数据,再启动 V1/SkillFS。
不要让 V1 读取 V2 manifest,也不要混用旧元数据与已修改的源内容。
仅保留二进制不等于可恢复状态,同时启动两个 daemon 也不是升级方式。