docs: tighten wording, soften node capability claims, link architecture to code
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/e2e Pipeline was successful

- Remove the node capability paragraph from README; soften "pure collector"
  and "no self-update" statements in docs, AGENTS.md and READMEs
- architecture.md: drop scalability goals, design decisions and open items;
  add a component-to-code table and code links
- Replace rhetorical contrasts in security docs with direct statements
- AGENTS.md: add project mental model, change map and writing style rules
This commit is contained in:
justme committed 2026-10-21 06:52:01 +08:00
1 parent a9bf849697
commit d5ebf29e57
11 files changed
+89 -100

No files matched your search

+6 -6
View File
@@ -63,7 +63,7 @@
- 一个能力只在一个地方声明:`Capability` 常量(`MoShiro.Backend.Application`),端点用 `.Requires(Capability.X)`;`Principal.Has(capability)` 是唯一的判断入口,会话和 API key 都走它。写蕴含读(`fleet.write` 带 `fleet.read`)。
- 对账测试:所有 `/api/admin` 端点必须声明能力,没声明测试就失败;OpenAPI 的每个操作带 `x-moshiro-capability`。
- 信任类是界面上特别标出的(吊销、账户管理),授权和其他能力一样靠 API key 或会话。真正由 passkey 签名保护的是操作员集合,那是节点自己验证的。
- 信任类是界面上特别标出的(吊销、账户管理),授权和其他能力一样靠 API key 或会话。passkey 签名保护的是操作员集合,由节点自己验证。
- 不能越权:创建 API key 时勾选的能力必须是创建者当前拥有能力的子集。
- Backend 更新确认:更新清单由 CI 的发布密钥签名(面板登记时验签),`seq` 防重放;确认只是让 Backend 有序退出并备份数据库,拉哪个镜像由 Quadlet 单元里写死的仓库和标签决定,面板决定不了。
@@ -74,7 +74,7 @@
| 密码 | PBKDF2;认领向导创建第一个管理员时设置 |
| passkey | WebAuthn,只支持 ES256,attestation 一律当 `none`;可以登录,也能给操作员集合签名(节点验证) |
- 两种方式的会话拿同样的能力。passkey 不是 Backend 用来拦操作的一道门,只用在节点自己会验证的事上,见第 5 节。
- 两种方式的会话拿同样的能力。passkey 只用在节点自己会验证的事上,见第 5 节。
- 认领向导(没有默认账户):面板里还没有任何用户时,打开 `/admin/` 进向导——用后端日志里的认领码(或 `MoShiro__ClaimCode`)创建第一个管理员(用户名和密码自己填),注册第一个 passkey,再用它给操作员集合签名建立信任根。谁先打开页面谁就能当管理员,所以要认领码;创建完码作废、入口关闭(`POST /api/admin/auth/setup` 之后返回 409)。后两步是引导不是门槛,可以稍后补;不注册 passkey、不建立信任根,就添加不了 Agent 和 Probe,只能进面板做基础管理,这是架构如此。之后新增的 passkey 不会自动成为操作员:要由现有操作员在信任页看清「新增了哪些」再签新版本,节点才认。
- 建议每个账户至少注册两个 passkey(手机加硬件密钥)。
@@ -89,7 +89,7 @@
## 5. passkey 用在哪里(没有提权窗口)
原则:passkey 不是 Backend 用来拦操作的一道门(Backend 被攻破时,它自己做的拦截也会被一起绕过),而是节点或密钥持有者需要它才能验证、解密的东西。
原则:passkey 只用在节点或密钥持有者自己验证、解密的场合。Backend 自己做的拦截在 Backend 被攻破时一起失效,所以不用 passkey 拦操作。
| 事 | 谁验证 | 要 passkey |
| --- | --- | --- |
@@ -141,18 +141,18 @@ extension(web ssh、文件、抓包,见 [security.md](security.md))现在
已经实现的部分
- 属主 `controllers` 是一组 passkey 凭证 id,必须是操作员集合里的凭证。由安装命令带来,节点注册时写进 `trust.json`,Backend 之后改不了。
- 每个 Agent 和 Probe 都必须有属主,至少一把——而且这是节点自己的要求,不是 Backend 的要求:节点启动时检查本地固定的属主,没有(或者属主的 passkey 已经不在操作员集合里)就不运行(共用 `Fleet.Hosting` 里的 `OwnerGuard`;配置 `fleet.require_owner`,`MOSHIRO_ALLOW_NO_OWNER=1` 放行)。面板被攻破也改不了。
- 每个 Agent 和 Probe 都必须有属主,至少一把。这由节点自己强制:节点启动时检查本地固定的属主,没有(或者属主的 passkey 已经不在操作员集合里)就不运行(共用 `Fleet.Hosting` 里的 `OwnerGuard`;配置 `fleet.require_owner`,`MOSHIRO_ALLOW_NO_OWNER=1` 放行)。面板被攻破也改不了。
- 添加节点时选属主:`POST /api/admin/join-tokens` 的 `controllers: [credential_id…]`,不给就是操作员集合里的全部凭证;给了就必须非空且都在集合里。还没有信任根或一把属主都没有时,面板提前拒绝创建加入码(400,字段 `controllers`),这只是尽早失败,真正的强制在节点;`MoShiro__RequireOwner=false` 可以关掉这个提前检查(只给没有信任根的老部署)。界面里是必选的「属主(passkey)」多选列表,只是选,不用触碰。
- 安装端也强制:`install.sh`、`install.ps1`、容器入口缺了 `MOSHIRO_GENESIS` 或 `MOSHIRO_CONTROLLERS` 就不装;`fleet-enroll --genesis` 不带 `--controllers` 也拒绝。
- 逆推出绑定关系:节点握手时把本地固定的属主(passkey 凭证 id)报给面板(hello 的 `owners`),面板顺着凭证 id 反查出它绑定在哪个账户的哪把 passkey 上,显示在「系统 → 版本」的「属主」列(对不上任何已注册 passkey 的显示「未知凭证」)。上报只是自述,真正的授权由节点自己校验 passkey 签名。
预留的设计(还没做)
- 谁来强制:节点本地,不是 Backend。 web ssh 这类会话要不要加密、要不要验证,由 Agent 和 Probe 自己要求。让 Backend 决定,被攻破的 Backend 一句「这次不用加密」就绕过去了。节点本地强制:没有操作员的现场证明不启动会话;会话密钥只和证明里的那把密钥协商,不和 Backend 转来的公钥协商;加密字段缺失就拒绝,不降级;能开哪些 extension、哪些 passkey 能授权,只看本节点属主。
- 谁来强制:节点本地。web ssh 这类会话要不要加密、要不要验证,由 Agent 和 Probe 自己要求。让 Backend 决定,被攻破的 Backend 一句「这次不用加密」就绕过去了。节点本地强制:没有操作员的现场证明不启动会话;会话密钥只和证明里的那把密钥协商,不和 Backend 转来的公钥协商;加密字段缺失就拒绝,不降级;能开哪些 extension、哪些 passkey 能授权,只看本节点属主。
- 证明的方向是反的:节点先出题(nonce 加它的临时公钥,用节点身份密钥签名),属主的浏览器把 nonce 和自己的临时公钥放进 passkey 断言的挑战里;节点验断言来自本节点属主、要求用户验证、nonce 是自己刚出的且没用过。Backend 仍然管谁有资格发起请求、审计、限流、转发密文。
- 交互式会话(终端、ssh)用现场签名的临时 ECDH 密钥协商,passkey 只签名授权,不参与解密,所以不支持 PRF 的 passkey 也能用,还有前向保密;之后双向是带序号的 AEAD。静态数据(抓包文件、会话录像)需要事后解密,才用属主记录里的接收者公钥加密(来自 WebAuthn PRF 或浏览器里不可导出的密钥)。
- 删除和使用分开:admin 能删节点、卸载 extension,读不到也用不了。改属主要现有属主的 passkey 签名(`ControllerUpdate`),账户被删、角色被改都改不了某个节点的属主。属主是 passkey 凭证,不是账户;一个人丢了全部属主 passkey,这个节点上的 extension 就没人能用了,所以添加时建议绑两把以上。
- 数据边界:监测数据(延迟、丢包、吞吐、事件、主机资源汇总)是明文,Backend 要做分析和展示;extension 会话和原始 payload 端到端加密,Backend 只存转发密文。读取权不是能力,是私钥。
- 数据边界:监测数据(延迟、丢包、吞吐、事件、主机资源汇总)是明文,Backend 要做分析和展示;extension 会话和原始 payload 端到端加密,Backend 只存转发密文。读取权由私钥决定。
## 10. 安装的供应链