Perlica v1:单目标群任务助手可实施规格 #1

Open
opened 2026-10-15 18:43:06 +08:00 by llm · 0 comments
Collaborator

Perlica v1:单目标群任务助手可实施规格

版本:1;整理日期:2026-10-15;追踪 Issue:#1。

Problem Statement

Perlica 需要在受控 Kubernetes 集群中为一个可信 QQ 群执行开发与运维任务。当前资料已经确定了目标群边界、消息保护、单活动任务、持久根文件系统沙箱、策略网关、审批和审计等决定,但还缺少一份按实现顺序组织、可由工程任务直接执行并可验收的规格。

第一版的核心风险是边界被隐式扩大:群外消息进入模型、凭据样内容进入持久层或出站服务、审批被自然语言复用、沙箱只持久化工作区、Kubernetes MCP 绕过策略网关,或 rootfs 的未验证实现被当成已完成能力。本规格将这些边界转换为模块契约、状态、验收条件和测试证据。

Solution

实现一个 TypeScript/Node.js 单实例服务,运行于 Kubernetes,并通过 NapCatQQ 连接一个配置指定的目标 QQ 群。消息入口先执行群边界、去重和内容保护,再写入 SQLite;只有明确 @Perlica 的目标群消息创建 Agent 任务。任务协调器维护一个持久状态机和唯一活动任务,使用 Pi Agent SDK 运行受限 Session,只注册 Perlica 的结构化工具。

所有工具调用都经过 Perlica 工具网关和执行代理。执行代理把动作规范化为动作计划,按 allow/ask/block 判定,执行或发出一次性审批请求,并记录实际参数、身份、目标和保护后的结果。沙箱、Kubernetes、Tavily、Web Fetch、Spectra、附件和记忆组件都位于该边界之后。

沙箱由可替换的 SandboxBackend 提供,最多三个,创建时继承唯一的全局沙箱镜像配置。沙箱目标语义是可恢复的整个根文件系统;节点绑定、持久 upper/work、重挂载、崩溃恢复和清理必须先通过真实 Linux 节点 rootfs 实测闸门。闸门未通过时不声称沙箱能力完成,也不把只持久化工作区作为等价实现。

第一版只读 Web 控制台复用任务、审批、沙箱和审计查询,不直接执行动作。QQ 只发送普通文本;长 Markdown 只有在模型明确调用 spectra_upload 且内容完成保护后才上传,QQ 最终收到普通文本短链接。

User Stories

  1. 作为目标群成员,我想在目标群中明确 @Perlica 发起任务,以便只有我明确请求时才消耗 Agent 资源。
  2. 作为非目标群成员,我希望我的消息不进入 Perlica 的数据库、模型或业务日志,以便群边界可验证。
  3. 作为目标群成员,我希望普通群消息在保存前自动替换凭据样内容,以便历史和任务资料不保存秘密原文。
  4. 作为系统维护者,我希望保护扫描失败时保存缺失状态而不是原文,以便失败关闭而不是绕过安全边界。
  5. 作为目标群成员,我希望任务只带触发前 15 分钟、最多 30 条消息和最多 24,000 个文本字符,以便 Session 不被无关聊天淹没。
  6. 作为目标群成员,我希望能通过受限历史检索寻找更早消息,以便有限任务窗口仍能处理较长背景。
  7. 作为任务发起者,我希望同一时间只有一个活动任务,以便任务之间不会争用沙箱、混淆 Session 或交叉使用审批。
  8. 作为后来发起任务的成员,我希望收到明确的忙碌提示,以便知道请求未被悄悄丢弃且可以稍后重试。
  9. 作为系统维护者,我希望进程重启后能从任务状态继续,以便 QQ 重连或服务恢复不会重复执行已完成的动作。
  10. 作为模型运行时,我希望只看到当前任务、受限历史和已保护工具资料,以便控制命令、其他任务和未保护值不会进入上下文。
  11. 作为系统维护者,我希望 Pi 默认 shell、read、write、edit 工具关闭,以便所有执行都能经过统一策略和审计。
  12. 作为任务发起者,我希望模型可以在沙箱中执行受限命令和读写文件,以便完成构建、测试和开发工作。
  13. 作为任务发起者,我希望沙箱内工具的输出受到时间、资源和输出大小限制,以便单个任务不会耗尽服务资源。
  14. 作为 Owner,我希望创建沙箱时可以指定不可迁移的节点和用途描述,以便工作负载落在已知节点并可回看用途。
  15. 作为任务发起者,我希望创建沙箱时能看到全局镜像介绍、digest 和 revision,以便无需 probe 就能选择适合的工具链。
  16. 作为 Owner,我希望所有沙箱共享唯一的全局镜像配置,以便系统不会同时运行无法解释的基础环境。
  17. 作为 Owner,我希望只修改镜像介绍时不销毁沙箱,以便更新环境说明不造成破坏性操作。
  18. 作为 Owner,我希望更换实际镜像时系统自动暂停创建并清理所有旧沙箱,以便新旧镜像不会共存。
  19. 作为 Owner,我希望镜像清理失败时切换保持阻塞,以便系统不会在资源状态不明时启用新镜像。
  20. 作为任务发起者,我希望七天没有实际触碰的沙箱自动删除,以便持久资源不会无限积累。
  21. 作为系统维护者,我希望节点故障的沙箱被标记为已删除且不自动迁移,以便节点绑定语义和数据风险保持明确。
  22. 作为 Owner,我希望能够更新沙箱用途描述但不能重命名沙箱,以便名称稳定而说明可以持续维护。
  23. 作为 Owner,我希望能查看沙箱操作、状态、最后活跃触碰和清理结果,以便处理孤儿资源和过期工作空间。
  24. 作为系统维护者,我希望低风险动作自动执行并审计,以便正常开发任务无需重复确认。
  25. 作为系统维护者,我希望已有明确范围的中风险动作按策略自动执行并告知,以便允许域名 GET、依赖安装、只读集群查询等工作保持可用。
  26. 作为 Owner,我希望高风险动作逐项审批,以便集群写入、删除、上传、扩展安装和凭据相关动作不会由模型自行决定。
  27. 作为系统维护者,我希望影响范围未知的动作直接拒绝,以便“询问确认”不会掩盖无法识别的权限边界。
  28. 作为 Owner,我希望审批票据绑定目标群、任务、动作计划哈希和过期时间,以便授权不能被改参数或换任务复用。
  29. 作为 Owner,我希望 .approve 和 .deny 在模型上下文之外解析,以便审批消息不会成为可被模型解释的自然语言资料。
  30. 作为系统维护者,我希望审批票据最多消费一次且过期后不可用,以便重放和超时重试不会重复执行高风险动作。
  31. 作为审计人员,我希望看到规范化计划、实际参数、执行身份、目标、审批票据和结果,以便事后判断系统实际做了什么。
  32. 作为系统维护者,我希望历史、网页、日志和工具结果带有资料标记,以便模型不会把不可信资料当成授权指令。
  33. 作为任务发起者,我希望使用 Tavily 搜索和 Web Fetch,以便在不暴露整个 Session 的情况下获取外部资料。
  34. 作为任务发起者,我希望只在明确请求时获取附件,以便图片和文件不会被默认下载或未经保护进入模型。
  35. 作为系统维护者,我希望不支持的附件类型只返回元数据或拒绝,以便媒体保护能力不足时不会假装安全完成。
  36. 作为 Owner,我希望安装 MCP 或 Skill 时记录来源、版本和完整性信息,以便扩展供应链可回看且不能自行改变策略。
  37. 作为系统维护者,我希望长期记忆只收到筛选并保护后的事实,以便记忆组件不会成为原始消息或审批的旁路存储。
  38. 作为系统维护者,我希望 pi-memory 通过 MemoryAdapter 接入并可替换,以便其 Pi 生命周期耦合不会破坏任务边界。
  39. 作为 Kubernetes 运维者,我希望只读 Kubernetes MCP 使用独立身份、窄 RBAC、工具白名单和禁用 Secret,以便观测能力不等于集群管理权限。
  40. 作为系统维护者,我希望 Kubernetes MCP 不能直接连接 Pi,以便 MCP 的工具过滤或确认提示不会替代 Perlica 策略网关。
  41. 作为 Owner,我希望第一版只开放一个经过测试的集群写动作,以便写能力从明确证据开始逐步扩大。
  42. 作为目标群成员,我希望收到普通 QQ 文本回复,以便 NapCat 真人账号不会依赖富文本渲染。
  43. 作为任务发起者,我希望长 Markdown 可以通过 Spectra 变为短链接,以便 QQ 回复保持简洁且能访问完整结果。
  44. 作为控制台使用者,我希望看到任务状态、Session 摘要、审批、沙箱和脱敏审计,以便在不获得执行权限的情况下观察运行情况。
  45. 作为系统维护者,我希望 QQ 发送失败只重试 Outbox 消息而不重建任务,以便外部传输故障不会重复执行。
  46. 作为发布负责人,我希望每个主要安全声明都有自动化测试和必要的真实环境证据,以便“通过”有明确含义。

