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 — Automating the Maker Data Sovereignty Workflow

The tool that turns every successful build into a signed triple, by default


What this page is

The companion page Deposit Your Maker Dataset describes the manual workflow — directory layout, manifest.yaml schema, Zenodo + OpenTimestamps + minisign rituals — that every serious maker should run. It is operational, accurate, and repeatable.

It is also six disciplines the maker has to remember, build after build, year after year. The operations guide notes the predictable failure mode: discipline reliably decays well into a typical project, and by year 2 the asset directory becomes scattered.

espctl deposit is the tool that collapses that decay. It automates the operations-guide workflow so that:

  • Every successful espctl build can become a signed, attestable triple via one command: espctl deposit add <build_id>.
  • An AI agent (Claude Code, Cursor, anything speaking MCP) can auto-call deposit.add after each build, surfacing only three questions to the maker (outcome, evidence, notes) — never publishing to Zenodo without explicit consent.
  • A future buyer can verify any exported bundle with plain minisign -Vm && ots verify in a Docker container with no espctl installed.

This is sovereignty translated from “homework” to “default behavior.” Read the operations guide first to understand why the discipline matters; this page is how the tool removes the friction.


What you get

One CLI surface, one MCP mirror, one schema.

SurfaceCommands / toolsWhat it covers
CLI (espctl deposit <sub>)init, add, list, verify, milestone, sign, attest, exportHand-driven from any terminal
MCP (deposit.*)8 tools mirroring the CLI 1:1Called by an AI agent after each successful build
Schemasmanifest.yaml v1 + deposit.toml + INDEX.toml + bundle verification scriptsStable across versions; buyer-side parsers from 2031 will work against 2026 archives

CLI and MCP dispatch into the same orchestrators — behavioral drift between the two surfaces is structurally impossible.

All local operations work offline. The only command that ever talks to the public internet is attest (Zenodo + OTS), and only with explicit confirmation.


CLI command reference

The eight subcommands ship across four releases. The first release (init/add/list/verify) is the standalone headline and delivers ~90% of the value on its own — even if no later release ever shipped, espctl deposit would still be useful.

espctl deposit init — set up the workspace (stable since v0.6.0)

Creates ~/maker-assets/{public,private,asset}/, drops a default deposit.toml, probes for minisign on PATH, and (on first run) prompts you to register a public-key fingerprint. Idempotent: a second invocation produces zero file mutations.

espctl deposit init                        # all defaults
espctl deposit init --root /data/maker     # custom workspace root
espctl deposit init --contributor-alias myhandle

init never modifies your ~/.gitconfig or your existing SSH keys. If minisign is not installed, it fails fast with a platform-specific install hint.

espctl deposit add <build_id> — convert a build into a signed triple (stable since v0.6.0)

The heart of the tool. Given the build_id of a successful espctl build, this command:

  1. Reads build context (chip, IDF version, source hash, binary hash, size, target) from espctl-core.
  2. Auto-fills the manifest.yaml fields it can derive: timestamp, contributor fingerprint, hardware, firmware section.
  3. Prompts (or accepts via flags) the three fields a human must judge:
    • --outcome passed|failed|partial
    • --verification-evidence <path> (one or more files, ≥ 1 required)
    • --notes <string> (terse is fine; one line is fine)
  4. Resolves the slug, applying -vN auto-suffix if a same-date collision exists.
  5. Writes the triple directory, signs manifest.yaml with minisign, and updates INDEX.toml.
# Interactive (fills missing fields by prompting)
espctl deposit add abc123

# Non-interactive (CI / scripts)
espctl deposit add abc123 \
  --slug crsf-parser \
  --outcome passed \
  --verification-evidence verification/monitor-capture.log \
  --verification-evidence verification/oscilloscope-ch9.png \
  --notes "16 channels track stick inputs; CRC fail < 0.1%/60s"

outcome: failed triples are first-class. Failure data has high training value — do not delete it.

espctl deposit list — query your triples (stable since v0.6.0)

Read-only; never mutates disk. Filter by date range, outcome, signing/attestation status:

espctl deposit list                                  # all triples
espctl deposit list --since 2026-04-01 --outcome passed
espctl deposit list --unsigned --json                # JSON for piping
espctl deposit list --unattested                     # triples without milestone attestation

espctl deposit verify [slug | --all] — integrity check (stable since v0.6.0)

Re-hashes evidence files, re-verifies the minisign signature on manifest.yaml, and checks INDEX consistency. Exit code equals the number of failed triples (0 = all pass):

