Files
CyberStrikeAI/docs/zh-CN/developer-guide.md
T
Sycun 27c1ff6573 feat: give the update source a page, and the adopt flow its preview
The console page grows an update-source section: what is in effect and where it
comes from (config.yaml or the tree's own tracking), an edit form for the three
fields, and - for a directory that is not a git installation - the adopt entry.
Adopt is preview-first: the plan lists what would be replaced and what would be
kept, the confirm dialog carries the counts, and the restart tick is the same one
an update uses. A refused save shows the server's own sentence (a remote name and
an address together, an ext:: address) instead of a generic failure, and the typed
values survive the failed save and the polling redraws.

zh/EN/RU dictionaries carry the 27 new keys; the console page's contract tests
grow to 29 (source rendering and save body, the refusal path, adopt gating by
installation kind, preview-then-confirm); docs/README describe choosing a source
and connecting an unpacked installation.
2026-10-07 12:49:15 +08:00

12 KiB
Raw Blame History

开发者指南

本文面向二次开发者,说明项目结构、启动方式、主要扩展点和开发习惯。

项目结构

cmd/server/              Web 服务入口
internal/app/            应用组装、路由注册、MCP 工具注册
internal/handler/        HTTP Handler
internal/database/       SQLite 数据访问
internal/security/       认证、限流、Shell 执行
internal/mcp/            MCP Server、外部 MCP 管理
internal/multiagent/     Eino 单代理、多代理、中间件
internal/workflow/       工作流运行时
internal/knowledge/      知识库索引与检索
internal/c2/             内置 C2
internal/project/        项目事实黑板
web/static/              前端 JS/CSS/资源
web/templates/           HTML 模板
tools/                   命令工具 YAML
roles/                   角色 YAML
agents/                  多代理 Markdown 定义
skills/                  Agent Skills
docs/                    项目文档

启动开发环境

go run ./cmd/server --config config.yaml

前端是静态页面,模板在 web/templates/,JS/CSS 在 web/static/。修改后刷新浏览器即可验证,多数场景不需要单独前端构建。

路由

路由集中在 internal/app/app.go 的 registerRoutes 中。新增业务接口通常需要:

  1. 在 internal/handler/ 增加 Handler。
  2. 在 internal/database/ 增加必要的数据访问。
  3. 在 internal/app/app.go 构造并注册路由。
  4. 如需对外文档,更新 internal/handler/openapi.go。
  5. 如需前端调用,更新 web/static/js/。

一键更新:让安装目录更新它自己的源码

部署现场的日常是"我的 fork 又前进了几十个提交 → 把这套安装带上去 → 重新编译 → 换二进制 → 重启"。 这件事以前只有 upgrade.sh,而它写死了别人的仓库:对跑 fork 的人来说"升级"等于"用别人的代码覆盖自己"。 现在这个能力做进了平台自己(internal/update),页面、REST 与 CLI 三条入口共用同一份实现, 所以它们给出的拒绝理由不可能各说各话。

三种入口

