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}