Schema, JCS canonicalization, and EIP-191 signing for verdict envelopes (deterministic outcome attestations).
Project description
verdict-protocol
Schema, JCS canonicalization, and EIP-191 signing for verdict envelopes — the deterministic post-flight attestations produced by agentshield-mcp's outcome evaluator.
This package is a pure library. It defines the wire format, hashes it, and signs it. It does not talk to the network, does not persist anything, and does not manage keys. All of those concerns live in the calling system (see POSITIONING.md for the dual-license context: this MIT package is the wedge; the hosted evaluator and managed-key service are separate).
Install
pip install verdict-protocol
Quickstart
from eth_account import Account
from verdict_protocol import (
Claim, Envelope, Verdict,
canonicalize, keccak256,
output_digest, params_digest,
sign, verify,
)
from verdict_protocol.schema import utcnow_iso
# 1. The agent describes what it's about to do.
params = {"pair": "ETH/USDC", "amount_in": "1.0", "min_out": "3500.0", "chain_id": 8453}
claim = Claim(
agent_id="did:agent:0xabc...",
intent="swap",
params_digest=params_digest(params),
created_at=utcnow_iso(),
)
# 2. The deterministic evaluator inspects the actual on-chain outcome.
observed = {"out_amount": "3501.42", "tx_hash": "0xdeadbeef...", "status": 1}
verdict = Verdict(
evaluator_id="agentshield-evaluator-v0",
claim_digest=keccak256(canonicalize(claim.model_dump(mode="json"))),
output_digest=output_digest(observed),
outcome="match",
evaluated_at=utcnow_iso(),
)
# 3. Bind them together and sign with the evaluator's hosted key.
acct = Account.create()
envelope = Envelope.build(claim, verdict, signer=acct.address)
signature = sign(envelope, acct.key)
# 4. Anyone holding the envelope + signature can verify it.
assert verify(envelope, signature, acct.address)
See tests/ for executable examples covering every public function.
I/O contract
| Layer | What it does | Bytes hashed |
|---|---|---|
canonicalize |
RFC 8785 JSON Canonicalization Scheme | UTF-8 bytes of canonical form |
params_digest |
keccak-256 over canonicalized claim inputs | keccak256(JCS(params)) |
output_digest |
keccak-256 over canonicalized evaluator observations | keccak256(JCS(observed)) |
content_hash |
binds claim + verdict in the envelope body | keccak256(JCS({schema_version, claim, verdict})) |
sign / verify |
EIP-191 personal_sign over envelope's JCS bytes |
eth_sign("\x19Ethereum Signed Message:\n" + len + JCS(envelope)) |
The canonicalized envelope is capped at 8 KB; this is enforced by the
Envelope validator. If you need to attest to a larger payload, hash it
externally and put the digest in params or observed.
Why these libraries
jcs==0.2.1— Anders Rundgren's reference implementation of RFC 8785. Pinned exactly: there is no other production-grade RFC 8785 implementation on PyPI, and we need byte-for-byte stability across releases. The Matrix-flavoredcanonicaljsonpackage implements a different canonical form and is not compatible.eth-account>=0.10,<0.14— official Ethereum keypair / EIP-191 helpers from theeth-protocolorg. We rely onAccount.sign_messageandAccount.recover_message; both are stable across the supported range.pydantic>=2.6,<3— strict validation, frozen models, JSON-mode dumps.eth-utils—keccak,to_checksum_address. Pulled in transitively byeth-account; declared explicitly to keep our import graph honest.
Out of scope for v0.1
Things this package deliberately does not do (each lives in a different repo):
- Networking. The caller is responsible for transmitting the envelope and signature.
- Persistence / Verdict Ledger storage. Hosted ledger writes happen in
agentshield-mcp; here we only define the wire shape. - Key management / rotation. The hosted evaluator service holds and rotates signing keys; this library only consumes a private key passed in by the caller.
- LangChain / framework adapters.
agentshield-langchain(planned) wraps this package; no framework dependencies leak in here. - Higher-level orchestration. No retries, no caching, no callbacks. One envelope in, one signature out.
If you need any of the above, reach for the AgentShield hosted SDK instead.
Development
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
ruff check .
mypy
pytest
CI runs the same gate on Python 3.10, 3.11, and 3.12 with coverage ≥90 %.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file verdict_protocol-0.1.0.tar.gz.
File metadata
- Download URL: verdict_protocol-0.1.0.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08dd03c54c6ffa8e7108fb6353ed7147636215f9799a4425fe4a5ac65502dfdb
|
|
| MD5 |
007cd3690f7156f5769a2011d1e2a3a2
|
|
| BLAKE2b-256 |
47dec28f8bfb9307fc4dd4f315fc7f0f6772094b62701b2a7c507592eea388de
|
File details
Details for the file verdict_protocol-0.1.0-py3-none-any.whl.
File metadata
- Download URL: verdict_protocol-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a06367aa7676e8ae3b777c9c7ea5655a1272532a487758329b65fe8dd4118a3c
|
|
| MD5 |
8881b75e226af642730fedf4c43dde07
|
|
| BLAKE2b-256 |
d069da5d75678c8b3b7b5f6486f7013a88d278a50bb90222cd7cd453fdbe8e72
|