入口 怎么用
控制台 「平台管理 → 一键更新」(#system-update)。打开页面只读本机状态、不联网;点「检查更新」才去 fetch;「一键更新」发起任务并轮询进度;页面另有"更新完成后退出进程"勾选与回滚按钮
REST GET /api/system/update(磁盘现状,不联网)、POST /api/system/update/check(fetch 后报告差集)、POST /api/system/update/apply(202 返回 job_id,用 GET /api/system/update/job 轮询)、POST /api/system/update/rollback
CLI ./cyberstrike-ai -check-update、./cyberstrike-ai -update、./cyberstrike-ai -update-rollback。安装目录 = --config 所在目录,未给 --config 时是当前目录

权限是 update:read(GET)与 update:apply(四个写接口)。update:apply 还要求会话是 global(all scope): 一台机器只有一份源码,assigned/own scope 的账号不该能移动别人正在跑的代码。

它从哪里取代码

从这个目录自己的远端,没有任何写死的第三方仓库地址。远端按 mine → origin → upstream 的次序在这个目录已有的 remote 里取(都不在时取第一个 remote),分支默认取当前分支 @{upstream} 指向的名字(没有 upstream 时用当前分支名)。 upgrade.sh 现在只是这条实现的薄壳:本目录是 git 工作树时它直接调 ./cyberstrike-ai -update。

选择更新源(config.yaml 的 update 段)

不配置时按上面的规则跟随本树;配置后显式来源优先:

update:
  remote: origin        # 已有远端名(mine/origin/upstream/任意名),与 remote_url 二选一
  # remote_url: https://github.com/AIPentest/CyberStrikeAI.git
  branch: main          # 可选;留空按 upstream/当前分支推断

控制台的「一键更新」页可以直接编辑并保存这三项。服务端保存时校验:远端名与分支名走与更新同一套白名单, 地址只允许 https/http/ssh/git/file:// 与本机绝对路径——git 的 ext:: 传输会执行命令,一律拒绝。 想让这套安装跟随官方仓库、自己的二开或别人的二开,改的都是这一处。

接入非 git 安装(解压/打包装的那类)

一开始用 Release 包解压安装、目录里没有 .git 的,配好上面的地址后可在同一页执行「预览并接入」: 预览在临时仓库里 fetch,先列出会被目标版本替换的本机文件与会保留的运维者内容;确认后目录接入 成为 git 工作树(git init + 添加 origin + 落地目标分支),被替换的文件全部留底在 .update-backup/<时间戳>/overwritten/,运维者内容照旧先暂存再放回,随后重编译二进制。 接入后它就是正常安装,一键更新与回滚都可用了;注意接入前没有 git 历史,因此「接入」本身没有可回滚的 上一提交,第一个回滚点由接入后的第一次更新写下。

四种拒绝场景

场景 reason 含义与处理
本地改过产品源码(不在 protected 清单里的路径) local_source_edits 一键更新是"下载",不是"替你决定冲突"。点名文件,先提交或还原再重试
分支与远端分叉(本地既领先又落后) diverged 那是合并决策,给出 ahead/behind 数字;手工处理后再点
本机没有 Go 工具链 no_toolchain 源码照样更新,只有二进制没换;装好 go 再点一次即可补上
这个目录不是 git 工作树 not_a_repo(状态里 installed: false) tarball 安装的目录没有可快进的远端,按发布包方式更新

apply 是异步的,所以它总是先回 202,上表的拒绝出现在轮询到的 job.failure(reason/message/items)里; rollback 是同步的,拒绝直接落到状态码(local_source_edits、diverged、no_state、moved_since_update、 no_binary → 409,其余 → 400)。其他失败(merge_failed、build_failed、swap_failed)带着 git/go 的原始输出; 编译失败时二进制保持原版本,而源码已移动、状态文件已写好,因此仍然可以回滚。

运维者的内容如何被保留

roles/ skills/ tools/ agents/ knowledge_base/ data/ log/ venv/ config.yaml .env 是运维者的内容, 判定在 update.Protected。合并前,只有本次更新确实会写到的那些路径被复制到 .update-backup/<时间戳>/ 并从工作树移开(你多出来的、上游没动的目录连碰都不碰),合并后再原样放回, 于是"更新代码、留下我的工作"成立。结果里的 keptContent 逐个点名被保留的文件——绝不静默丢弃: 当上游与你在同一个文件上都有改动,落地的是你的版本,而这件事写在结果里。

回滚

./cyberstrike-ai -update-rollback 或页面按钮:git reset --hard 回到那次更新前的提交, 并把换二进制时留下的 cyberstrike-ai.prev 放回原位,随后删除 .update-state.json。 它会拒绝的情形:没有更新记录(no_state)、没有留下旧二进制(no_binary)、记录里的提交在本仓库不存在 (bad_state)、HEAD 自那次更新之后又动过(moved_since_update:回滚只该撤到更新前,不该顺手抹掉之后的工作)、 以及存在本地源码改动(local_source_edits)。

重启的两种情形

进程只有在请求显式带 restart: true、且启动时装配了重启钩子时才会优雅退出(Shutdown 后退出码 0)。 能不能被拉起来取决于外部守护(systemd、run.sh),页面如实这么写,而不是承诺一次可能不发生的启动; 没装配钩子时要求重启是 400,而不是先停掉服务再自称重启了。未重启时接口只报告 needsRestart,旧二进制继续跑。

数据库

默认 SQLite。新增表或字段时:

  • 将迁移逻辑放到数据库初始化或对应模块迁移函数。
  • 保持向后兼容,避免破坏已有 data/conversations.db。
  • 添加针对迁移和核心查询的单测。

新增工具

命令工具优先通过 tools/*.yaml 增加,不必改 Go 代码。需要 Go 内置工具时:

  • 在合适模块注册 MCP Tool。
  • 定义清晰 InputSchema。
  • 处理超时、错误、审计和 HITL 上下文。
  • 避免把高风险操作默认免审批。

工具 YAML 规则见 tools/README.md。

新增角色

角色通过 roles/*.yaml 管理。常见字段包括名称、描述、系统提示词和工具列表。角色应遵循最小工具集原则,不要把所有工具默认交给专用角色。

新增子代理

多代理子 Agent 放在 agents/*.md。Front matter 示例:

---
name: Vulnerability Triage
id: vulnerability-triage
description: 对漏洞线索进行验证、定级和修复建议整理
tools:
  - nmap
  - nuclei
bind_role: 综合漏洞扫描
max_iterations: 200
---

正文是系统提示词。主代理可使用固定文件名或 kind: orchestrator。

新增 Skill

Skill 放在 skills/<name>/SKILL.md。用于提供专题能力、流程说明或附属资料。详见 Skills 指南。

前端开发

前端代码按功能拆分在 web/static/js/。新增页面或模块时:

  • 复用现有 apiFetch、modal、通知、i18n 工具。
  • 同步更新 web/static/i18n/zh-CN.json 和 en-US.json。
  • 避免把敏感 Key 放到前端。
  • 高风险按钮要有确认和清晰状态反馈。

i18n 规范见 前端国际化方案。

OpenAPI

internal/handler/openapi.go 维护内置 OpenAPI 输出。新增公开接口后建议同步补:

  • path
  • method
  • summary/description
  • requestBody
  • responses
  • security

这样 /api-docs 才能反映最新接口。

开发习惯

  • 优先保持现有模块边界。
  • 大模型、外部 API、文件系统、Shell 相关改动必须考虑超时和错误路径。
  • 高风险能力要接入 HITL 或至少有清晰审计。
  • 代码变更后运行相关包单测。

新增业务模块的完整配方

不要只加一个 Handler。完整模块通常要考虑:

  1. 数据模型:是否需要 SQLite 表和迁移。
  2. Handler:HTTP 参数、错误码、分页、过滤。
  3. Audit:管理动作是否要审计。
  4. Monitor:如果会执行长任务,是否要记录执行状态。
  5. MCP:是否要暴露给 Agent。
  6. HITL:MCP 工具是否有审批边界。
  7. OpenAPI:是否更新 /api/openapi/spec。
  8. Frontend:是否需要 i18n、状态、空态、错误提示。
  9. Tests:数据库、handler、边界条件。
  10. Docs:配置、使用、排错和安全影响。

少做其中一项,后面通常会以“用户看不懂”“Agent 调错”“接口没人会用”的形式返工。

Handler 错误设计

建议错误响应保持:

{
  "error": "machine_readable_code",
  "message": "给用户看的说明"
}

不要只返回 Go error 字符串。前端需要稳定字段,用户需要可操作建议,日志需要详细错误。

长任务设计

扫描、索引、批量任务、C2 等都可能长时间运行。设计时要回答:

  • 是否能取消?
  • 是否能查询进度?
  • 失败后能否重试?
  • 结果写在哪里?
  • 页面刷新后状态是否还在?
  • 是否会阻塞 HTTP 请求?

如果答案是否定的,应考虑接入任务表、事件流或监控模块。

测试优先级

最值得补测试的地方:

  • 配置热应用。
  • HITL 审批分支。
  • Shell 超时和无输出。
  • 外部 MCP 失败恢复。
  • 知识库索引和检索后处理。
  • WebShell 编码和系统识别。
  • SQLite 迁移兼容。

这些地方比普通 getter/setter 更容易出现真实用户故障。