尚未检测到更新。GitHub 发布版本后会显示在这里。
制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗ANP(Agent Network Protocol)消息层的一套自建实现:一个可部署的消息服务 AnpioAgent、
它依赖的协议原语库,以及一个 Rust 命令行客户端 anpio-cli。
| 目录 | 产物 | 角色 |
|---|---|---|
packages/core | @anpio-cli/core | 协议原语:DID 组合、RFC 9421 签名与验签、密钥解析、CAIP-10 身份、JCS 规范化 |
packages/service | anpio-service(AnpioAgent) | ANPMessageService:接收、存储、投递、取件,群组、附件与联邦 |
rust/ | anpio-cli | 命令行客户端:身份、登录、消息、群组与群端到端加密 |
core 不含传输与进程,service 建在它上面。客户端是独立产物:有自己的 store 根
($ANPIOCLI_HOME,缺省 ~/.anpio-cli),不读也不写服务端的数据库。
客户端用 Rust 写,是因为群 E2EE 需要 MLS,而官方 MLS 实现只在 Rust crate anp 里
(client-mls.md §2)。它依赖 crates.io 的 anp,不是把本仓库
发布成 anp;crates.io 上另有一个同名的 anpio-cli,与本仓库不是同一份代码。
ANP 里有两个可以被"自己搭一个"的服务,本仓库做的是第二个:
did.json、发号(handle)。独立部署,不属于本仓库。
anpio-cli 是它的客户端(did create / did register / did publish /
did resolve / did update)。AnpioAgent 就是它。两者分开部署,分工是清楚的:DID Service 只管身份,本服务承载消息。所以 AnpioAgent
不托管别人的 DID 文档,只在需要时解析对方的文档,从中取唯一的 ANPMessageService
条目来找到入口服务。一处例外:本服务自己名下的群 DID 文档由本服务发布——群文档是
消息层的产物(这个群住在哪台 Host 上、名册与事件序在哪儿),交给别人托管等于让别的服务器
替我们签一份关于我们的声明(groups.md §2)。
需要 Node.js ≥ 24.16 与 Rust ≥ 1.88。
npm ci
npm run build
# 起一个消息服务;配置全部走环境变量,没有命令行旗标
ANPIOCLI_LISTEN=127.0.0.1:8787 node packages/service/dist/main.js
另开一个终端,用一个不需要身份的方法确认它在说 ANP(anp.get_capabilities 是 P1 §8.2
唯一免认证的方法):
cargo run --manifest-path rust/Cargo.toml -- discover http://127.0.0.1:8787
完整的本机链路——玩具 DID 主机、真 packages/service、真 anpio-cli 二进制——由 e2e 脚本
一次跑完。它覆盖身份、消息与通讯录、群、P6、登录挑战与换钥:
./scripts/e2e.sh # 先构建再跑
./scripts/e2e.sh --no-build # 用现成产物
要接自己的 DID 主机,先建身份,再收发:
cargo build --manifest-path rust/Cargo.toml
rust/target/debug/anpio-cli did create --host http://127.0.0.1:8765 --handle alice
rust/target/debug/anpio-cli msg send --to did:wba:... --text "hello"
rust/target/debug/anpio-cli msg inbox
msg send 返回成功表示消息已进入对方入口服务的处理边界,不表示对方已读。msg inbox
读的是本机记录,不是服务端;要联网把服务端还留着的消息搬下来,用 msg fetch。
anpio-cli 是二进制,不是 npm 包。不要 cargo install anpio-cli(crates.io 上那个是
别的项目);要从源码装就 cargo install --path rust/crates/anpio-cli。
anpio-key 里默认由 did create 把私钥写进 store。另一条路是交给本地
anpio-key 的受保护服务:钥匙由服务生成并
只留在服务里,身份目录记的是钥匙名(kid)和 grant id。
# 管理员在自己的终端,一次性:把身份 provision 进受保护服务
anpio-cli did create --host https://awiki.me --handle mybot --key anpio-key
# 身份记录丢了(换机器、清了 profile)时,用 attach 恢复
anpio-cli did attach --did <did> --config /Library/ANPIOKey/service.json \
--kid <agent>/<profile>-sign --grant http=<grant-id> --agent mybot
身份文件里一个私钥字节都没有,只记钥匙名和 grant;每次签名由 CLI 请守护进程签。
非管理员终端、非交互执行时 --key anpio-key 直接拒绝,不会偷偷改用本机钥匙。
受保护入口还能代签 origin proof,并在绑定设备名后用 SIGN_PURPOSE(device_assertion)
签群 E2EE 的设备断言。did attach 是恢复入口(旧命令 did attach-anpio-key 仍可解析)。
细节与拒绝边界见 rust/README.md
和 anpio-key-integration.md。
受保护的签名口只认登记的 Agent 账号。进程跑在管理员自己账号下(单用户机器、桌面
启动的编辑器或 coding agent)时,管理员为这个 DID 签一张能力令牌,did attach
记下它:
# 管理员终端;明文只写一次,到 0600 文件
anpio-key admin --config /Library/ANPIOKey/service.json capability mint \
--uid 601 --did <did> --label mybot --out /tmp/mybot.capability
anpio-cli did attach --did <did> --config /Library/ANPIOKey/service.json \
--kid <agent>/<profile>-sign --grant http=<grant-id> \
--capability-file /tmp/mybot.capability --agent mybot
--capability-file 是叠加不是替代:令牌说"以哪个主体、哪个 DID 行事",grant 仍说
"能签什么、多久、几次"。attach 会校验令牌形状(anp1_ + 43 字符 base64url、0600、
属调用方、普通文件),并比对服务端回报的 capability_did 与要 attach 的 DID;对不上
直接拒绝,不写任何东西。之后令牌被复制到身份目录的 capability(0600),/tmp 那份
可以删。
CAPABILITY_SCOPE_DENIED。CAPABILITY_REQUIRED / CAPABILITY_INVALID /
CAPABILITY_EXPIRED,都不重试。unlock 后跑一次
anpio-cli did refresh --uid 601 即可,不必重签令牌。did create --key anpio-key 尚未自动签发能力令牌;先 provision,再由管理员 mint 并
attach。完整语义见 anpio-key 文档站的能力令牌。
使用者文档(中文在 /,英文在 /en/):https://anpio-cli.vercel.app
源文件在 vercel/,是一个自包含的 VitePress 站点;Vercel 项目的 Root Directory
指向 vercel/。本地预览与构建:
npm run docs:dev
npm run docs:build
实现者文档:docs/design/。逐条引规范行号,记录取舍与偏离,以及哪些
规范条款本实现故意不覆盖。改代码前先读对应的那一篇,索引见 docs/index.md。
还剩什么:TODO.md。只记仍然开着的事——做完的划掉,不复述。
.github/workflows/ci.yml 在每次 push 到 main 和每个 PR 上跑三个独立 job:
| job | 跑什么 |
|---|---|
check | npm ci → npm run build → npm run typecheck → npm test |
rust | cargo test --all-targets,然后 cargo clippy --all-targets -- -D warnings |
e2e | ./scripts/e2e.sh:真服务、真 DID 主机、真 socket、真 WebSocket |
本地跑同一套:
npm ci && npm run build && npm run typecheck && npm test
cargo test --manifest-path rust/Cargo.toml --all-targets
cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
./scripts/e2e.sh
npm run build 不能省。 每个包的测试按包名 import @anpio-cli/core,而那个名字解析到
packages/core/dist——空检出直接跑 npm test 会红得像测试坏了,其实是缺构建。
引用行号以 ANP 已发布的 v1.2 为准。一处例外需要记住:P6(群组端到端加密)仍是候选,
它的 anp.group.e2ee.v2 卡在"注册一个稳定的 MLS ExtensionType code point"这道发布门槛
上——所以本实现照做 P6 的方法,但不把它声明为稳定 Profile。P6 的命令仍然可用,
anp.get_capabilities 只是扣留那一条广告(见 TODO.md §3.1)。
MIT。