Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

espctl deposit — 把数据主权工作流变成“默认行为“

让每一次成功 build 自动变成签名三元组


这一页是什么

姊妹页《制作者数据资产存证操作指南》讲的是手工工作流——目录结构、manifest.yaml 模板、Zenodo + OpenTimestamps + minisign 的仪式步骤。它准确、可复用、可照抄。

但它也意味着六项纪律,制作者得在每一次 build 后都自觉去做、做完几年。操作指南里写得很直白:纪律会在典型项目走到中后期就衰减,到第 2 年资产目录就会散乱。

espctl deposit 就是把这个衰减点掉的工具。它把操作指南里的步骤自动化,让:

  • 每一次成功的 espctl build 都可以通过一条命令变成签名、可存证的三元组:espctl deposit add <build_id>
  • AI agent(Claude Code、Cursor,任何说 MCP 的客户端)可以在每次 build 成功后自动调用 deposit.add,只问制作者三个问题(结果、证据、备注),并且永远不会在没有显式确认的情况下把数据推到 Zenodo。
  • 未来的买家可以用纯 minisign -Vm && ots verify,在没有装 espctl 的干净 Docker 容器里独立验证任意导出包。

这是把“主权当作家庭作业“翻译成“主权当作默认行为“。强烈建议先读操作指南理解为什么这件事重要,这一页讲的是工具怎么把摩擦消掉。


你拿到的东西

一套 CLI、一套 MCP 镜像、一套 schema。

表面命令 / 工具用来干什么
CLI (espctl deposit <sub>)initaddlistverifymilestonesignattestexport手动在终端里跑
MCP (deposit.*)8 个工具,与 CLI 1:1 镜像AI agent 在每次 build 成功后自动调用
Schemamanifest.yaml v1 + deposit.toml + INDEX.toml + 自验证脚本跨版本稳定;2031 年的买家工具应该能解析 2026 年的归档

CLI 和 MCP 走同一套编排器——两个表面行为不可能漂移。

所有本地操作都离线工作。唯一会访问公网的命令是 attest(Zenodo + OTS),并且必须有显式确认。


CLI 命令参考

8 个子命令分布在 4 个发行版里。首个版本(init/add/list/verify)就是独立的旗舰版本,单独发布就已经覆盖了约 90% 的价值——即使后续版本永远不发布,espctl deposit 也已经能用。

espctl deposit init — 初始化工作区(v0.6.0 起稳定)

创建 ~/maker-assets/{public,private,asset}/、写入默认 deposit.toml、探测 PATH 上的 minisign,首次运行时提示注册公钥指纹。幂等:第二次执行不会修改任何文件。

espctl deposit init                        # 全用默认值
espctl deposit init --root /data/maker     # 自定义工作区根目录
espctl deposit init --contributor-alias myhandle

init 永远不会改你的 ~/.gitconfig、也不会动你已有的 SSH key。如果 minisign 没装,会立刻 fail-fast 并给出对应平台的安装提示。

espctl deposit add <build_id> — 把一次 build 转为签名三元组(v0.6.0 起稳定)

这是工具的心脏。给一个成功 espctl buildbuild_id,这条命令会:

  1. espctl-core 读 build 上下文(芯片、IDF 版本、源码 hash、二进制 hash、size、target)。
  2. 自动填充能推导出来的 manifest.yaml 字段:时间戳、贡献者指纹、硬件段、固件段。
  3. 提示(或通过参数接收)需要人判断的三个字段:
    • --outcome passed|failed|partial
    • --verification-evidence <path>(可重复,至少一个)
    • --notes <string>(可以很短;一行就够)
  4. 解析 slug,如果同日同名冲突,自动加 -vN 后缀。
  5. 写出三元组目录、用 minisign 签 manifest.yaml、更新 INDEX.toml
# 交互式(缺字段时通过 prompt 询问)
espctl deposit add abc123

