Skip to main content

dreamwell_engine/
events.rs

1//! Event envelope types for the canon pipeline.
2//!
3//! `CanonEnvelope` wraps an event payload with metadata required
4//! for deterministic event ordering and hash chain verification.
5
6use crate::ids::{ActorId, EventId, ScopeKey};
7use serde::{Deserialize, Serialize};
8
9/// Canon event envelope — wraps a payload with deterministic metadata.
10/// The hash chain links each event to its predecessor for tamper detection.
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct CanonEnvelope<E> {
13    /// Unique event identifier.
14    pub event_id: EventId,
15    /// Actor who caused the event.
16    pub actor_id: ActorId,
17    /// Partition scope.
18    pub scope_key: ScopeKey,
19    /// Deterministic tick when event occurred.
20    pub tick: u64,
21    /// The event payload.
22    pub payload: E,
23    /// Hash of the previous event in the chain.
24    pub hash_prev: u64,
25    /// Hash of this event (computed from all fields above).
26    pub hash_self: u64,
27}
28
29/// Trait for types that can be deterministically hashed for the canon pipeline.
30pub trait CanonHashable {
31    /// Write deterministic bytes for hashing. Must be stable across versions.
32    fn canon_bytes(&self, out: &mut Vec<u8>);
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn envelope_construction() {
41        let env = CanonEnvelope {
42            event_id: EventId(1),
43            actor_id: ActorId(42),
44            scope_key: ScopeKey(100),
45            tick: 500,
46            payload: "test_event".to_string(),
47            hash_prev: 0,
48            hash_self: 12345,
49        };
50        assert_eq!(env.event_id, EventId(1));
51        assert_eq!(env.tick, 500);
52    }
53
54    #[test]
55    fn serde_roundtrip() {
56        let env = CanonEnvelope {
57            event_id: EventId(1),
58            actor_id: ActorId(2),
59            scope_key: ScopeKey(3),
60            tick: 4,
61            payload: 42u64,
62            hash_prev: 5,
63            hash_self: 6,
64        };
65        let json = serde_json::to_string(&env).unwrap();
66        let back: CanonEnvelope<u64> = serde_json::from_str(&json).unwrap();
67        assert_eq!(env, back);
68    }
69}