espctl deposit verify                               # current directory triple
espctl deposit verify crsf-parser-v2                # named slug
espctl deposit verify --all                         # walk asset/triples/
espctl deposit verify --all --strict                # additionally require milestone attestation

Any byte-flip in evidence, signature, or fingerprint produces InvalidSignature — tamper detection is part of the contract.

espctl deposit milestone <name> — offline bundling (stable since v0.6.1)

Groups N triples into a deterministic <name>.tar.gz with a sidecar metadata.json (Zenodo-draft-ready) and a minisign signature. Offline only — does not contact any external service.

espctl deposit milestone 2026-05                    # all triples in May 2026
espctl deposit milestone phase-3 --slugs slugs.txt  # explicit slug list
espctl deposit milestone 2026-Q2 --include-failed   # include outcome:failed triples

Bundles are reproducible: re-running with the same inputs produces a byte-identical tarball.

espctl deposit sign <path> — general-purpose minisign wrapper (stable since v0.6.1)

A thin convenience layer over minisign -Sm / -Vm. Useful for signing anything outside the triple lifecycle (release notes, blog drafts, ad-hoc files):

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

espctl deposit attest <milestone> — Zenodo + OTS (stable since v0.7.0)

The only online command. Uploads the milestone bundle to Zenodo (receiving a DOI), stamps it with OpenTimestamps (receiving a Bitcoin-anchored .ots), and writes both receipts into asset/deposits/.

Zenodo publish is irreversible — DOIs are permanent. The command will refuse to proceed without explicit confirmation:

espctl deposit attest 2026-05 --confirm             # both Zenodo + OTS
espctl deposit attest 2026-05 --ots-only            # OTS only (no consent needed; not irreversible)
espctl deposit attest 2026-05 --zenodo-only --confirm

Without --confirm, the command exits with ConsentRequired. There is no environment variable, no config flag, no agent setting that bypasses this gate. Every layer of the tool enforces it.

espctl deposit export <slug | --slugs FILE> — buyer-side bundle (stable since v0.7.0)

Builds a self-contained export.tar.gz that bundles the triple(s), the minisign public key, the Zenodo DOI receipt, the OTS stamp(s), and a VERIFY.md README that walks a buyer through verification with no espctl installed. The bundle includes a shell script that runs in a clean Docker container:

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   # for buyer audit before signing

--redact-keys strips the maker’s identifying handles for an arms-length preview. Cryptographic signatures and Zenodo DOIs remain — the buyer can still verify provenance without seeing the maker’s identity.


MCP tool surface

Each CLI subcommand has a 1:1 MCP mirror under the deposit.* namespace. An AI agent (Claude Code, Cursor, anything speaking MCP) calls these directly. CLI and MCP share the same flow orchestrators — there is no surface where they can drift.

ToolPermission classReturns
deposit.initwrite (local){ root, created_new, signing_key_existing }
deposit.addwrite (local){ slug, signed } or a NeedsMakerInput envelope
deposit.listread-only[ TripleRecord ]
deposit.verifyread-only[ VerifyReport ]
deposit.milestonewrite (local){ name, triple_count, tarball_path, sha256 }
deposit.signwrite (local){ signed } or { verified }
deposit.attestwrite (irreversible external){ doi?, ots_stamp? } or ConsentRequiredError
deposit.exportwrite (local){ output_path, sha256 }

Agent permission policies should treat the three classes differently — always allow read-only, ask once per write, and require per-call human confirmation on deposit.attest. The tool layer enforces the consent gate regardless of what the agent’s local policy allows.

The three-prompt agent flow

When an agent calls deposit.add with auto_fill: true, it gets one of two responses:

  1. Ok { slug, signed } — the agent had enough context (e.g., from session prompt history) to fill all required fields. Triple is signed. Done.
  2. NeedsMakerInput { fields: [outcome, evidence_paths, notes], prompt: "..." } — the agent surfaces exactly three questions to the maker and re-calls deposit.add with the answers.

This is the leverage moment. The maker types three short answers; the agent does everything else. The field list is exactly three and is part of the wire contract — no agent or model can expand it.

What the agent will never do autonomously

  • Publish to Zenodo without explicit_consent: true. The gate is enforced server-side in the tool, not in the agent’s prompt.
  • Synthesize verification outcomes. If the agent does not have ground-truth evidence, it must call NeedsMakerInput rather than guess outcome: passed.
  • Modify your ~/.gitconfig or SSH keys. deposit init ships with this property; the test suite enforces it.

A walk through a typical session

A maker finishes a build, reviews monitor output, and decides to deposit. With a Claude Code agent running:

maker: Build looks good — channels track, CRC stable. Deposit it.

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