# 非交互式(CI / 脚本)
espctl deposit add abc123 \
  --slug crsf-parser \
  --outcome passed \
  --verification-evidence verification/monitor-capture.log \
  --verification-evidence verification/oscilloscope-ch9.png \
  --notes "16 通道跟杆灵敏;CRC 失败率 < 0.1%/60s"

outcome: failed 的三元组是一等公民。失败数据的训练价值很高——别删。

espctl deposit list — 查询你的三元组(v0.6.0 起稳定)

只读;永远不会修改磁盘。可以按日期范围、outcome、签名 / 存证状态过滤:

espctl deposit list                                  # 所有三元组
espctl deposit list --since 2026-04-01 --outcome passed
espctl deposit list --unsigned --json                # JSON 输出,方便管道
espctl deposit list --unattested                     # 还未做 milestone 存证的

espctl deposit verify [slug | --all] — 完整性校验(v0.6.0 起稳定)

重算证据文件 hash、重新验 manifest.yaml 的 minisign 签名、检查 INDEX 一致性。退出码 = 失败三元组数(0 = 全部通过):

espctl deposit verify                               # 当前目录的三元组
espctl deposit verify crsf-parser-v2                # 指名 slug
espctl deposit verify --all                         # 遍历 asset/triples/
espctl deposit verify --all --strict                # 额外要求必须已 milestone 存证

证据、签名或指纹的任何一字节翻转都会产生 InvalidSignature —— 篡改检测是协议的一部分。

espctl deposit milestone <name> — 离线打包(v0.6.1 起稳定)

把 N 个三元组打包成确定性的 <name>.tar.gz,附带 metadata.json(Zenodo 草稿就绪)和 minisign 签名。纯离线 —— 不会联系任何外部服务。

espctl deposit milestone 2026-05                    # 2026 年 5 月的所有三元组
espctl deposit milestone phase-3 --slugs slugs.txt  # 显式给 slug 列表
espctl deposit milestone 2026-Q2 --include-failed   # 把 outcome:failed 也打进来

打包是可重现的:同样输入两次会产生完全相同的 tarball 字节流。

espctl deposit sign <path> — 通用 minisign 包装器(v0.6.1 起稳定)

minisign -Sm / -Vm 之上的薄便利层。用来签三元组生命周期之外的任何东西(发行说明、blog 草稿、临时文件):

espctl deposit sign milestone-2026-05.tar.gz       # 产出 .minisig
espctl deposit sign milestone-2026-05.tar.gz --verify

espctl deposit attest <milestone> — Zenodo + OTS(v0.7.0 起稳定)

**全局唯一的联网命令。**把 milestone 包上传到 Zenodo(拿 DOI)、用 OpenTimestamps 打时间戳(拿到 Bitcoin 锚定的 .ots),并把两份回执写进 asset/deposits/

Zenodo 发布不可逆 —— DOI 是永久的。这条命令在没有显式确认时会拒绝执行:

espctl deposit attest 2026-05 --confirm             # Zenodo + OTS 都做
espctl deposit attest 2026-05 --ots-only            # 只 OTS(不需要确认,因为不是不可逆)
espctl deposit attest 2026-05 --zenodo-only --confirm

没加 --confirm 会以 ConsentRequired 退出。没有任何环境变量、配置项、agent 设置可以绕开这道闸。工具的每一层都在强制它。

espctl deposit export <slug | --slugs FILE> — 给买家的可验证包(v0.7.0 起稳定)

构造一个自包含export.tar.gz,里面打包三元组、minisign 公钥、Zenodo DOI 回执、OTS 时间戳、以及一份 VERIFY.md 说明文档——让买家不需要装 espctl 就能完成验证。包里附一份能在干净 Docker 容器里跑的 shell 脚本:

espctl deposit export crsf-parser-v2 --out crsf-parser-v2-export.tar.gz
espctl deposit export --slugs license-batch.txt --out 2026-Q2-license-bundle.tar.gz
espctl deposit export crsf-parser-v2 --redact-keys --out preview.tar.gz   # 买家先看后签前的预审版

