CESR stands for Composable Event Streaming Representation.
CESROX stands for the Rust implementation of the CESR protocol specification. Alternate spellings include CESRox, CESR-ox, or cesrox.
CESROX Working Group Bi-Weekly Meeting Agenda and Notes
KERI and ACDC Roadmap
KERI WebOfTrust repo
CESROX is the premiere Rust implementation of the CESR protocol specification providing efficient, performant, usable serialization and deserialization to and from qualified cryptographic primitives used in the KERI and ACDC space in a composable way. Extensibility for other domains beyond KERI and ACDC is planned for and expected.
The "-ox" suffix comes from "-ox" sounding like the word oxide from the idea of the chemical rust being an iron oxide.
The unique most important feature is binary and text composability of cryptographic primitives and other data structures in both the JSON, CBOR, and MessagePack (MSGPK) formats.
Binary and Text Composability means that any set of text domain encoded primitives may be converted en-masse to the binary domain and then either further composed with other binary domain single or group encodings and then either decoded or converted back to the text domain en-masse without loss.
Regarding composability of concretely defined types and code tables, any number of code tables can be used for different applications as long as the encodings satisfy the composability property.
Therefore the core feature of CESRox is a flexible serialization and deserialization - using serde - for composable encodings using specified code tables for concrete types.
While code tables specify actual encoding formats any encoding is composable with any other encoding providing capability for asynchronous, parallel stream processing.
Ensure CESROX is use case agnostic. Currently CESR is KERI specific. Therefore when the CESR protocol gets new master code tables, or is extended by another spec like CESR-proofs, CESROX must be open to extension.
The implementation is opaque to the clients regarding the format input streams are defined in. In other words CESROX encapsulates both the payload type (so whether it is JSON/CBOR/MsgPack/…) as well as whether it is text or binary stream.
Clients willing to use CESROX are willing to implement mappings to client-specific data models. CESROX is use case agnostic and therefore data model agnostic.
CESROX provides and exposes all the types that are related to attachments that are defined in master code tables of CESR protocol.
To support multiple code tables the parsing of the codes should be table based.
There are three basic types of codes in the KERI master code table. Fixed length primitives, variable length primitives, and group codes.
In addition any number of context specific code tables may be supported.
Acronym | Specification | Technical Design | Python | Rust | Leads | Report/Notes | Next date |
---|---|---|---|---|---|---|---|
CESR | ACCEPTANCE- |
BasixPrefix
, SelfSigningPrefix
, …). Basically extract prefix
module.event_parsing
module (including the nom
logic);SignedEventData
from KERIOX into CESROX and make it generic type, so that it becomes SignedEventData<T, U>
. SignedEventData<T, U>
is then the primary type that is exposed by CESROX after deserializing a CESR stream. Example below:#[derive(Clone, Debug, PartialEq)]
pub struct SignedEventData<T, U> where T: Serialize + Deserialize, U: Attachment {
pub payload: T,
pub attachments: Vec<U>,
}
impl Attachment for KERIAttachment {…}
use cesrox::KERIAttachment;
// this comes from the client side, so from KERIOX
use keri::event_parsing::EventType;
type KERIBasedSED = SignedEventData<EventType, KERIAttachment>;
impl TryFrom<KERIBasedSED> for Message {…}
import {cesrDeserializer, addCodeTables, RawMsg } from "cesrox";
import * as fs from 'fs';
// Code Tables are provided from various sources:
let codeTable1 = {
"A": {"description": "...", codeLength: ..., totalLength: ...},
"-D": { codeLength: 4, indexLength: 2, totalLength: 4, "protocol": ["E", "F", "E", "A"] }
};
/*`codeTable1` above contains `protocol` that unambgiously
expresses the intent of an attachments that consists
of some other primitives,
ie. in case of `-D` it is `pre+snu+dig+sig`
*/
let codeTable2 = fs.loadFileSync("some_file.json");
await addCodeTables([JSON.stringify(codeTable1), codeTable2]);
//CESR stream got from somewhere else...
let cesrStream: Buffer = Buffer.from("...");
// Deserializes CESR stream into an array of rawMsgs.
let rawMsgs: RawMsg[] = await cesrDeserializer(cesrStream);
// rawMsg body:
// payload: string, (ie. JSON),
// attachments: [
// {"code": "-A": value: [{ "code": "1AAA", "value": "some signature..." },
// { ... }]},
// {"code": "-D", value: [
// [{}, {},{}, {}],
// [{}, {},{}, {}], ...
// ]}
// ]
rawMsgs.map(rawMsg => /* maps rawMsg into client data model */)
Discussion:
addCodeTables(codeTables: string[])
from line #16
injects code tables into CESROX. These are the source of truth for all the codes received via CESR stream. In case of tuples/triples/quadruples etc
, protocol
contains the expected primitives.
cesrDeserializer(cesrStream: Buffer)
returns an array of one or more messages along with attachments, extracted from cesrStream
. On top of RawMsg[]
, client runs internal processing and executes his business logic, including RawMsg payload
deserialization, validation, attachments
organization and so on.
If TCP/UDP is in place and chunks may be received, ie. cesrStream1
and cesrStream2
that combined constitutes one event with attachments
We aim generics rather than traits. CESROX in runtime shall use at most a few different (T,U)
pairs and in most cases it will be only one pair. In other words the requirement for additional machine code for all known T
, U
to support them in runtime is minimal. Therefore it is reasonable to have very limited amount of T
and U
rather than additional memory footprint while mantaining trait support.