proof-aware-crypto-tooling-.../WALLET.md

211 lines
8.9 KiB
Markdown
Raw Normal View History

# warden — the verified-custody wallet
warden is the acting end of pacta. Where the rest of the project *decides*
which cryptographic code is trustworthy, warden *runs on that decision*:
it builds an Ed25519 custody boundary out of the four independently
proven curve25519-dalek forks, and puts them to work guarding money-
adjacent signatures — inbound and outbound.
It is agent-native first. The primary interface is an MCP server; a human
CLI is provided for operation and inspection. The design research behind
that choice is in [docs/agent-native.md](docs/agent-native.md).
---
## The one idea
**Inbound acceptance requires a unanimous quorum of provably-equivalent
verifiers. Outbound signatures must pass the same quorum before release.**
Each quorum member is an Ed25519 verifier compiled from a source workspace
whose correctness certificates are machine-checked in Lean 4 and replay-
attested in the public [Lean Transparency Log](https://ltl.zkdefi.org).
The four members — `dalek`, `anza`, `risc0`, `betrusted` — are genuinely
different codebases, but each is *proven* to decide the same predicate:
```
accept(A, m, R, s) ⇔ decompress(R) = [k](A) + [s]B
```
Classic N-version programming hopes independent implementations won't
share a bug. warden doesn't hope: on the proven domain the members
*cannot* disagree about semantics, so a runtime disagreement is not a
difference of opinion — it is evidence of build corruption, a memory
fault, or tampering. That turns "the verifiers disagreed" from a shrug
into an alarm with a machine-checked guarantee behind it.
---
## Trust posture (read this before trusting it)
| surface | assurance |
|---|---|
| inbound verification | **custody-grade** — quorum of certificate-covered verify paths |
| outbound firewall (verify-after-sign) | **custody-grade** — same quorum |
| outbound signing itself | **trusted base** — the attested artifact, not a third implementation; not covered by any theorem |
| SHA-512 | opaque oracle inside the theorems |
| wire parsers | outcomes are hypotheses |
| reproducible builds, side channels | not claimed (that is R5) |
| ML-DSA / PQC | fail-closed: no proven implementation exists |
The asymmetry is the point: warden is strongest exactly where it matters
most for custody — deciding whether an inbound authorization is real — and
honest about the weaker outbound edge, which it fences with the firewall.
---
## Anatomy of a wallet
A wallet is a directory of evidence, not a database of secrets:
```
capsule.json the sealed custody capsule: quorum members, their attested
source commits, the transparency-log receipts that
authorized them (the R4 gate), and the policy in force
ledger.jsonl append-only, SHA-256 hash-chained event log
keys/ wallet identities (local signer keys are 0600; the airgap
identity has no private key on this host)
incidents/ quorum divergences and firewall quarantines, full trails
receipts/ refusal receipts — signed, machine-actionable
quarantine/ signatures the firewall refused to release
airgap/ outbox/inbox for the Precursor-style gap signer
latch.json present and latched=true when custody is frozen
```
Every state change — inbound verify, outbound sign, refusal, incident,
latch, unlatch — is a hash-chained ledger entry. `pacta wallet
verify-ledger` recomputes the chain; a single altered byte anywhere in
the history is caught.
---
## The R4 gate, in executable form
`pacta wallet init` refuses to create a wallet unless, **for every member**:
1. the built binary's provenance names an attested source commit;
2. the evidence dir holds that component's attestation + inclusion receipt;
3. the attestation re-validates locally — verdicts are **re-derived from
observed axiom cones**, never taken from the provider's label;
4. the local score reaches the required tier (default **R4**);
5. the receipt's inclusion proof verifies against its signed tree head,
under the log public key you pass;
6. the source commit the binary was compiled from matches the attested one.
Miss any of these and wallet creation fails with the reasons listed. A
custody wallet that cannot show its evidence has no business existing.
`--trusted-provider` is **required**: you must name whose *observations*
you are consuming. You are never asked to trust their verdicts.
---
## Quickstart
```bash
# 0. one-time: build the dogfood signer + the four quorum members
pacta dogfood-build --source <…>/curve25519-dalek-source
pacta wallet build-quorum --sources-root <…>/sources # dalek anza risc0 betrusted
# 1. fetch fresh evidence for each fork from the live log
for c in dalek anza risc0 betrusted; do
pacta log-fetch --url https://ltl.zkdefi.org \
--component ${c}-ed25519-verified --out-dir ./evidence
done
# 2. create the wallet (R4 gate)
pacta wallet init --wallet ./my-warden --evidence ./evidence \
--log-public-key ./log.pub --trusted-provider local-pacta-provider
# 3. inspect
pacta wallet status --wallet ./my-warden
pacta wallet card --wallet ./my-warden # the self-proving custody card
# 4. serve the agent-native surface
pacta wallet mcp --wallet ./my-warden # stdio JSON-RPC MCP server
```
---
## Agent-native surface (MCP)
`pacta wallet mcp` speaks MCP over stdio JSON-RPC. Seven outcome-first
tools; strict input schemas; results carry evidence; errors are structured
objects, never prose.
| tool | does |
|---|---|
| `wallet_status` | custody posture: members/tiers, latch, ledger head + chain integrity, counts |
| `verify_inbound` | run the quorum on (payload, signature, public_key); unanimity or a classified incident |
| `request_signature` | intent-bound outbound signing through the firewall; refusal object on any gate |
| `custody_card` | the self-proving card (embedded inclusion proofs; recompute, don't believe) |
| `posture_challenge` | nonce → firewalled, signed posture attestation with the quorum trail |
| `list_incidents` | divergences and quarantines, newest-first |
| `explain_refusal` | fetch a refusal receipt by index (or latest) |
Refusal codes (every refusal names one): `EVIDENCE_REQUIRED`,
`POLICY_DENIED`, `CUSTODY_LATCHED`, `EVIDENCE_STALE`, `MALFORMED_INTENT`,
`SIGNER_UNAVAILABLE`, `FIREWALL_QUARANTINE`, `PENDING_AIRGAP`.
### The custody card is self-proving
Unlike an A2A agent card that you take on the operator's signature, the
warden card embeds, per member, the transparency-log **inclusion proof**
and **signed tree head**. A counterparty recomputes the Merkle roots and
checks the STH signature against the log key it already pins — trust by
recomputation, not by assertion. See `verify_posture_attestation` and the
log's own `verify.py` for the ~40-line client side.
---
## The signing firewall (verify-after-sign)
Outbound is: **intent → sign → firewall → release**.
1. **intent** — a structured envelope whose `purpose` is recorded (the
ledger stores *why*, not only *what*) and whose `payload_sha256` binds
the request to exact bytes.
2. **sign** — either the local dogfood signer or the airgap/Precursor
signer (seed never on this host; request parked in `airgap/outbox`,
response read from `airgap/inbox`).
3. **firewall** — the fresh signature faces the full quorum. This is the
textbook fault-injection countermeasure: a glitched or tampered signer
is caught before anything leaves the building.
4. **release** — only unanimity releases. A rejected self-signature is
**quarantined, never returned**, and custody **latches**.
A latched wallet refuses all outbound with `CUSTODY_LATCHED`, and — by
design — its refusals arrive **unsigned**: a wallet that no longer trusts
its own boundary does not certify its apologies. `pacta wallet unlatch
--note "<why>"` is a deliberate operator act; the note is recorded
permanently in the ledger next to the latch it releases.
---
## Divergence taxonomy
The forks' accept() predicates are *allowed* to differ only on documented
degenerate inputs (anza rejects `A = 0` and a legacy excluded-small-order-
`R` list). warden fails closed regardless; the taxonomy only grades the
alarm:
| classification | when | verdict | incident |
|---|---|---|---|
| `unanimous-accept` | all accept | accept | — |
| `unanimous-reject` | all reject | reject | — |
| `semantic-edge` | disagree AND a documented edge flag applies | reject | note |
| `unexplained` | disagree with no explanation (or a member errored) | reject | **tamper → latch** |
---
## Product lineup
warden ships as one core with four production-ready deployment profiles —
see [docs/products.md](docs/products.md). In one line each:
- **warden-solo** — a single agent's custody sidecar (local signer).
- **warden-airgap** — signing behind a Precursor/Betrusted hardware gap.
- **warden-treasury** — trust-minimized chain watching (re-verify with the
chain's own proven verifier; the RPC is demoted to bandwidth).
- **warden-choir** — N wardens cross-witnessing each other's ledgers.