agent: I need three things to finish the triple:
       1. Outcome: passed / failed / partial?
       2. Which files from this build are verification evidence?
       3. One-line notes for the manifest.

maker: passed; monitor-capture.log and oscilloscope-ch9.png; 16 ch track,
       CRC < 0.1%/60s.

agent: [re-calls deposit.add with all three fields]
       → Ok { slug: "2026-05-15-crsf-parser-v2", signed: true }

agent: Done. Triple signed; INDEX updated. You have 23 unattested triples
       this month — say "milestone 2026-05" when you want to bundle.

End-to-end wall clock is on the order of 30 seconds — most of it is the maker typing the three answers.


Configuration

~/.config/espctl/deposit.toml controls defaults:

[deposit]
root                           = "~/maker-assets"
default_contributor_alias      = "myhandle"
auto_suggest                   = true       # print "Tip: deposit add <id>" after successful build

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

[deposit.zenodo]
api_token_env                  = "ZENODO_API_TOKEN"
community                      = ""                          # optional 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

Top-level unknown fields are tolerated (so future versions can add settings without breaking your config). Section names and existing field names are stable across all 0.x versions.


What is intentionally out of scope

espctl deposit is a sovereignty tool, not a marketplace. The following are documented non-goals — they will not be added in a later version, they are architectural commitments enforced by tests and CI:

  • No dataset trading or matchmaking. No listing, escrow, royalty distribution, or price discovery. You own the keys; you do the negotiation. esphome.cloud takes no percentage of any maker-to-buyer transaction.
  • No centralized hosting of the asset tier. Triples never leave your machine except through commands you run with full receipts. esphome.cloud servers do not store your asset directory.
  • No quality scoring. The tool does not opine on whether your triple is “worth more” than another. That is for buyers and reference prices to decide, not the tool.
  • No analytics or telemetry upload from any deposit-* crate. A 1,000-call audit MUST show zero HTTP requests to esphome.cloud-owned domains. CI enforces this via cargo deny + grep.
  • No GPG. SSH + minisign is the only signing path. (See operations guide §4 on why.)
  • No IPFS as primary attestation. Zenodo + OTS dual-attestation only. IPFS pinning is not durable and does not survive maker neglect.
  • No auto-publish to Zenodo without explicit consent at every layer. The DOI is permanent; the gate is structural.
  • No RAG / vector-library indexing of triple contents inside the asset layer. A vector store is a consumption-layer artifact and must not be written into asset/triples/. deposit add works regardless of whether you also run an embedder.

This list is not advisory — it is the architectural fence. If a future release adds any of these capabilities, that release is not espctl deposit; it is a different product under a different name.


Roadmap

ReleaseThemeSurface addedStatus
v0.6.0Core CLIinit, add, list, verify + manifest.yaml v1 schemaStandalone headline. 90% of the value lands here.
v0.6.1Milestone + signmilestone, signOffline bundling.
v0.7.0Attest + exportattest (Zenodo + OTS), export (buyer bundle)Only online release.
v0.8.0MCP mirror8 deposit.* tools + agent flowEnables the 3-prompt agent workflow.
v0.9.0Ecosystem hooksespctl build success hint, espctl monitor archiveOptional. Formally skipped at 2026-05-21; may re-enter if usage data justifies it.
v1.0.0SemVer commitments fully activeAfter v0.8.0 has run against real maker workflows for ≥ 3 months.

The v0.6.0 standalone-ship constraint is structural: even if later releases never ship, v0.6.0 is on its own a complete sovereignty tool. The downstream releases are additive.


Verifying provenance without espctl installed

This is the test that matters for a 2031 buyer:

# Buyer extracts a bundle exported by espctl deposit export
tar xzf 2026-Q2-license-bundle.tar.gz
cd 2026-Q2-license-bundle/

# Buyer reads VERIFY.md, then runs the included script in a clean Docker container
docker run --rm -v "$PWD:/work" -w /work alpine:latest sh ./verify.sh

# verify.sh installs minisign and ots inside the container, then runs:
#   minisign -Vm manifest.yaml -p contributor.pub
#   ots verify milestone-2026-Q2.tar.gz.ots
# Both must exit 0.

If both verifications pass, the buyer has cryptographic proof that:

  1. The triple was signed by the contributor’s public key (held offline by the maker).
  2. The milestone bundle existed on or before a specific Bitcoin block (and therefore on or before a specific date).
  3. The Zenodo DOI (also included in the bundle) is a public-record claim of authorship at that date.

Three independent ledgers, all verifiable years later, from a clean container, by a stranger.

That is the actual product.


See also


esphome.cloud / Aegis
May 2026