--redact-keys 会去掉制作者的标识 handle,做“先看后签“的远距预审。密码学签名和 Zenodo DOI 保留——买家仍能验出处,只是看不到身份。


MCP 工具表面

每个 CLI 子命令在 deposit.* 命名空间下都有一个 1:1 的 MCP 镜像。AI agent(Claude Code、Cursor、任何说 MCP 的客户端)直接调它们。CLI 和 MCP 共享同一套编排器——表面之间不可能漂移。

工具权限类别返回
deposit.init写(本地){ root, created_new, signing_key_existing }
deposit.add写(本地){ slug, signed }NeedsMakerInput 信封
deposit.list只读[ TripleRecord ]
deposit.verify只读[ VerifyReport ]
deposit.milestone写(本地){ name, triple_count, tarball_path, sha256 }
deposit.sign写(本地){ signed }{ verified }
deposit.attest写(不可逆,联网){ doi?, ots_stamp? }ConsentRequiredError
deposit.export写(本地){ output_path, sha256 }

Agent 权限策略应当区分对待这三类:只读永远放行;本地写每次询问;deposit.attest 必须每一次都人工确认。同意闸做在工具层,无论 agent 自己的本地策略允许什么。

三问 Agent 流程

agent 用 auto_fill: truedeposit.add 时,会得到两种回应之一:

  1. Ok { slug, signed } —— agent 有足够上下文(比如来自会话 prompt 历史)填齐了所有必填字段。三元组已签。完成。
  2. NeedsMakerInput { fields: [outcome, evidence_paths, notes], prompt: "..." } —— agent 把恰好三个问题转给制作者,拿到答复后重新调一次 deposit.add

这就是杠杆点。制作者敲三个短答案,剩下的全由 agent 做。字段列表恰好是三个,且这是协议的一部分——任何 agent / 模型都不能扩展它。

Agent 绝不会自动做的事

  • 在没有 explicit_consent: true 的情况下推到 Zenodo。这道闸在工具服务端、不在 agent 的 prompt 里。
  • 凭空合成验证结论。如果 agent 没有 ground-truth 证据,必须调 NeedsMakerInput,不能猜 outcome: passed
  • 修改你的 ~/.gitconfig 或 SSH keydeposit init 自带这条不变量;测试套件强制它。

一次典型会话

制作者跑完一次 build,看了 monitor 输出,决定存证。Claude Code agent 在跑:

maker: build 看着没问题——通道跟杆、CRC 稳定。存了吧。

agent: [调 deposit.add build_id=abc123 auto_fill=true]
       → NeedsMakerInput { fields: [outcome, evidence_paths, notes] }

agent: 我需要三件事才能完成三元组:
       1. 结果:passed / failed / partial?
       2. 这次 build 哪些文件作为验证证据?
       3. manifest 的一行备注。

maker: passed;monitor-capture.log 和 oscilloscope-ch9.png;
       16 通道跟杆,CRC < 0.1%/60s。

agent: [带齐三个字段重新调 deposit.add]
       → Ok { slug: "2026-05-15-crsf-parser-v2", signed: true }

agent: 完成。三元组已签;INDEX 已更新。本月你有 23 个未存证三元组——
       想打包时说 "milestone 2026-05"。

端到端钟表时间约 30 秒——大部分时间是制作者敲那三个回答。


配置

~/.config/espctl/deposit.toml 控制默认值:

[deposit]
root                           = "~/maker-assets"
default_contributor_alias      = "myhandle"
auto_suggest                   = true       # build 成功后打印 "提示:deposit add <id>"

[deposit.signing]
key_path                       = "~/.ssh/id_ed25519"
minisign_pubkey_path           = "~/.config/espctl/minisign.pub"

[deposit.zenodo]
api_token_env                  = "ZENODO_API_TOKEN"
community                      = ""                          # 可选 Zenodo community
default_license                = "all-rights-reserved"

[deposit.ots]
calendars                      = ["https://alice.btc.calendar.opentimestamps.org", "https://bob.btc.calendar.opentimestamps.org"]

