Skip to main content

MeshEvent

Struct MeshEvent 

Source
pub struct MeshEvent {
    pub v: u32,
    pub stream: StreamId,
    pub seq: u64,
    pub lamport: u64,
    pub at: u64,
    pub repo: String,
    pub pk: String,
    pub body: Value,
    pub sig: String,
}
Expand description

One event as the wire and the store carry it. The body is kept as JSON so that an event of an unknown kind survives storage and relay byte-for-byte; MeshEvent::body interprets it.

Every field above the signature is covered by it, which is what makes a relay harmless: a runtime that forwards an event cannot alter its stream, its sequence, its repository or its body without the next reader noticing, and so vouches for nothing. The wall clock is the one field nothing is decided by — freshness is measured with beats and monotonic clocks, never by comparing two machines’ idea of the time.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, MeshEvent};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionClosed { session: "s1".into() }).unwrap();

let event: &MeshEvent = &j.events()[0];
assert_eq!(event.seq, 1, "dense from 1 within its stream");
assert_eq!(event.kind(), "session_closed");
assert_eq!(event.id(), format!("{}/1", event.stream));

// a relayed byte changed anywhere under the signature is a different event
let mut tampered = event.clone();
tampered.repo = "another-repository".into();
assert_ne!(tampered.signing_bytes(), event.signing_bytes());

Fields§

§v: u32

The event format version.

§stream: StreamId

The stream that wrote it.

§seq: u64

Dense from 1 within the stream.

§lamport: u64

The Lamport stamp: above every stamp its writer had seen.

§at: u64

The writer’s wall clock, seconds since the Unix epoch. Informational only: nothing is decided by comparing it with another machine’s clock.

§repo: String

The mesh repository id the event belongs to.

§pk: String

The writer’s Ed25519 public key, hex; its digest is the stream’s node.

§body: Value

The body, kind tagged.

§sig: String

Ed25519 over the canonical bytes of everything above, hex.

Implementations§

Source§

impl MeshEvent

Source

pub fn signing_bytes(&self) -> Vec<u8> ⓘ

The bytes the signature covers: a domain separator, then the canonical JSON of every field of the event except the signature itself. Canonical, so that signer and verifier agree however their JSON parsers order keys; domain-separated, so that a signature over an event can never be presented as one over an advertisement or a link message.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionClosed { session: "s1".into() }).unwrap();
let event = j.events()[0].clone();

// the signature is not part of what it covers, so re-signing changes nothing here
let mut resigned = event.clone();
resigned.sig = "00".repeat(64);
assert_eq!(resigned.signing_bytes(), event.signing_bytes());

// the body is
let mut edited = event.clone();
edited.body = serde_json::json!({ "kind": "session_closed", "session": "s2" });
assert_ne!(edited.signing_bytes(), event.signing_bytes());
Source

pub fn id(&self) -> String

<stream>/<seq>: the event’s identity everywhere. It is the pair rather than a digest because it is also the address replication works with — a peer’s marks name a stream and a sequence, and what is missing is arithmetic rather than a search.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionClosed { session: "s1".into() }).unwrap();
j.append_own(EventBody::SessionClosed { session: "s2".into() }).unwrap();

let events = j.events();
assert_eq!(events[0].id(), format!("{}/1", events[0].stream));
assert_ne!(events[0].id(), events[1].id());
Source

pub fn body(&self) -> Option<EventBody>

The interpreted body; None for a kind this executable does not know. An event this version cannot read is still stored and relayed byte-for-byte, so an older runtime in a mesh with newer ones carries their traffic instead of breaking it — the fold counts what it could not interpret rather than dropping it silently.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionClosed { session: "s1".into() }).unwrap();
let event = j.events()[0].clone();
assert!(matches!(event.body(), Some(EventBody::SessionClosed { .. })));

// a kind from a newer peer: unreadable here, and still an event
let mut newer = event.clone();
newer.body = serde_json::json!({ "kind": "telepathy_offered" });
assert!(newer.body().is_none());
assert_eq!(newer.kind(), "telepathy_offered");
Source

pub fn kind(&self) -> &str

The body’s kind word, known or not. It reads the tag out of the stored JSON rather than going through MeshEvent::body, so that an event this executable cannot interpret can still be counted and shown by name.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened {
    info: majordomus_cli::mesh::journal::SessionInfo::named("s1", "cli"),
})
.unwrap();
assert_eq!(j.events()[0].kind(), "session_opened");

Trait Implementations§

Source§

impl Clone for MeshEvent

Source§

fn clone(&self) -> MeshEvent

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MeshEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for MeshEvent

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl JsonSchema for MeshEvent

Source§

fn schema_name() -> Cow<'static, str>

The name of the generated JSON Schema. Read more
Source§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

fn json_schema(generator: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
Source§

fn inline_schema() -> bool

Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being re-used where possible using the $ref keyword. Read more
Source§

impl PartialEq for MeshEvent

Source§

fn eq(&self, other: &MeshEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for MeshEvent

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for MeshEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more