open-standards/pool/pool-rs/Cargo.toml
Transaction Science 2bfaa00d22 pool: ledger entries are sealed pool/1 JCR-1 receipts
§6.5 previously said what the ledger was not: tamper-evident as a
sequence, but unauthenticated, with the spec forbidding an
implementation from calling an entry sealed until the signed form
landed. It lands here, and §6.5 now says what the entry is.

Each entry is a `pool/1` JCR-1 receipt — canonical det-CBOR (RFC 8949
§4.2.1: length-first key order, shortest integer form, no floats, bools
or nulls) inside a COSE_Sign1 envelope (RFC 9052, EdDSA/Ed25519,
`alg = -8`), the steward's identifier as `kid`. That is the wire format
the rest of this family seals with, so a `pool/1` receipt verifies under
a tool that has never heard of Pool.

The JCR-1 primitives are **vendored**, which is what `mesh-receipt`
documents doing for the same reason: the reference library stays
self-contained while the bytes stay compatible with the family's
cross-language verifiers. Vendoring also kept the dependency honest —
`mesh-receipt` reaches into `jcp-schema` for the decoder, and taking
that dependency here would have contradicted the "self-contained"
rationale in the vendored header, so the decoder is written against
JCR-1's own grammar instead.

Two properties, checked separately because they fail separately:

- **Authenticity** — every entry verifies under the steward's key.
- **Sequence** — every `seq` is its position and every `prev` is the
  content address of the entry before it, from genesis.

Neither alone is enough, and the tests say so rather than assuming it.
`a_removed_entry_breaks_the_chain_though_each_entry_is_still_signed`
deletes an entry, asserts the survivors STILL verify individually, and
asserts the stream is refused anyway: signatures without a chain are a
bag of receipts, not a ledger.

§6.6 is new: canonicity is checked, not assumed. A signature covers
exact bytes, so a verifier that accepted a non-canonical encoding of the
same document would let two byte strings share one signature. The
decoder re-encodes what it decoded and refuses any difference.

The decoder is the one piece here that is new code rather than vendored,
so it carries its own tests: round-trip, a non-shortest integer refused,
map keys out of length-first order refused, trailing bytes refused,
duplicate keys refused, every truncation of a valid core refused, and
the types outside the core grammar (floats, bools, nulls, negative
integers, byte strings) refused.

§8 gains the limit this does NOT remove: the steward signs its own
decisions, so a ledger is self-attested by default — it proves this
steward wrote this sequence, not that the decisions were right or that a
second party saw them. The family already holds the shape of the answer
elsewhere, a meter attestation signed by a key distinct from the receipt
signer, and `pool/1` has no analogue for the decision dimension yet.
Said in the spec rather than left for a reader to discover.

Conformance §7.13 is rewritten and §7.14 added. 31 tests.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-18 15:30:07 -04:00

15 lines
382 B
TOML

[workspace]
resolver = "3"
members = ["crates/pool-core"]
[workspace.package]
edition = "2024"
license = "Apache-2.0"
repository = "https://git.transaction.science/Transaction-Science/open-standards"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
blake3 = "1"
ed25519-dalek = { version = "2", features = ["rand_core"] }
thiserror = "2"