[deposit.defaults]
include_failed_in_milestone    = true
require_signature_on_add       = true

顶层未知字段会被容忍(以便未来版本添加新设置不会弄坏你的配置)。段名与已存在字段名跨所有 0.x 版本保持稳定。


故意不在范围内的事

espctl deposit主权工具,不是市场。下列是有据可查的非目标——不会在后续版本里加,这是写在测试和 CI 里的架构承诺:

  • 不做数据集交易、撮合。没有挂牌、没有托管、没有分润、没有价格发现。key 在你手里;谈判你自己谈。esphome.cloud 不抽任何制作者-买家交易的百分比。
  • 不集中托管资产层。三元组永远不离开你的机器,除非是你亲自跑的命令并附完整回执。esphome.cloud 服务器不存你的 asset 目录。
  • 不打质量分。工具不评断你的三元组“比另一份值钱“。这交给买家和参考价决定,不是工具。
  • 任何 deposit-* crate 都不上传分析或遥测。1,000 次调用的审计必须显示对 esphome.cloud 域名的 HTTP 请求。CI 用 cargo deny + grep 强制这条。
  • 不用 GPG。SSH + minisign 是唯一签名路径。(操作指南 §4 解释了为什么。)
  • 不把 IPFS 作为主存证 Zenodo + OTS 双存证。IPFS 钉是不持久的、扛不住制作者疏忽。
  • 永远不会自动推到 Zenodo,每一层都要显式确认。DOI 是永久的;闸是结构性的。
  • 不在资产层内部做 RAG / 向量库索引。向量存储是消费层产物,不能写进 asset/triples/。无论你是否在跑 embedder,deposit add 都正常工作。

这张清单不是建议 —— 它是架构栅栏。如果未来某个版本加了上面任何一项,那个版本就不是 espctl deposit,是另一个名字下的另一个产品。


发布路线

版本主题新增表面状态
v0.6.0核心 CLIinitaddlistverify + manifest.yaml v1 schema独立旗舰。约 90% 的价值在这里落地。
v0.6.1milestone + signmilestonesign离线打包。
v0.7.0attest + exportattest(Zenodo + OTS)、export(买家包)唯一联网版本。
v0.8.0MCP 镜像8 个 deposit.* 工具 + agent 流程启用三问 agent 工作流。
v0.9.0生态钩子espctl build 成功提示、espctl monitor 归档可选。2026-05-21 形式上跳过;如使用数据证明值得,可再入。
v1.0.0SemVer 承诺全面生效v0.8.0 在真实制作者工作流跑过 ≥ 3 个月之后。

v0.6.0 独立可发的约束是结构性的:即便后续版本永不发布,v0.6.0 本身就是一个完整的主权工具。后续版本是叠加,不是必需。


不装 espctl 也能验出处

这是 2031 年买家在意的那道题:

# 买家展开一份由 espctl deposit export 导出的包
tar xzf 2026-Q2-license-bundle.tar.gz
cd 2026-Q2-license-bundle/

# 买家读 VERIFY.md,然后在干净 Docker 容器里跑包内附带的脚本
docker run --rm -v "$PWD:/work" -w /work alpine:latest sh ./verify.sh

# verify.sh 会在容器里装好 minisign 和 ots,然后执行:
#   minisign -Vm manifest.yaml -p contributor.pub
#   ots verify milestone-2026-Q2.tar.gz.ots
# 两者都必须以 0 退出。

如果两项都通过,买家在密码学层面证明了:

  1. 三元组由贡献者的公钥(离线由制作者持有)签发。
  2. milestone 包在某个 Bitcoin 区块之前就已存在(因此在某个日期之前已存在)。
  3. Zenodo DOI(也在包内)是当时作者身份的公开记录主张。

三条独立的账本,任何一个陌生人在多年之后、从干净容器里都能验。

这才是真正的产品。


另见


esphome.cloud / Aegis
2026 年 5 月