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: u32The event format version.
stream: StreamIdThe stream that wrote it.
seq: u64Dense from 1 within the stream.
lamport: u64The Lamport stamp: above every stamp its writer had seen.
at: u64The writer’s wall clock, seconds since the Unix epoch. Informational only: nothing is decided by comparing it with another machine’s clock.
repo: StringThe mesh repository id the event belongs to.
pk: StringThe writer’s Ed25519 public key, hex; its digest is the stream’s node.
body: ValueThe body, kind tagged.
sig: StringEd25519 over the canonical bytes of everything above, hex.
Implementations§
Source§impl MeshEvent
impl MeshEvent
Sourcepub fn signing_bytes(&self) -> Vec<u8> ⓘ
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());Sourcepub fn id(&self) -> String
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());Sourcepub fn body(&self) -> Option<EventBody>
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");Sourcepub fn kind(&self) -> &str
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<'de> Deserialize<'de> for MeshEvent
impl<'de> Deserialize<'de> for MeshEvent
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl JsonSchema for MeshEvent
impl JsonSchema for MeshEvent
Source§fn schema_id() -> Cow<'static, str>
fn schema_id() -> Cow<'static, str>
Source§fn json_schema(generator: &mut SchemaGenerator) -> Schema
fn json_schema(generator: &mut SchemaGenerator) -> Schema
Source§fn inline_schema() -> bool
fn inline_schema() -> bool
$ref keyword. Read more