Skip to main content

plato_ship_protocol/
lib.rs

1//! # plato-ship-protocol
2//!
3//! Ship Interconnection Protocol — 6-layer trait definitions for the Plato network stack.
4//!
5//! Layers (bottom to top):
6//! 1. [`HarborLayer`]  — direct addressing, peer discovery
7//! 2. [`TidePoolLayer`] — async message routing, buffering
8//! 3. [`CurrentLayer`]  — cross-runtime transport (git-based i2i)
9//! 4. [`ChannelLayer`]  — simulation ↔ live channel bridging
10//! 5. [`BeaconLayer`]   — event signaling, observation, trust score emission
11//! 6. [`ReefLayer`]     — persistence, state handoff, belief serialization
12
13// ─── Layer 1: Harbor ─────────────────────────────────────────────────────────
14
15/// Direct addressing and peer discovery.
16///
17/// Implements `L1 Harbor → plato-address` (identity, exits, path resolution).
18///
19/// # Examples
20///
21/// ```rust
22/// use plato_ship_protocol::HarborLayer;
23///
24/// struct MockHarbor { peers: Vec<Vec<u8>> }
25///
26/// impl HarborLayer for MockHarbor {
27///     fn resolve_peer(&self, address: &[u8]) -> Option<Vec<u8>> {
28///         self.peers.iter().find(|p| p.as_slice() == address).cloned()
29///     }
30///     fn register_peer(&mut self, address: &[u8], _meta: &[u8]) -> bool {
31///         self.peers.push(address.to_vec());
32///         true
33///     }
34///     fn list_peers(&self) -> Vec<Vec<u8>> { self.peers.clone() }
35/// }
36///
37/// let mut h = MockHarbor { peers: vec![] };
38/// assert!(h.register_peer(b"ship-1", b"{}"));
39/// assert_eq!(h.resolve_peer(b"ship-1"), Some(b"ship-1".to_vec()));
40/// assert_eq!(h.list_peers().len(), 1);
41/// ```
42pub trait HarborLayer {
43    /// Resolve a peer's routing descriptor by address. Returns `None` if unknown.
44    fn resolve_peer(&self, address: &[u8]) -> Option<Vec<u8>>;
45
46    /// Register a peer with associated metadata. Returns `true` on success.
47    fn register_peer(&mut self, address: &[u8], meta: &[u8]) -> bool;
48
49    /// List all currently known peer addresses.
50    fn list_peers(&self) -> Vec<Vec<u8>>;
51}
52
53// ─── Layer 2: Tide Pool ───────────────────────────────────────────────────────
54
55/// Async message routing and buffering.
56///
57/// Implements `L2 TidePool → plato-relay` (trust-weighted BFS routing, spore probes).
58///
59/// # Examples
60///
61/// ```rust
62/// use plato_ship_protocol::TidePoolLayer;
63///
64/// struct MockTidePool { buf: std::collections::VecDeque<Vec<u8>> }
65///
66/// impl TidePoolLayer for MockTidePool {
67///     fn enqueue(&mut self, msg: &[u8]) -> bool {
68///         self.buf.push_back(msg.to_vec());
69///         true
70///     }
71///     fn dequeue(&mut self) -> Option<Vec<u8>> { self.buf.pop_front() }
72///     fn buffer_len(&self) -> usize { self.buf.len() }
73/// }
74///
75/// let mut tp = MockTidePool { buf: std::collections::VecDeque::new() };
76/// assert!(tp.enqueue(b"hello"));
77/// assert_eq!(tp.buffer_len(), 1);
78/// assert_eq!(tp.dequeue(), Some(b"hello".to_vec()));
79/// ```
80pub trait TidePoolLayer {
81    /// Enqueue a message for routing. Returns `true` if accepted into the buffer.
82    fn enqueue(&mut self, msg: &[u8]) -> bool;
83
84    /// Dequeue the next routed message. Returns `None` if the buffer is empty.
85    fn dequeue(&mut self) -> Option<Vec<u8>>;
86
87    /// Number of messages currently buffered.
88    fn buffer_len(&self) -> usize;
89}
90
91// ─── Layer 3: Current ────────────────────────────────────────────────────────
92
93/// Cross-runtime transport (git-based i2i).
94///
95/// Implements `L3 Current → plato-bridge / plato-hooks` (git-based inter-instance transport).
96///
97/// # Examples
98///
99/// ```rust
100/// use plato_ship_protocol::CurrentLayer;
101///
102/// struct MockCurrent;
103///
104/// impl CurrentLayer for MockCurrent {
105///     fn export(&self, msg: &[u8]) -> Vec<u8> {
106///         let mut out = b"git:".to_vec();
107///         out.extend_from_slice(msg);
108///         out
109///     }
110///     fn import(&self, data: &[u8]) -> Option<Vec<u8>> {
111///         data.strip_prefix(b"git:").map(|b| b.to_vec())
112///     }
113///     fn transport_id(&self) -> String { "git-i2i-mock".to_string() }
114/// }
115///
116/// let c = MockCurrent;
117/// let exported = c.export(b"payload");
118/// assert_eq!(c.import(&exported), Some(b"payload".to_vec()));
119/// ```
120pub trait CurrentLayer {
121    /// Serialize and frame a message for cross-runtime export.
122    fn export(&self, msg: &[u8]) -> Vec<u8>;
123
124    /// Deserialize and unframe an incoming cross-runtime message.
125    /// Returns `None` if the frame is malformed.
126    fn import(&self, data: &[u8]) -> Option<Vec<u8>>;
127
128    /// A stable identifier for this transport implementation.
129    fn transport_id(&self) -> String;
130}
131
132// ─── Layer 4: Channel ────────────────────────────────────────────────────────
133
134/// Simulation ↔ live channel bridging.
135///
136/// Implements `L4 Channel → plato-sim-bridge`.
137///
138/// # Examples
139///
140/// ```rust
141/// use plato_ship_protocol::ChannelLayer;
142///
143/// struct MockChannel { live: bool }
144///
145/// impl ChannelLayer for MockChannel {
146///     fn bridge_send(&mut self, _channel: u8, _msg: &[u8]) -> bool { self.live }
147///     fn bridge_recv(&mut self, _channel: u8) -> Option<Vec<u8>> {
148///         if self.live { Some(b"ack".to_vec()) } else { None }
149///     }
150///     fn is_live(&self) -> bool { self.live }
151/// }
152///
153/// let mut ch = MockChannel { live: true };
154/// assert!(ch.is_live());
155/// assert!(ch.bridge_send(0, b"ping"));
156/// assert_eq!(ch.bridge_recv(0), Some(b"ack".to_vec()));
157/// ```
158pub trait ChannelLayer {
159    /// Send a message on the given channel id. Returns `true` if delivered.
160    fn bridge_send(&mut self, channel: u8, msg: &[u8]) -> bool;
161
162    /// Receive a message from the given channel. Returns `None` if nothing waiting.
163    fn bridge_recv(&mut self, channel: u8) -> Option<Vec<u8>>;
164
165    /// Whether this channel is operating in live (vs. simulation) mode.
166    fn is_live(&self) -> bool;
167}
168
169// ─── Layer 5: Beacon ─────────────────────────────────────────────────────────
170
171/// Event signaling, observation, and trust score emission.
172///
173/// Implements `L5 Beacon → cuda-trust` (trust score events, observation).
174///
175/// # Examples
176///
177/// ```rust
178/// use plato_ship_protocol::BeaconLayer;
179///
180/// struct MockBeacon { score: f32, last: Option<Vec<u8>> }
181///
182/// impl BeaconLayer for MockBeacon {
183///     fn emit_event(&mut self, event: &[u8]) -> bool {
184///         self.last = Some(event.to_vec());
185///         true
186///     }
187///     fn observe(&self) -> Option<Vec<u8>> { self.last.clone() }
188///     fn trust_score(&self) -> f32 { self.score }
189/// }
190///
191/// let mut b = MockBeacon { score: 0.9, last: None };
192/// assert!(b.emit_event(b"connected"));
193/// assert_eq!(b.observe(), Some(b"connected".to_vec()));
194/// assert!((b.trust_score() - 0.9).abs() < f32::EPSILON);
195/// ```
196pub trait BeaconLayer {
197    /// Emit a protocol event. Returns `true` if observers were notified.
198    fn emit_event(&mut self, event: &[u8]) -> bool;
199
200    /// Observe the most recent event. Returns `None` if no events have been emitted.
201    fn observe(&self) -> Option<Vec<u8>>;
202
203    /// Current trust score for this node, in `[0.0, 1.0]`.
204    fn trust_score(&self) -> f32;
205}
206
207// ─── Layer 6: Reef ───────────────────────────────────────────────────────────
208
209/// Persistence, state handoff, and belief serialization.
210///
211/// Implements `L6 Reef → plato-afterlife` (ghost tile persistence, state handoff).
212///
213/// # Examples
214///
215/// ```rust
216/// use plato_ship_protocol::ReefLayer;
217///
218/// struct MockReef { store: std::collections::HashMap<Vec<u8>, Vec<u8>> }
219///
220/// impl ReefLayer for MockReef {
221///     fn persist(&mut self, key: &[u8], state: &[u8]) -> bool {
222///         self.store.insert(key.to_vec(), state.to_vec());
223///         true
224///     }
225///     fn restore(&self, key: &[u8]) -> Option<Vec<u8>> {
226///         self.store.get(key).cloned()
227///     }
228///     fn handoff(&mut self, _state: &[u8]) -> bool { true }
229/// }
230///
231/// let mut r = MockReef { store: std::collections::HashMap::new() };
232/// assert!(r.persist(b"ghost-1", b"belief-state"));
233/// assert_eq!(r.restore(b"ghost-1"), Some(b"belief-state".to_vec()));
234/// ```
235pub trait ReefLayer {
236    /// Persist state under the given key. Returns `true` on success.
237    fn persist(&mut self, key: &[u8], state: &[u8]) -> bool;
238
239    /// Restore state by key. Returns `None` if not found.
240    fn restore(&self, key: &[u8]) -> Option<Vec<u8>>;
241
242    /// Hand off serialized state to the next runtime instance.
243    /// Returns `true` if the handoff was accepted.
244    fn handoff(&mut self, state: &[u8]) -> bool;
245}
246
247// ─── ShipStack ───────────────────────────────────────────────────────────────
248
249/// A composed stack of all 6 Ship Interconnection Protocol layers.
250///
251/// `send` routes a message through layers 1 → 6 (bottom-up).
252/// `receive` routes incoming data through layers 6 → 1 (top-down).
253pub struct ShipStack {
254    pub harbor: Box<dyn HarborLayer>,
255    pub tide_pool: Box<dyn TidePoolLayer>,
256    pub current: Box<dyn CurrentLayer>,
257    pub channel: Box<dyn ChannelLayer>,
258    pub beacon: Box<dyn BeaconLayer>,
259    pub reef: Box<dyn ReefLayer>,
260}
261
262impl ShipStack {
263    /// Route a message outbound through all 6 layers (L1 → L6).
264    ///
265    /// Each layer may transform the payload; the final bytes are returned.
266    pub fn send(&mut self, msg: &[u8]) -> Vec<u8> {
267        // L1 Harbor: register the message origin
268        self.harbor.register_peer(msg, b"outbound");
269        // L2 TidePool: buffer for routing
270        self.tide_pool.enqueue(msg);
271        let buffered = self.tide_pool.dequeue().unwrap_or_else(|| msg.to_vec());
272        // L3 Current: frame for cross-runtime transport
273        let framed = self.current.export(&buffered);
274        // L4 Channel: bridge to live channel 0
275        self.channel.bridge_send(0, &framed);
276        // L5 Beacon: emit send event
277        self.beacon.emit_event(b"send");
278        // L6 Reef: persist outbound state
279        self.reef.persist(b"last-send", &framed);
280        framed
281    }
282
283    /// Route incoming data inbound through all 6 layers (L6 → L1).
284    ///
285    /// Each layer may transform the payload; the final bytes are returned.
286    pub fn receive(&mut self, data: &[u8]) -> Vec<u8> {
287        // L6 Reef: restore / record inbound
288        self.reef.persist(b"last-recv", data);
289        // L5 Beacon: emit receive event
290        self.beacon.emit_event(b"recv");
291        // L4 Channel: receive from live channel 0
292        let from_channel = self.channel.bridge_recv(0).unwrap_or_else(|| data.to_vec());
293        // L3 Current: unframe cross-runtime payload
294        let unframed = self.current.import(&from_channel).unwrap_or_else(|| from_channel.clone());
295        // L2 TidePool: buffer then drain
296        self.tide_pool.enqueue(&unframed);
297        let routed = self.tide_pool.dequeue().unwrap_or_else(|| unframed.clone());
298        // L1 Harbor: register inbound peer
299        self.harbor.register_peer(&routed, b"inbound");
300        routed
301    }
302}
303
304// ─── Tests ───────────────────────────────────────────────────────────────────
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309    use std::collections::{HashMap, VecDeque};
310
311    // ── Mock implementations ──────────────────────────────────────────────────
312
313    struct MockHarbor {
314        peers: Vec<Vec<u8>>,
315    }
316    impl HarborLayer for MockHarbor {
317        fn resolve_peer(&self, address: &[u8]) -> Option<Vec<u8>> {
318            self.peers.iter().find(|p| p.as_slice() == address).cloned()
319        }
320        fn register_peer(&mut self, address: &[u8], _meta: &[u8]) -> bool {
321            self.peers.push(address.to_vec());
322            true
323        }
324        fn list_peers(&self) -> Vec<Vec<u8>> {
325            self.peers.clone()
326        }
327    }
328
329    struct MockTidePool {
330        buf: VecDeque<Vec<u8>>,
331    }
332    impl TidePoolLayer for MockTidePool {
333        fn enqueue(&mut self, msg: &[u8]) -> bool {
334            self.buf.push_back(msg.to_vec());
335            true
336        }
337        fn dequeue(&mut self) -> Option<Vec<u8>> {
338            self.buf.pop_front()
339        }
340        fn buffer_len(&self) -> usize {
341            self.buf.len()
342        }
343    }
344
345    struct MockCurrent;
346    impl CurrentLayer for MockCurrent {
347        fn export(&self, msg: &[u8]) -> Vec<u8> {
348            let mut out = b"git:".to_vec();
349            out.extend_from_slice(msg);
350            out
351        }
352        fn import(&self, data: &[u8]) -> Option<Vec<u8>> {
353            data.strip_prefix(b"git:").map(|b| b.to_vec())
354        }
355        fn transport_id(&self) -> String {
356            "git-i2i-mock".to_string()
357        }
358    }
359
360    struct MockChannel {
361        live: bool,
362        inbox: Option<Vec<u8>>,
363    }
364    impl ChannelLayer for MockChannel {
365        fn bridge_send(&mut self, _channel: u8, msg: &[u8]) -> bool {
366            self.inbox = Some(msg.to_vec());
367            true
368        }
369        fn bridge_recv(&mut self, _channel: u8) -> Option<Vec<u8>> {
370            self.inbox.take()
371        }
372        fn is_live(&self) -> bool {
373            self.live
374        }
375    }
376
377    struct MockBeacon {
378        score: f32,
379        last: Option<Vec<u8>>,
380    }
381    impl BeaconLayer for MockBeacon {
382        fn emit_event(&mut self, event: &[u8]) -> bool {
383            self.last = Some(event.to_vec());
384            true
385        }
386        fn observe(&self) -> Option<Vec<u8>> {
387            self.last.clone()
388        }
389        fn trust_score(&self) -> f32 {
390            self.score
391        }
392    }
393
394    struct MockReef {
395        store: HashMap<Vec<u8>, Vec<u8>>,
396    }
397    impl ReefLayer for MockReef {
398        fn persist(&mut self, key: &[u8], state: &[u8]) -> bool {
399            self.store.insert(key.to_vec(), state.to_vec());
400            true
401        }
402        fn restore(&self, key: &[u8]) -> Option<Vec<u8>> {
403            self.store.get(key).cloned()
404        }
405        fn handoff(&mut self, _state: &[u8]) -> bool {
406            true
407        }
408    }
409
410    fn make_stack() -> ShipStack {
411        ShipStack {
412            harbor: Box::new(MockHarbor { peers: vec![] }),
413            tide_pool: Box::new(MockTidePool { buf: VecDeque::new() }),
414            current: Box::new(MockCurrent),
415            channel: Box::new(MockChannel { live: true, inbox: None }),
416            beacon: Box::new(MockBeacon { score: 1.0, last: None }),
417            reef: Box::new(MockReef { store: HashMap::new() }),
418        }
419    }
420
421    // ── Integration tests ─────────────────────────────────────────────────────
422
423    #[test]
424    fn test_ship_stack_construction() {
425        let stack = make_stack();
426        assert!(stack.channel.is_live());
427        assert_eq!(stack.current.transport_id(), "git-i2i-mock");
428        assert_eq!(stack.beacon.trust_score(), 1.0);
429    }
430
431    #[test]
432    fn test_message_roundtrip() {
433        let mut stack = make_stack();
434        let original = b"hello plato";
435
436        // send frames the message through all layers
437        let sent = stack.send(original);
438        assert_eq!(sent, b"git:hello plato");
439
440        // receive unframes back to original
441        // MockChannel.bridge_recv will return the framed bytes placed by send's bridge_send
442        // But send already called bridge_send and bridge_recv is None after that.
443        // So we simulate a fresh inbound by injecting the framed bytes directly.
444        let received = stack.receive(&sent);
445        assert_eq!(received, original.as_ref());
446    }
447}