didbot_data/lib.rs
1//! The atproto data model, its canonical encoding, and the CIDs that names.
2//!
3//! atproto's records are exchanged as JSON and *identified* as DAG-CBOR. The
4//! two are the same data model wearing different clothes, and the identity of
5//! a record — the CID a commit signs, a link quotes and an MST keys on — is
6//! the sha-256 of the DAG-CBOR bytes. Everything downstream of that hash
7//! depends on there being exactly one such encoding per record, which is why
8//! this crate is three narrow pieces rather than a serializer:
9//!
10//! * [`Value`] is the data model: the values that *have* an encoding, and
11//! nothing else. Building one from JSON is where every rejection happens.
12//! * [`dag_cbor`] turns one into bytes, and cannot fail. It reads them back
13//! too, which is a stricter job: see [`dag_cbor::decode`].
14//! * [`Cid`] is the hash of those bytes, and the only string form atproto
15//! permits for it.
16//!
17//! * [`mst`] is the fourth piece and the largest: the Merkle search tree a
18//! repository is keyed by, along with the two pure functions its shape
19//! comes from.
20//!
21//! ```
22//! use didbot_data::{dag_cbor, Value};
23//!
24//! let record = serde_json::json!({
25//! "$type": "com.example.thing",
26//! "text": "quernstone",
27//! "count": 1,
28//! });
29//! let value = Value::object_from_json(&record).expect("a data-model record");
30//! let bytes = dag_cbor::encode(&value);
31//! assert_eq!(dag_cbor::cid(&value), didbot_data::Cid::of_dag_cbor(&bytes));
32//! ```
33//!
34//! # Why this is its own crate
35//!
36//! It sits below everything that would use it and depends on none of them. A
37//! repository needs it to name a record, a Merkle search tree needs it to key
38//! one, a commit needs it to sign one, and a CAR file needs it to frame one.
39//! The tree is here, because a tree is nothing but keys and the encoding of
40//! its own nodes; the commit and the CAR are in `didbot-repo`, because
41//! they need a signing key and this crate deliberately depends on nothing.
42//!
43//! # What this crate does not do
44//!
45//! It is not a `serde` data format: records arrive here as `serde_json::Value` from
46//! an HTTP body, and a derive-driven path would only add a second way to
47//! produce bytes that must be identical to these.
48//!
49//! The conformance suite this is held to is `data-model/` in the vendored
50//! upstream vectors; see `docs/conformance.md`.
51
52#![forbid(unsafe_code)]
53
54pub mod cid;
55pub mod dag_cbor;
56mod encoding;
57pub mod mst;
58pub mod value;
59
60pub use cid::{Cid, CidError, RawHasher};
61pub use value::{DataError, ErrorKind, Value};