agent-scroll (Rust)
Idiomatic Rust port of
@p-vbordei/agent-scroll. Canonical byte-deterministic transcript format for AI-agent conversations. Same RFC 8785 JCS bytes, same SHA-256 hashes, same Ed25519 signatures as the TypeScript reference — verified by 20 byte-equality vectors plus mutation, roundtrip, and chain-tamper tests.
A scroll is an ordered chain of sealed turns. Each turn is a normalized serde_json::Value ({version, turn, role, model, params, messages, ..., timestamp_ns, prev_hash?}) serialized via JCS, SHA-256 hashed, optionally Ed25519-signed, and linked to its predecessor by prev_hash. Any byte flip — in body, hash, signature, or chain order — is detectable.
What's in the box
canonical(&Value)— RFC 8785 JCS bytes (with au64 → f64normalization pass; see Architecture)hash_canonical(&Value)—sha256:<hex>stringseal(&turn, sign)— produce a sealedValue(hash + optional Ed25519 signature)seal_chain(&[Value], sign)— chain-linked sealed turns, each carryingprev_hashverify(&[Value], pubkey)— schema + hash + chain link + signatureserialize/deserialize— round-trip via canonical bytesvalidate_turn/validate_sealed_turn— explicit role / tool-call schema checks
Install
[]
= "0.1"
Quickstart
See examples/quickstart.rs. Build three turns, seal-and-sign as a chain, verify, then mutate a byte and watch verify fail:
Expected output:
sealed 3 turns, all hashes set
verify clean: ok=true
verify tampered: ok=false reason=BadHash
How it relates
| Port | Source | Same vectors |
|---|---|---|
agent-scroll |
TypeScript reference | — |
agent-scroll |
Python | C1–C4 + 20 byte-equality |
agent-scroll (this repo) |
Rust | C1–C4 + 20 byte-equality |
Conformance
This port is verified against the same fixture set as the TypeScript reference:
- C1 — byte equality across 20 vectors (the gold standard). Canonical bytes of each fixture must hex-match the TS output.
- C2 — single-byte mutation in hash or body MUST fail
verify. - C3 —
serialize→deserializeroundtrip is byte-stable. - C4 — chain tamper / reorder MUST fail
verify.
Fixtures in fixtures/ (c1-hex.json carries the expected hex per vector) and the 20 wire-format vectors in vectors/ are copied verbatim from the TS conformance suite.
Architecture
See docs/architecture.md for module map, dependency choices, and the critical serde_jcs u64-precision workaround (timestamp_ns is a u64 larger than 2^53, which serde_jcs doesn't coerce to f64 as RFC 8785 mandates — we normalize numbers manually in canonical.rs).
Development
License
Apache-2.0 — see LICENSE.