Implementation Decisions

边界与角色

  • 配置一个 targetGroupId;入口第一步比较群 ID。非目标群事件在内存丢弃,不写业务日志、数据库或模型。
  • 成员与 Owner 均绑定目标群 ID 和 QQ 数字 ID,不使用昵称或 QQ 群管理员身份。第一版两名成员都登记为 Owner。
  • 只有白名单成员的明确提及可以触发任务;其他身份的消息不授予任务或管理权限。控制命令必须先校验角色,未知命令不回退到模型执行。目标群内普通聊天仍按统一内容保护规则处理。
  • 点号开头的控制命令由 dot-command-router 解析,不进入 Agent Session。第一版至少支持 .approve <approval-id> <short-code>、.deny <approval-id>,以及 Owner 的沙箱、镜像介绍和扩展管理命令。
  • NapCat 事件只在 NapCatAdapter 内转换为 IncomingGroupMessage;领域模块不依赖 OneBot/QQ 事件类型。
  • 目标群消息按 QQ message ID 去重;Outbox 发送按幂等键去重。

内容保护与数据流

  • 统一 ContentProtection 出站入口执行结构化抽取、凭据/私钥/Token/kubeconfig 模式检测、不可逆替换、最终序列化和再次扫描。
  • 返回值包含 content、status(redacted、scanned_no_findings、scan_incomplete、not_scanned)、policyVersion 和 findingCount。scan_incomplete 不得作为安全通过。
  • 第一版规则覆盖常见私钥、JWT、云 Token、Authorization/Cookie、kubeconfig、ServiceAccount 和高熵凭据样式。检测不是有效性验证,也不宣称零漏报。
  • 数据库、模型、长期记忆、Tavily、Fetch、Spectra、审计和错误日志只接收保护后的值。工具资料进入 Session 时带来源、时间和“资料而非指令”标记,并附保护状态提示。
  • 非文本附件默认只存元数据;按需获取只支持受限图片或纯文本。音频、视频和压缩包拒绝。图片在 OCR/敏感内容策略未满足前只返回元数据。
  • 保留期固定为:脱敏群消息默认 90 天(磁盘压力可提前清理)、Session 30 天、工具输出全文 14 天、审计 30 天、审批票据为任务结束后 7 天或 30 天的较长者。
  • 保留期以记录创建时间计龄;审批删除时间取“票据创建后 30 天”和“任务终态后 7 天”的较晚者,任务未结束时保留票据。票据记录保留不延长其授权有效期。长期记忆独立管理删除/恢复,群历史清理同步清理 FTS 和相关附件,不通过日志或备份回灌已清理的业务数据。备份应有受控保留期和恢复后清理步骤。

任务与 Session

  • 状态为 received → contextualizing → running → waiting_approval → completing → completed/failed/cancelled,状态持久化并通过数据库唯一约束保证一个活动任务。
  • 只有明确 @Perlica 创建任务。活动任务期间后续提及仍可脱敏入库,但不创建任务,立即通过 Outbox 回复忙碌提示。
  • 初始任务窗口为触发前 15 分钟内最多 30 条消息和最多 24,000 个文本字符;边界超出时截断并记录截断原因。更早历史只能通过受限 group_search_history 获取。
  • 为使边界可测试,先单独保留已保护的触发消息,再按时间从近到远选取窗口消息,最终按时间顺序交给模型;30 条和 24,000 字符预算包含触发消息。按 Unicode 码点计数,不截断保护占位符或结构化消息段。单条触发消息本身超限时拒绝创建任务并提示缩短,不静默丢掉请求主体。
  • Session 只包含任务窗口、任务内 Perlica 回复、工具调用和已保护工具结果。控制命令、其他任务和未明确请求的后续群消息不自动加入。
  • 重启恢复以持久状态和已记录的动作结果为准;同一消息 ID、动作计划和已消费票据不能再次执行。结果不明确的外部调用进入恢复队列,禁止无条件重放高风险动作。

Pi 与工具网关

  • 使用锁定版本的 @earendil-works/pi-coding-agent,关闭默认 bash、read、write、edit,只注册结构化 Perlica 工具。
  • 初始上下文可由管理配置定制并记录配置版本;它经过内容保护,不能覆盖系统权限、工具白名单、任务边界或审批规则。凭据由专用服务凭据通道使用,不作为初始上下文内容。
  • 第一版工具集合为 sandbox_exec、sandbox_read、sandbox_write、group_recent_context、group_search_history、get_attachment、web_search、web_fetch、spectra_upload、sandbox_manage、受限 cluster_*、memory_recall 和 memory_candidate。
  • 工具参数使用结构化对象,不接受任意 SQL、shell 字符串作为沙箱外能力或凭据。执行代理只执行动作计划,不执行模型自然语言授权。
  • MemoryAdapter 隔离不带 scope 的 jayzeng/pi-memory。先验证初始化、工具注册、Session 边界、上下文注入、删除/恢复和 qmd 失败降级;若不能安全隔离,复用其 Markdown 记忆、daily log、scratchpad、qmd 和删除恢复语义而自行实现。群消息、历史检索、Session 摘要和审计由 Perlica 自己拥有。

执行策略与审批

  • Low:沙箱内受资源、网络和运行时限制的文件/命令、测试和构建,自动执行并审计。
  • Medium:已配置明确范围的允许域名 GET/HEAD、依赖安装、集群只读查询、创建允许类型沙箱、修改描述,自动执行并告知。
  • High:集群写入/删除、沙箱删除、上传、Secret/凭据相关动作、MCP/Skill 安装、节点/RBAC/网络策略变化,逐项 Owner 审批。
  • Block:无法判定影响范围、宿主访问、绕过执行代理、向模型或沙箱提供长期凭据、策略自修改,直接拒绝。
  • 结构化动作必须记录动作类型、参数摘要、目标、影响范围、预期结果、策略结果和规范化计划哈希。审批票据绑定 groupId、Owner QQ ID、taskId、计划哈希、目标摘要、10 分钟有效期和一次消费。
  • 审批消息展示动作、目标、参数摘要、出站目的地、短哈希和过期时间。计划任一字段变化、群/Owner/任务不匹配、票据过期、重复消费或执行超时均要求新票据。
  • 沙箱内安全主要依赖 OS/运行时、文件系统、网络和资源限制;危险命令黑名单不能作为唯一边界。沙箱没有长期 Kubernetes kubeconfig 或 ServiceAccount token。
  • 授权对象使用稳定目标身份而非仅名称:计划绑定具体资源身份、镜像 digest/配置 revision、出站正文和目的地等影响执行的字段。执行前重新校验策略和目标版本;状态变化导致影响范围变化时重新规划、重新审批。请求超时或崩溃后只能查询已记录 operation 的实际结果,不能靠新 operation ID 重试来绕过一次消费。
  • 七天过期删除与节点丢失后的清理是 Owner 预先配置的生命周期策略,由独立协调器按原因和配置版本审计执行,不逐次等待聊天审批;模型主动删除仍属于 High。镜像切换审批绑定切换目标和将被销毁的沙箱集合,集合发生变化时重新确认,避免破坏范围扩大。

