Docs

envelope

Source: crates/lvcs-net/src/wire/envelope.rs.

/// The envelope's version (design §5).
///
/// `major` changes only with an incompatible layout: a peer rejects an
/// envelope whose `major` is not `lvcs_vocab::ids::WIRE_MAJOR` with
/// [`WireError::UnknownMajor`]. A new message kind is a `minor` change:
/// peers accept any `minor` and ignore kinds they don't know (design §10).
/// Encoded as the map `{ major, minor }`. Owner: `M3.envelope`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WireVersion {
    /// Changes only with an incompatible layout.
    pub major: u16,
    /// Changes when a message kind is added; peers accept any.
    pub minor: u16,
}

/// Who signed an envelope (design §5, §9, §15).
///
/// Chosen at the M3 freeze: gossip messages are signed by the participant
/// who sent them, so capability checks and per-signer token buckets key on
/// the participant, while gossipsub's own message author (the forwarding
/// device) is what a repeat offense down-scores; sync-stream frames are
/// signed by the sending device, whose identity Noise already
/// authenticated. Participant and device keys are separate keys (design
/// §2): a participant key is never a device key. Encoded as a one-entry map
/// `{ participant: <bytes> }` or `{ device: <bytes> }`. Owner:
/// `M3.envelope`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Sender {
    /// A participant's Ed25519 public key: the bytes of an
    /// `lvcs_doc::ParticipantKey`.
    Participant([u8; 32]),
    /// A device's Ed25519 public key, from which its libp2p `PeerId`
    /// derives.
    Device([u8; 32]),
}

/// One message between peers (design §5, decisions 20 and 67).
///
/// `payload` is the canonical CBOR encoding of the payload type `kind` names
/// (the `wire_kind` set in `docs/generated/vocabulary.md`, an open set: a
/// kind outside it is ignored, never rejected). `frontier` is the sender's
/// visible frontier id when the message is about project state (absent
/// otherwise; encoded as CBOR null, so the field set never varies). `sig`
/// is `sender`'s `SIGNATURE` over the canonical CBOR array
/// `[v, kind, sender, frontier, payload]` (an array of the signed fields,
/// like `ChangeSig`'s, chosen at the M3 freeze). Encoded as the map
/// `{ v, kind, sender, frontier, payload, sig }` in that order, digests and
/// `payload` as byte strings. Automerge sync messages travel inside a
/// `sync` payload unchanged (decision 30). Owner: `M3.envelope`.
#[derive(Debug, Clone, PartialEq, Eq)]
#[expect(
    clippy::min_ident_chars,
    reason = "the field `v` is named for the encoded envelope's `v` map key (design section 5), \
              which this contract type mirrors field for field; Clippy checks a field's name \
              against the struct's lint level, so the struct is the narrowest item"
)]
pub struct Envelope {
    /// The envelope's version.
    pub v: WireVersion,
    /// The message kind: a `wire_kind` value, or one this peer doesn't know.
    pub kind: String,
    /// Who signed the envelope.
    pub sender: Sender,
    /// The sender's visible frontier id, when the message is about project
    /// state.
    pub frontier: Option<[u8; 32]>,
    /// The canonical CBOR encoding of the payload type `kind` names.
    pub payload: Vec<u8>,
    /// `sender`'s signature over the signed fields.
    pub sig: [u8; 64],
}

/// Why bytes are not an acceptable envelope (design §5).
///
/// Each maps to one `rejection` value: `TooLarge` and `Malformed` to
/// `malformed`, `UnknownMajor` to `unknown_version`, `BadSignature` to
/// `bad_signature`. Owner: `M3.envelope`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WireError {
    /// Longer than `lvcs_vocab::limits::WIRE_MAX_BYTES`; not decoded.
    TooLarge {
        /// The length received, in bytes.
        len: usize,
    },
    /// Not a canonical CBOR envelope or payload.
    Malformed(String),
    /// The envelope's major version is not `WIRE_MAJOR`.
    UnknownMajor {
        /// The major version the envelope carried.
        major: u16,
    },
    /// `sig` is not `sender`'s signature over the signed fields.
    BadSignature,
}