SQLite 与数据模型

  • 第一版是 SQLite + WAL 单实例;领域接口使用 MessageStore、TaskStore、ApprovalStore、SandboxStore 和 AuditStore,不把 SQLite API 暴露给领域模块。
  • 最小表为 group_messages、attachments、tasks、task_events、session_summaries、approvals、sandbox_image_config、sandboxes、sandbox_operations、audit_events 和 outbox_messages。
  • group_messages 强制目标群 ID、QQ message ID 唯一、保护状态和保留期;FTS5 只索引脱敏文本。
  • group_search_history 后端强制目标群、时间跨度、结果数和字符上限,模型不提交 SQL,结果按相关性/时间排序后再次保护。
  • 第一版历史检索可覆盖尚未过期的群历史,最大跨度不超过配置的消息保留期;默认每次最多 30 条、24,000 个文本字符,参数只能收紧这些上限。每条返回来源时间和消息 ID;空结果、越界请求与权限拒绝都不泄露其他群数据。
  • 任务、沙箱名称、活动沙箱计数、审批消费和 Outbox 幂等键使用数据库约束或事务校验。磁盘告急、备份/恢复和数据库损坏必须可观察。

SandboxBackend 与 rootfs 闸门

  • 核心依赖可替换契约:currentImage()、create(spec)、inspect(name)、execute(name, action)、stop(name)、delete(name)、reconcile()。
  • SandboxImageConfig 包含镜像引用、固定 digest、介绍和 revision。SandboxSpec 包含不可变名称、用途描述、硬节点名、资源限制、网络策略和生命周期策略,不接受每沙箱镜像。
  • 第一后端目标为节点级 OverlayFS/受控 runner 或 containerd runtime handler;持久层保存 upper/work 和恢复元数据,lower 由固定 OCI digest 解包。普通 Pod + PVC 只持久化工作区,不能通过验收。
  • rootfs 实测闸门固定 Linux 节点、可验证支持 xattr/d_type 的本地文件系统和一个固定 digest;必须验证 layer 解包、创建/覆盖/删除、whiteout、目录 rename、权限、硬链接、大文件 copy-up、卸载重挂载、runner/containerd 重启、kill/fsync、最小断电一致性、节点 pin、local PV RWO 冲突、节点离线与恢复清理。
  • 闸门交付 capability、RuntimeClass、seccomp/AppArmor、存储文件系统、节点权限和逃逸面报告。若只能通过过宽 privileged 或宿主 socket 才能工作,能力隔离到专用节点组件并停在风险评审,不进入普通 Agent Pod。
  • 全局事务/租约保证最多三个活动沙箱。实际镜像变化进入 image-switching:暂停创建、停止并销毁全部现有沙箱、确认清理后激活新配置;任一清理失败进入 image-switch-blocked。只改介绍只递增 revision。
  • 容量占位包含创建中、删除中及资源清理未确认的沙箱,防止重试或孤儿资源突破三个上限。镜像切换锁与创建容量事务使用同一协调边界;创建提交前复核全局镜像 revision,防止创建与切换竞态。节点恢复清理前不重新使用旧沙箱身份或名称。
  • lastActiveAt 仅由命令、文件读写、安装等实际触碰刷新,元数据观察不刷新。连续七天无活跃触碰进入删除;删除失败进入告警与孤儿资源表。节点故障标记 lost/deleted,不迁移,恢复后只清理残留。
  • 同一沙箱的写动作和生命周期变更串行化,删除先等待运行操作完成、取消或超时。节点故障由后端明确的节点丢失判定产生,单次 API 查询失败仅视为观察失败,不冒充已验证删除。记录故障判定和清理证据。
  • 每轮连续沙箱操作结束后、任务完成前,向模型发送一次结构化描述复核提示,允许其更新用途描述或明确保持;提示不刷新活跃触碰时间。

Kubernetes、Web 与扩展

  • containers/kubernetes-mcp-server 位于独立 ClusterToolProvider 和 Perlica 策略网关之后,Pi 不直连 MCP。MCP confirmation fallback 不能作为 Perlica 授权。
  • 首个 MCP 实例固定版本,使用 read_only=true、工具白名单、禁用 config、Secret、pods_exec、pods_run 和写工具,独立 ServiceAccount、窄 namespace RoleBinding、NetworkPolicy。工具结果再次保护。
  • 写实例只有在只读路径稳定后部署,使用独立身份,第一版只开放一个经过测试的资源类型/动作;所有写调用先规范化、审批、审计后转发。
  • Tavily 只接收保护后的搜索查询;Web Fetch 只接受明确 URL,正文标记为不可信资料;Spectra 只接收模型明确选择的保护后 Markdown,不上传原始消息、审计、秘密或任意文件。
  • MCP/Skill 安装只由 Owner 控制命令触发,来源、版本、digest/完整性信息进入 lockfile;安装过程不获得集群凭据,模型不能修改策略。
  • Web 控制台第一阶段只读,部署在集群内部或可信反向代理后,显示任务状态、摘要、脱敏工具调用、审批、沙箱、节点、镜像介绍、删除倒计时、审计和连接状态,不提供执行接口或任意文件上传。
  • 控制台必须有认证与只读授权,不能以“内网可达”代替认证。Fetch、附件及沙箱网络执行明确的域名/目的地址与资源限额;重定向和解析后的实际地址再次校验,默认拒绝元数据端点、集群管理面和未授权内部地址。外部读取策略不能变成任意出站上传或集群访问的旁路。

交付顺序与启用条件

  1. 项目骨架:TypeScript/Node、Docker、Vitest、配置校验、迁移、结构化日志和 fail-closed 启动。
  2. rootfs 原型闸门:独立于 Agent 主流程完成真实节点实验和报告;未通过则停止沙箱主线。
  3. 消息边界与保护:NapCat stub、目标群过滤、去重、内容保护、SQLite/FTS5、保留策略和 Outbox。
  4. 任务、dot command 与审批:状态机、busy、计划哈希、票据、恢复和攻击测试。
  5. Pi 与工具网关:fake model/fake executor 合同测试,MemoryAdapter Spike,验证工具循环和保护。
  6. 沙箱集成:接入已通过闸门的后端、三个上限、持久 rootfs、节点绑定、自动删除、镜像切换和 reconciler。
  7. 外部内容服务:Tavily、Fetch、附件和 Spectra,先假服务后真实 API。
  8. Kubernetes MCP:先只读观测,再一个审批写动作;固定版本并重跑工具、RBAC、脱敏和回归测试。
  9. NapCat 真链路与只读控制台:触发、执行、审批、busy、断线恢复和发送失败重试。
  10. 发布硬化:固定镜像 digest、非 root、资源限制、NetworkPolicy、PSA/例外记录、最小 Secret 挂载、探针和单副本恢复。

Testing Decisions

测试边界

主要在最高的应用边界测试:输入标准化 IncomingGroupMessage 或控制命令,通过真实内容保护、SQLite、任务协调器、策略、执行代理和 Outbox,观察 QQ 回复、Session 输入、后端调用和持久化结果。模型、时钟、NapCat 传输、外部 API 和基础设施后端使用可控替身;只在契约和发布阶段做窄范围真实连接测试。

rootfs 是例外的真实环境闸门,必须在真实 Linux 节点、真实目标文件系统和实际 runner/containerd 组合上验证。fake backend、普通 PVC 或单元测试不能证明 rootfs 闸门通过。

必须覆盖的行为

当前仓库只有需求、计划、术语、ADR 和 research,没有现成代码或测试可复用。首批使用 Vitest 在上述应用边界建立行为测试,真实 SQLite 使用临时数据库,可控时钟覆盖期限和重启;后续沿用该边界,不为每个内部模块另建同义测试层。

  • 目标群隔离、消息 ID 去重、未提及不建任务、窗口三重上限、忙碌回复和 Outbox 幂等。
  • 私钥、JWT、Token、Cookie、kubeconfig、ServiceAccount 和高熵样本的替换;扫描超时/错误时原文不落库、不入模型、不出站。
  • SQLite 约束、FTS 目标群强制、历史结果上限、保留期、磁盘压力清理、恢复和损坏告警。
  • 状态机重启恢复、重复事件、模型重试、工具超时、取消和结果不明;高风险动作不无条件重放。
  • .approve/.deny 的群、Owner、任务、计划哈希、短码、过期和一次性消费;修改任一计划字段必须使旧票据失效;审批消息不得进入 Session。
  • Low/Medium/High/Block 的外部行为、结构化参数、输出保护、资源限制、宿主访问拒绝和长期凭据拒绝。
  • SandboxBackend 契约、三个沙箱上限、名称不可变、描述更新、活跃触碰、七天删除、节点丢失/清理、镜像介绍 revision、镜像切换阻塞和新旧 digest 不共存。
  • rootfs 的 copy-up、whiteout、rename、权限、硬链接、大文件、重挂载、重启、kill/fsync、断电最小一致性、节点 pin、RWO 和恢复清理。
  • Pi 默认工具关闭、工具 allowlist、历史/网页/日志资料标记、MemoryAdapter 隔离和 qmd 失败降级。
  • Tavily、Fetch、Spectra、附件和 MCP 的请求参数、结果保护、错误保护及不支持媒体类型的拒绝。
  • MCP 只读工具白名单、Secret/config/exec/run 禁用、ServiceAccount/RBAC namespace 边界、网关不可绕过和一个受审批写动作。
  • 断线重连、QQ 发送失败重试、数据库恢复、控制台只读性和审计可回看性。
  • 白名单、初始上下文配置、超大触发消息、Unicode/占位符截断、沙箱描述复核、删除与触碰竞态、切换与创建竞态、自动生命周期删除、审批目标变化,以及重定向/内部地址绕过。

验收证据

第一版发布前必须提供自动化测试报告、迁移/恢复记录、rootfs 原型报告、策略与逃逸面报告、MCP/RBAC 配置快照、脱敏回归样本和一次真实 NapCat 群 E2E。以下每项均需可观察证据:

  1. 群外消息永不进入 Agent 数据库或模型。
  2. 未 @Perlica 不创建任务,活动任务期间只返回 busy。
  3. 15 分钟、30 条、24,000 字符上限同时生效。
  4. 原始消息库与长期记忆分离。
  5. 所有已启用保护规则在数据库、模型、记忆、外部请求、审计和错误日志出口生效。
  6. 保护失败不外发、不保存原文。
  7. 沙箱低风险动作自动执行且受运行时资源边界约束。
  8. 影响范围未知的动作拒绝。
  9. 高风险动作需要一次性、内容绑定 Owner 审批。
  10. 审批消息不进入模型。
  11. 沙箱没有长期集群凭据。
  12. 执行代理记录计划、实际参数、身份、目标、审批和保护后的结果。
  13. 一个活动任务和同一沙箱写操作不会并发破坏。
  14. 超时、重启、断线和结果不明不会无条件重复高风险动作。
  15. 七天无活跃触碰进入删除流程。
  16. 创建沙箱可获得当前镜像介绍、digest、revision,所有活动沙箱的 digest 处于当前一致状态。
  17. 镜像切换阻止创建、销毁旧沙箱、清理失败保持阻塞,不出现新旧镜像共存。
  18. rootfs backend 可以通过契约替换,且实测恢复整个 rootfs 变更。
  19. Kubernetes MCP 不能绕过 Perlica 网关,首个写动作有 RBAC、审批和审计证据。
  20. NapCat 断线可恢复,QQ 发送失败不重复执行任务。

测试只断言外部行为和安全边界,不锁定具体 SQLite 查询、Pi 扩展内部函数、MCP 实现细节或 runner 私有结构。没有对应真实环境证据的能力只能标记为未启用或实验性,不能纳入已通过验收的第一版。

Out of Scope

  • 多目标群、多租户、跨群协作和并发 Agent 任务。
  • 泛化群管机器人、自动处理未提及聊天、按昵称授权和 QQ 群管理员推断权限。
  • 多副本高写入架构、PostgreSQL 迁移、跨节点沙箱迁移和节点故障后的沙箱恢复。
  • 将普通 Pod + PVC、/workspace 持久化或模型记忆文件宣称为整个 rootfs 持久化。
  • 把危险命令黑名单当作沙箱或执行代理的唯一安全边界。
  • 给 Pi 直接暴露宿主 shell、Kubernetes API、MCP 原始连接、kubeconfig、ServiceAccount token 或宿主 socket。
  • 在第一版开放任意 Kubernetes 写入、pods_exec、pods_run、Helm、Tekton、KubeVirt 或 Secret 操作。
  • 自动接收音频、视频、压缩包或未通过敏感内容策略的图片正文。
  • 模型自行安装扩展、修改策略、修改权限或选择每沙箱基础镜像。
  • 未经模型明确选择的 Spectra 上传、富文本 QQ 消息和控制台执行/任意文件上传。
  • 声称内容保护零漏报、检测到无发现即证明内容安全,或以第三方 MCP confirmation 代替业务审批。
  • 重新进行需求访谈;本规格只整理 REQUIREMENT、PLAN、GLOSSARY、accepted ADR 和现有 research 的决定。

Further Notes

  • 本规格使用 GLOSSARY.md 的“目标群”“控制命令”“Agent 任务”“Session”“内容保护”“沙箱”“全局沙箱镜像”“动作计划”“执行代理”“审批票据”“集群工具适配器”和“扩展”等术语。
  • 遵循 ADR-0001 至 ADR-0006:单目标群与保护消息边界、单活动任务与内容绑定审批、可插拔持久 rootfs、Kubernetes MCP 位于策略网关之后、pi-memory 位于 MemoryAdapter 之后、全局唯一沙箱镜像。
  • 依据:需求、落地计划、术语表、ADR-0001、ADR-0002、ADR-0003、ADR-0004、ADR-0005、ADR-0006、rootfs research、执行策略 research、Pi 与记忆 research、Kubernetes MCP research。Forgejo 阅读时以仓库相应文档为准。
  • 实现前必须给执行超时、CPU/内存/存储、输出大小、附件大小、并发控制、允许网络目的地和节点丢失判定配置有限值;缺失或无界值使相关能力不可启用。具体运行环境数值由 rootfs 实测与部署配置记录,不在本规格中假定。
  • ready-for-agent 表示本文规格已具备实现入口,不表示 rootfs 实测闸门、真实 QQ 连接、真实外部 API 或生产发布已经通过。
  • rootfs 闸门失败、保护能力不足、MCP 权限边界无法证明或结果不明的高风险动作都应停止对应能力并保留证据;不得静默降级或扩大权限。
  • 第一版默认单实例 SQLite 和只读控制台是有意限制。需要多副本、跨节点恢复或更广 Kubernetes 工具集时,应新增 ADR、研究和验收标准。
# Perlica v1:单目标群任务助手可实施规格 版本:1;整理日期:2026-10-15;追踪 Issue:https://git.feniota.org/feniota/perlica/issues/1。 ## Problem Statement Perlica 需要在受控 Kubernetes 集群中为一个可信 QQ 群执行开发与运维任务。当前资料已经确定了目标群边界、消息保护、单活动任务、持久根文件系统沙箱、策略网关、审批和审计等决定,但还缺少一份按实现顺序组织、可由工程任务直接执行并可验收的规格。 第一版的核心风险是边界被隐式扩大:群外消息进入模型、凭据样内容进入持久层或出站服务、审批被自然语言复用、沙箱只持久化工作区、Kubernetes MCP 绕过策略网关,或 rootfs 的未验证实现被当成已完成能力。本规格将这些边界转换为模块契约、状态、验收条件和测试证据。 ## Solution 实现一个 TypeScript/Node.js 单实例服务,运行于 Kubernetes,并通过 NapCatQQ 连接一个配置指定的目标 QQ 群。消息入口先执行群边界、去重和内容保护,再写入 SQLite;只有明确 `@Perlica` 的目标群消息创建 Agent 任务。任务协调器维护一个持久状态机和唯一活动任务,使用 Pi Agent SDK 运行受限 Session,只注册 Perlica 的结构化工具。 所有工具调用都经过 Perlica 工具网关和执行代理。执行代理把动作规范化为动作计划,按 `allow/ask/block` 判定,执行或发出一次性审批请求,并记录实际参数、身份、目标和保护后的结果。沙箱、Kubernetes、Tavily、Web Fetch、Spectra、附件和记忆组件都位于该边界之后。 沙箱由可替换的 `SandboxBackend` 提供,最多三个,创建时继承唯一的全局沙箱镜像配置。沙箱目标语义是可恢复的整个根文件系统;节点绑定、持久 upper/work、重挂载、崩溃恢复和清理必须先通过真实 Linux 节点 rootfs 实测闸门。闸门未通过时不声称沙箱能力完成,也不把只持久化工作区作为等价实现。 第一版只读 Web 控制台复用任务、审批、沙箱和审计查询,不直接执行动作。QQ 只发送普通文本;长 Markdown 只有在模型明确调用 `spectra_upload` 且内容完成保护后才上传,QQ 最终收到普通文本短链接。 ## User Stories 1. 作为目标群成员,我想在目标群中明确 `@Perlica` 发起任务,以便只有我明确请求时才消耗 Agent 资源。 2. 作为非目标群成员,我希望我的消息不进入 Perlica 的数据库、模型或业务日志,以便群边界可验证。 3. 作为目标群成员,我希望普通群消息在保存前自动替换凭据样内容,以便历史和任务资料不保存秘密原文。 4. 作为系统维护者,我希望保护扫描失败时保存缺失状态而不是原文,以便失败关闭而不是绕过安全边界。 5. 作为目标群成员,我希望任务只带触发前 15 分钟、最多 30 条消息和最多 24,000 个文本字符,以便 Session 不被无关聊天淹没。 6. 作为目标群成员,我希望能通过受限历史检索寻找更早消息,以便有限任务窗口仍能处理较长背景。 7. 作为任务发起者,我希望同一时间只有一个活动任务,以便任务之间不会争用沙箱、混淆 Session 或交叉使用审批。 8. 作为后来发起任务的成员,我希望收到明确的忙碌提示,以便知道请求未被悄悄丢弃且可以稍后重试。 9. 作为系统维护者,我希望进程重启后能从任务状态继续,以便 QQ 重连或服务恢复不会重复执行已完成的动作。 10. 作为模型运行时,我希望只看到当前任务、受限历史和已保护工具资料,以便控制命令、其他任务和未保护值不会进入上下文。 11. 作为系统维护者,我希望 Pi 默认 shell、read、write、edit 工具关闭,以便所有执行都能经过统一策略和审计。 12. 作为任务发起者,我希望模型可以在沙箱中执行受限命令和读写文件,以便完成构建、测试和开发工作。 13. 作为任务发起者,我希望沙箱内工具的输出受到时间、资源和输出大小限制,以便单个任务不会耗尽服务资源。 14. 作为 Owner,我希望创建沙箱时可以指定不可迁移的节点和用途描述,以便工作负载落在已知节点并可回看用途。 15. 作为任务发起者,我希望创建沙箱时能看到全局镜像介绍、digest 和 revision,以便无需 probe 就能选择适合的工具链。 16. 作为 Owner,我希望所有沙箱共享唯一的全局镜像配置,以便系统不会同时运行无法解释的基础环境。 17. 作为 Owner,我希望只修改镜像介绍时不销毁沙箱,以便更新环境说明不造成破坏性操作。 18. 作为 Owner,我希望更换实际镜像时系统自动暂停创建并清理所有旧沙箱,以便新旧镜像不会共存。 19. 作为 Owner,我希望镜像清理失败时切换保持阻塞,以便系统不会在资源状态不明时启用新镜像。 20. 作为任务发起者,我希望七天没有实际触碰的沙箱自动删除,以便持久资源不会无限积累。 21. 作为系统维护者,我希望节点故障的沙箱被标记为已删除且不自动迁移,以便节点绑定语义和数据风险保持明确。 22. 作为 Owner,我希望能够更新沙箱用途描述但不能重命名沙箱,以便名称稳定而说明可以持续维护。 23. 作为 Owner,我希望能查看沙箱操作、状态、最后活跃触碰和清理结果,以便处理孤儿资源和过期工作空间。 24. 作为系统维护者,我希望低风险动作自动执行并审计,以便正常开发任务无需重复确认。 25. 作为系统维护者,我希望已有明确范围的中风险动作按策略自动执行并告知,以便允许域名 GET、依赖安装、只读集群查询等工作保持可用。 26. 作为 Owner,我希望高风险动作逐项审批,以便集群写入、删除、上传、扩展安装和凭据相关动作不会由模型自行决定。 27. 作为系统维护者,我希望影响范围未知的动作直接拒绝,以便“询问确认”不会掩盖无法识别的权限边界。 28. 作为 Owner,我希望审批票据绑定目标群、任务、动作计划哈希和过期时间,以便授权不能被改参数或换任务复用。 29. 作为 Owner,我希望 `.approve` 和 `.deny` 在模型上下文之外解析,以便审批消息不会成为可被模型解释的自然语言资料。 30. 作为系统维护者,我希望审批票据最多消费一次且过期后不可用,以便重放和超时重试不会重复执行高风险动作。 31. 作为审计人员,我希望看到规范化计划、实际参数、执行身份、目标、审批票据和结果,以便事后判断系统实际做了什么。 32. 作为系统维护者,我希望历史、网页、日志和工具结果带有资料标记,以便模型不会把不可信资料当成授权指令。 33. 作为任务发起者,我希望使用 Tavily 搜索和 Web Fetch,以便在不暴露整个 Session 的情况下获取外部资料。 34. 作为任务发起者,我希望只在明确请求时获取附件,以便图片和文件不会被默认下载或未经保护进入模型。 35. 作为系统维护者,我希望不支持的附件类型只返回元数据或拒绝,以便媒体保护能力不足时不会假装安全完成。 36. 作为 Owner,我希望安装 MCP 或 Skill 时记录来源、版本和完整性信息,以便扩展供应链可回看且不能自行改变策略。 37. 作为系统维护者,我希望长期记忆只收到筛选并保护后的事实,以便记忆组件不会成为原始消息或审批的旁路存储。 38. 作为系统维护者,我希望 pi-memory 通过 `MemoryAdapter` 接入并可替换,以便其 Pi 生命周期耦合不会破坏任务边界。 39. 作为 Kubernetes 运维者,我希望只读 Kubernetes MCP 使用独立身份、窄 RBAC、工具白名单和禁用 Secret,以便观测能力不等于集群管理权限。 40. 作为系统维护者,我希望 Kubernetes MCP 不能直接连接 Pi,以便 MCP 的工具过滤或确认提示不会替代 Perlica 策略网关。 41. 作为 Owner,我希望第一版只开放一个经过测试的集群写动作,以便写能力从明确证据开始逐步扩大。 42. 作为目标群成员,我希望收到普通 QQ 文本回复,以便 NapCat 真人账号不会依赖富文本渲染。 43. 作为任务发起者,我希望长 Markdown 可以通过 Spectra 变为短链接,以便 QQ 回复保持简洁且能访问完整结果。 44. 作为控制台使用者,我希望看到任务状态、Session 摘要、审批、沙箱和脱敏审计,以便在不获得执行权限的情况下观察运行情况。 45. 作为系统维护者,我希望 QQ 发送失败只重试 Outbox 消息而不重建任务,以便外部传输故障不会重复执行。 46. 作为发布负责人,我希望每个主要安全声明都有自动化测试和必要的真实环境证据,以便“通过”有明确含义。 ## Implementation Decisions ### 边界与角色 - 配置一个 `targetGroupId`;入口第一步比较群 ID。非目标群事件在内存丢弃,不写业务日志、数据库或模型。 - 成员与 Owner 均绑定目标群 ID 和 QQ 数字 ID,不使用昵称或 QQ 群管理员身份。第一版两名成员都登记为 Owner。 - 只有白名单成员的明确提及可以触发任务;其他身份的消息不授予任务或管理权限。控制命令必须先校验角色,未知命令不回退到模型执行。目标群内普通聊天仍按统一内容保护规则处理。 - 点号开头的控制命令由 `dot-command-router` 解析,不进入 Agent Session。第一版至少支持 `.approve <approval-id> <short-code>`、`.deny <approval-id>`,以及 Owner 的沙箱、镜像介绍和扩展管理命令。 - NapCat 事件只在 `NapCatAdapter` 内转换为 `IncomingGroupMessage`;领域模块不依赖 OneBot/QQ 事件类型。 - 目标群消息按 QQ message ID 去重;Outbox 发送按幂等键去重。 ### 内容保护与数据流 - 统一 `ContentProtection` 出站入口执行结构化抽取、凭据/私钥/Token/kubeconfig 模式检测、不可逆替换、最终序列化和再次扫描。 - 返回值包含 `content`、`status`(`redacted`、`scanned_no_findings`、`scan_incomplete`、`not_scanned`)、`policyVersion` 和 `findingCount`。`scan_incomplete` 不得作为安全通过。 - 第一版规则覆盖常见私钥、JWT、云 Token、Authorization/Cookie、kubeconfig、ServiceAccount 和高熵凭据样式。检测不是有效性验证,也不宣称零漏报。 - 数据库、模型、长期记忆、Tavily、Fetch、Spectra、审计和错误日志只接收保护后的值。工具资料进入 Session 时带来源、时间和“资料而非指令”标记,并附保护状态提示。 - 非文本附件默认只存元数据;按需获取只支持受限图片或纯文本。音频、视频和压缩包拒绝。图片在 OCR/敏感内容策略未满足前只返回元数据。 - 保留期固定为:脱敏群消息默认 90 天(磁盘压力可提前清理)、Session 30 天、工具输出全文 14 天、审计 30 天、审批票据为任务结束后 7 天或 30 天的较长者。 - 保留期以记录创建时间计龄;审批删除时间取“票据创建后 30 天”和“任务终态后 7 天”的较晚者,任务未结束时保留票据。票据记录保留不延长其授权有效期。长期记忆独立管理删除/恢复,群历史清理同步清理 FTS 和相关附件,不通过日志或备份回灌已清理的业务数据。备份应有受控保留期和恢复后清理步骤。 ### 任务与 Session - 状态为 `received → contextualizing → running → waiting_approval → completing → completed/failed/cancelled`,状态持久化并通过数据库唯一约束保证一个活动任务。 - 只有明确 `@Perlica` 创建任务。活动任务期间后续提及仍可脱敏入库,但不创建任务,立即通过 Outbox 回复忙碌提示。 - 初始任务窗口为触发前 15 分钟内最多 30 条消息和最多 24,000 个文本字符;边界超出时截断并记录截断原因。更早历史只能通过受限 `group_search_history` 获取。 - 为使边界可测试,先单独保留已保护的触发消息,再按时间从近到远选取窗口消息,最终按时间顺序交给模型;30 条和 24,000 字符预算包含触发消息。按 Unicode 码点计数,不截断保护占位符或结构化消息段。单条触发消息本身超限时拒绝创建任务并提示缩短,不静默丢掉请求主体。 - Session 只包含任务窗口、任务内 Perlica 回复、工具调用和已保护工具结果。控制命令、其他任务和未明确请求的后续群消息不自动加入。 - 重启恢复以持久状态和已记录的动作结果为准;同一消息 ID、动作计划和已消费票据不能再次执行。结果不明确的外部调用进入恢复队列,禁止无条件重放高风险动作。 ### Pi 与工具网关 - 使用锁定版本的 `@earendil-works/pi-coding-agent`,关闭默认 `bash`、`read`、`write`、`edit`,只注册结构化 Perlica 工具。 - 初始上下文可由管理配置定制并记录配置版本;它经过内容保护,不能覆盖系统权限、工具白名单、任务边界或审批规则。凭据由专用服务凭据通道使用,不作为初始上下文内容。 - 第一版工具集合为 `sandbox_exec`、`sandbox_read`、`sandbox_write`、`group_recent_context`、`group_search_history`、`get_attachment`、`web_search`、`web_fetch`、`spectra_upload`、`sandbox_manage`、受限 `cluster_*`、`memory_recall` 和 `memory_candidate`。 - 工具参数使用结构化对象,不接受任意 SQL、shell 字符串作为沙箱外能力或凭据。执行代理只执行动作计划,不执行模型自然语言授权。 - `MemoryAdapter` 隔离不带 scope 的 `jayzeng/pi-memory`。先验证初始化、工具注册、Session 边界、上下文注入、删除/恢复和 qmd 失败降级;若不能安全隔离,复用其 Markdown 记忆、daily log、scratchpad、qmd 和删除恢复语义而自行实现。群消息、历史检索、Session 摘要和审计由 Perlica 自己拥有。 ### 执行策略与审批 - `Low`:沙箱内受资源、网络和运行时限制的文件/命令、测试和构建,自动执行并审计。 - `Medium`:已配置明确范围的允许域名 GET/HEAD、依赖安装、集群只读查询、创建允许类型沙箱、修改描述,自动执行并告知。 - `High`:集群写入/删除、沙箱删除、上传、Secret/凭据相关动作、MCP/Skill 安装、节点/RBAC/网络策略变化,逐项 Owner 审批。 - `Block`:无法判定影响范围、宿主访问、绕过执行代理、向模型或沙箱提供长期凭据、策略自修改,直接拒绝。 - 结构化动作必须记录动作类型、参数摘要、目标、影响范围、预期结果、策略结果和规范化计划哈希。审批票据绑定 `groupId`、Owner QQ ID、`taskId`、计划哈希、目标摘要、10 分钟有效期和一次消费。 - 审批消息展示动作、目标、参数摘要、出站目的地、短哈希和过期时间。计划任一字段变化、群/Owner/任务不匹配、票据过期、重复消费或执行超时均要求新票据。 - 沙箱内安全主要依赖 OS/运行时、文件系统、网络和资源限制;危险命令黑名单不能作为唯一边界。沙箱没有长期 Kubernetes kubeconfig 或 ServiceAccount token。 - 授权对象使用稳定目标身份而非仅名称:计划绑定具体资源身份、镜像 digest/配置 revision、出站正文和目的地等影响执行的字段。执行前重新校验策略和目标版本;状态变化导致影响范围变化时重新规划、重新审批。请求超时或崩溃后只能查询已记录 operation 的实际结果,不能靠新 operation ID 重试来绕过一次消费。 - 七天过期删除与节点丢失后的清理是 Owner 预先配置的生命周期策略,由独立协调器按原因和配置版本审计执行,不逐次等待聊天审批;模型主动删除仍属于 High。镜像切换审批绑定切换目标和将被销毁的沙箱集合,集合发生变化时重新确认,避免破坏范围扩大。 ### SQLite 与数据模型 - 第一版是 SQLite + WAL 单实例;领域接口使用 `MessageStore`、`TaskStore`、`ApprovalStore`、`SandboxStore` 和 `AuditStore`,不把 SQLite API 暴露给领域模块。 - 最小表为 `group_messages`、`attachments`、`tasks`、`task_events`、`session_summaries`、`approvals`、`sandbox_image_config`、`sandboxes`、`sandbox_operations`、`audit_events` 和 `outbox_messages`。 - `group_messages` 强制目标群 ID、QQ message ID 唯一、保护状态和保留期;FTS5 只索引脱敏文本。 - `group_search_history` 后端强制目标群、时间跨度、结果数和字符上限,模型不提交 SQL,结果按相关性/时间排序后再次保护。 - 第一版历史检索可覆盖尚未过期的群历史,最大跨度不超过配置的消息保留期;默认每次最多 30 条、24,000 个文本字符,参数只能收紧这些上限。每条返回来源时间和消息 ID;空结果、越界请求与权限拒绝都不泄露其他群数据。 - 任务、沙箱名称、活动沙箱计数、审批消费和 Outbox 幂等键使用数据库约束或事务校验。磁盘告急、备份/恢复和数据库损坏必须可观察。 ### SandboxBackend 与 rootfs 闸门 - 核心依赖可替换契约:`currentImage()`、`create(spec)`、`inspect(name)`、`execute(name, action)`、`stop(name)`、`delete(name)`、`reconcile()`。 - `SandboxImageConfig` 包含镜像引用、固定 digest、介绍和 revision。`SandboxSpec` 包含不可变名称、用途描述、硬节点名、资源限制、网络策略和生命周期策略,不接受每沙箱镜像。 - 第一后端目标为节点级 OverlayFS/受控 runner 或 containerd runtime handler;持久层保存 upper/work 和恢复元数据,lower 由固定 OCI digest 解包。普通 Pod + PVC 只持久化工作区,不能通过验收。 - rootfs 实测闸门固定 Linux 节点、可验证支持 xattr/`d_type` 的本地文件系统和一个固定 digest;必须验证 layer 解包、创建/覆盖/删除、whiteout、目录 rename、权限、硬链接、大文件 copy-up、卸载重挂载、runner/containerd 重启、kill/fsync、最小断电一致性、节点 pin、local PV RWO 冲突、节点离线与恢复清理。 - 闸门交付 capability、RuntimeClass、seccomp/AppArmor、存储文件系统、节点权限和逃逸面报告。若只能通过过宽 privileged 或宿主 socket 才能工作,能力隔离到专用节点组件并停在风险评审,不进入普通 Agent Pod。 - 全局事务/租约保证最多三个活动沙箱。实际镜像变化进入 `image-switching`:暂停创建、停止并销毁全部现有沙箱、确认清理后激活新配置;任一清理失败进入 `image-switch-blocked`。只改介绍只递增 revision。 - 容量占位包含创建中、删除中及资源清理未确认的沙箱,防止重试或孤儿资源突破三个上限。镜像切换锁与创建容量事务使用同一协调边界;创建提交前复核全局镜像 revision,防止创建与切换竞态。节点恢复清理前不重新使用旧沙箱身份或名称。 - `lastActiveAt` 仅由命令、文件读写、安装等实际触碰刷新,元数据观察不刷新。连续七天无活跃触碰进入删除;删除失败进入告警与孤儿资源表。节点故障标记 `lost/deleted`,不迁移,恢复后只清理残留。 - 同一沙箱的写动作和生命周期变更串行化,删除先等待运行操作完成、取消或超时。节点故障由后端明确的节点丢失判定产生,单次 API 查询失败仅视为观察失败,不冒充已验证删除。记录故障判定和清理证据。 - 每轮连续沙箱操作结束后、任务完成前,向模型发送一次结构化描述复核提示,允许其更新用途描述或明确保持;提示不刷新活跃触碰时间。 ### Kubernetes、Web 与扩展 - `containers/kubernetes-mcp-server` 位于独立 ClusterToolProvider 和 Perlica 策略网关之后,Pi 不直连 MCP。MCP confirmation fallback 不能作为 Perlica 授权。 - 首个 MCP 实例固定版本,使用 `read_only=true`、工具白名单、禁用 `config`、Secret、`pods_exec`、`pods_run` 和写工具,独立 ServiceAccount、窄 namespace RoleBinding、NetworkPolicy。工具结果再次保护。 - 写实例只有在只读路径稳定后部署,使用独立身份,第一版只开放一个经过测试的资源类型/动作;所有写调用先规范化、审批、审计后转发。 - Tavily 只接收保护后的搜索查询;Web Fetch 只接受明确 URL,正文标记为不可信资料;Spectra 只接收模型明确选择的保护后 Markdown,不上传原始消息、审计、秘密或任意文件。 - MCP/Skill 安装只由 Owner 控制命令触发,来源、版本、digest/完整性信息进入 lockfile;安装过程不获得集群凭据,模型不能修改策略。 - Web 控制台第一阶段只读,部署在集群内部或可信反向代理后,显示任务状态、摘要、脱敏工具调用、审批、沙箱、节点、镜像介绍、删除倒计时、审计和连接状态,不提供执行接口或任意文件上传。 - 控制台必须有认证与只读授权,不能以“内网可达”代替认证。Fetch、附件及沙箱网络执行明确的域名/目的地址与资源限额;重定向和解析后的实际地址再次校验,默认拒绝元数据端点、集群管理面和未授权内部地址。外部读取策略不能变成任意出站上传或集群访问的旁路。 ### 交付顺序与启用条件 1. 项目骨架:TypeScript/Node、Docker、Vitest、配置校验、迁移、结构化日志和 fail-closed 启动。 2. rootfs 原型闸门:独立于 Agent 主流程完成真实节点实验和报告;未通过则停止沙箱主线。 3. 消息边界与保护:NapCat stub、目标群过滤、去重、内容保护、SQLite/FTS5、保留策略和 Outbox。 4. 任务、dot command 与审批:状态机、busy、计划哈希、票据、恢复和攻击测试。 5. Pi 与工具网关:fake model/fake executor 合同测试,MemoryAdapter Spike,验证工具循环和保护。 6. 沙箱集成:接入已通过闸门的后端、三个上限、持久 rootfs、节点绑定、自动删除、镜像切换和 reconciler。 7. 外部内容服务:Tavily、Fetch、附件和 Spectra,先假服务后真实 API。 8. Kubernetes MCP:先只读观测,再一个审批写动作;固定版本并重跑工具、RBAC、脱敏和回归测试。 9. NapCat 真链路与只读控制台:触发、执行、审批、busy、断线恢复和发送失败重试。 10. 发布硬化:固定镜像 digest、非 root、资源限制、NetworkPolicy、PSA/例外记录、最小 Secret 挂载、探针和单副本恢复。 ## Testing Decisions ### 测试边界 主要在最高的应用边界测试:输入标准化 `IncomingGroupMessage` 或控制命令,通过真实内容保护、SQLite、任务协调器、策略、执行代理和 Outbox,观察 QQ 回复、Session 输入、后端调用和持久化结果。模型、时钟、NapCat 传输、外部 API 和基础设施后端使用可控替身;只在契约和发布阶段做窄范围真实连接测试。 rootfs 是例外的真实环境闸门,必须在真实 Linux 节点、真实目标文件系统和实际 runner/containerd 组合上验证。fake backend、普通 PVC 或单元测试不能证明 rootfs 闸门通过。 ### 必须覆盖的行为 当前仓库只有需求、计划、术语、ADR 和 research,没有现成代码或测试可复用。首批使用 Vitest 在上述应用边界建立行为测试,真实 SQLite 使用临时数据库,可控时钟覆盖期限和重启;后续沿用该边界,不为每个内部模块另建同义测试层。 - 目标群隔离、消息 ID 去重、未提及不建任务、窗口三重上限、忙碌回复和 Outbox 幂等。 - 私钥、JWT、Token、Cookie、kubeconfig、ServiceAccount 和高熵样本的替换;扫描超时/错误时原文不落库、不入模型、不出站。 - SQLite 约束、FTS 目标群强制、历史结果上限、保留期、磁盘压力清理、恢复和损坏告警。 - 状态机重启恢复、重复事件、模型重试、工具超时、取消和结果不明;高风险动作不无条件重放。 - `.approve`/`.deny` 的群、Owner、任务、计划哈希、短码、过期和一次性消费;修改任一计划字段必须使旧票据失效;审批消息不得进入 Session。 - Low/Medium/High/Block 的外部行为、结构化参数、输出保护、资源限制、宿主访问拒绝和长期凭据拒绝。 - SandboxBackend 契约、三个沙箱上限、名称不可变、描述更新、活跃触碰、七天删除、节点丢失/清理、镜像介绍 revision、镜像切换阻塞和新旧 digest 不共存。 - rootfs 的 copy-up、whiteout、rename、权限、硬链接、大文件、重挂载、重启、kill/fsync、断电最小一致性、节点 pin、RWO 和恢复清理。 - Pi 默认工具关闭、工具 allowlist、历史/网页/日志资料标记、MemoryAdapter 隔离和 qmd 失败降级。 - Tavily、Fetch、Spectra、附件和 MCP 的请求参数、结果保护、错误保护及不支持媒体类型的拒绝。 - MCP 只读工具白名单、Secret/config/exec/run 禁用、ServiceAccount/RBAC namespace 边界、网关不可绕过和一个受审批写动作。 - 断线重连、QQ 发送失败重试、数据库恢复、控制台只读性和审计可回看性。 - 白名单、初始上下文配置、超大触发消息、Unicode/占位符截断、沙箱描述复核、删除与触碰竞态、切换与创建竞态、自动生命周期删除、审批目标变化,以及重定向/内部地址绕过。 ### 验收证据 第一版发布前必须提供自动化测试报告、迁移/恢复记录、rootfs 原型报告、策略与逃逸面报告、MCP/RBAC 配置快照、脱敏回归样本和一次真实 NapCat 群 E2E。以下每项均需可观察证据: 1. 群外消息永不进入 Agent 数据库或模型。 2. 未 `@Perlica` 不创建任务,活动任务期间只返回 busy。 3. 15 分钟、30 条、24,000 字符上限同时生效。 4. 原始消息库与长期记忆分离。 5. 所有已启用保护规则在数据库、模型、记忆、外部请求、审计和错误日志出口生效。 6. 保护失败不外发、不保存原文。 7. 沙箱低风险动作自动执行且受运行时资源边界约束。 8. 影响范围未知的动作拒绝。 9. 高风险动作需要一次性、内容绑定 Owner 审批。 10. 审批消息不进入模型。 11. 沙箱没有长期集群凭据。 12. 执行代理记录计划、实际参数、身份、目标、审批和保护后的结果。 13. 一个活动任务和同一沙箱写操作不会并发破坏。 14. 超时、重启、断线和结果不明不会无条件重复高风险动作。 15. 七天无活跃触碰进入删除流程。 16. 创建沙箱可获得当前镜像介绍、digest、revision,所有活动沙箱的 digest 处于当前一致状态。 17. 镜像切换阻止创建、销毁旧沙箱、清理失败保持阻塞,不出现新旧镜像共存。 18. rootfs backend 可以通过契约替换,且实测恢复整个 rootfs 变更。 19. Kubernetes MCP 不能绕过 Perlica 网关,首个写动作有 RBAC、审批和审计证据。 20. NapCat 断线可恢复,QQ 发送失败不重复执行任务。 测试只断言外部行为和安全边界,不锁定具体 SQLite 查询、Pi 扩展内部函数、MCP 实现细节或 runner 私有结构。没有对应真实环境证据的能力只能标记为未启用或实验性,不能纳入已通过验收的第一版。 ## Out of Scope - 多目标群、多租户、跨群协作和并发 Agent 任务。 - 泛化群管机器人、自动处理未提及聊天、按昵称授权和 QQ 群管理员推断权限。 - 多副本高写入架构、PostgreSQL 迁移、跨节点沙箱迁移和节点故障后的沙箱恢复。 - 将普通 Pod + PVC、`/workspace` 持久化或模型记忆文件宣称为整个 rootfs 持久化。 - 把危险命令黑名单当作沙箱或执行代理的唯一安全边界。 - 给 Pi 直接暴露宿主 shell、Kubernetes API、MCP 原始连接、kubeconfig、ServiceAccount token 或宿主 socket。 - 在第一版开放任意 Kubernetes 写入、`pods_exec`、`pods_run`、Helm、Tekton、KubeVirt 或 Secret 操作。 - 自动接收音频、视频、压缩包或未通过敏感内容策略的图片正文。 - 模型自行安装扩展、修改策略、修改权限或选择每沙箱基础镜像。 - 未经模型明确选择的 Spectra 上传、富文本 QQ 消息和控制台执行/任意文件上传。 - 声称内容保护零漏报、检测到无发现即证明内容安全,或以第三方 MCP confirmation 代替业务审批。 - 重新进行需求访谈;本规格只整理 REQUIREMENT、PLAN、GLOSSARY、accepted ADR 和现有 research 的决定。 ## Further Notes - 本规格使用 `GLOSSARY.md` 的“目标群”“控制命令”“Agent 任务”“Session”“内容保护”“沙箱”“全局沙箱镜像”“动作计划”“执行代理”“审批票据”“集群工具适配器”和“扩展”等术语。 - 遵循 ADR-0001 至 ADR-0006:单目标群与保护消息边界、单活动任务与内容绑定审批、可插拔持久 rootfs、Kubernetes MCP 位于策略网关之后、pi-memory 位于 MemoryAdapter 之后、全局唯一沙箱镜像。 - 依据:[需求](../../REQUIREMENT.md)、[落地计划](../PLAN.md)、[术语表](../../GLOSSARY.md)、[ADR-0001](../adr/0001-target-group-and-protected-message-boundary.md)、[ADR-0002](../adr/0002-single-task-and-content-bound-approval.md)、[ADR-0003](../adr/0003-pluggable-persistent-rootfs-backend.md)、[ADR-0004](../adr/0004-kubernetes-mcp-behind-policy-gateway.md)、[ADR-0005](../adr/0005-pi-memory-behind-memory-adapter.md)、[ADR-0006](../adr/0006-global-sandbox-image.md)、[rootfs research](../research/persistent-rootfs-options.md)、[执行策略 research](../research/execution-policy.md)、[Pi 与记忆 research](../research/pi-sdk-memory.md)、[Kubernetes MCP research](../research/kubernetes-mcp-server-perlica.md)。Forgejo 阅读时以仓库相应文档为准。 - 实现前必须给执行超时、CPU/内存/存储、输出大小、附件大小、并发控制、允许网络目的地和节点丢失判定配置有限值;缺失或无界值使相关能力不可启用。具体运行环境数值由 rootfs 实测与部署配置记录,不在本规格中假定。 - `ready-for-agent` 表示本文规格已具备实现入口,不表示 rootfs 实测闸门、真实 QQ 连接、真实外部 API 或生产发布已经通过。 - rootfs 闸门失败、保护能力不足、MCP 权限边界无法证明或结果不明的高风险动作都应停止对应能力并保留证据;不得静默降级或扩大权限。 - 第一版默认单实例 SQLite 和只读控制台是有意限制。需要多副本、跨节点恢复或更广 Kubernetes 工具集时,应新增 ADR、研究和验收标准。
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
feniota/perlica#1
No description provided.