feat(p2p): add MeshNode integrating all production modules
New mesh_node.rs providing a production-ready node: - MeshNodeBuilder for fluent configuration - MeshConfig integration for all settings - MeshMetrics tracking for all operations - Rate limiting on incoming messages - Backpressure controller - Graceful shutdown via ShutdownCoordinator - Optional FappRouter based on capabilities - MeshRouter for envelope routing - TransportManager for multi-transport support Key APIs: - MeshNodeBuilder::new().fapp_relay().build() - node.process_incoming() with rate limiting + metrics - node.gc() for store/routing table cleanup - node.shutdown() for graceful termination 222 tests passing (203 lib + 3 fapp_flow + 16 multi_node)
This commit is contained in:
86
crates/meshservice/examples/fapp_service.rs
Normal file
86
crates/meshservice/examples/fapp_service.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
//! FAPP Service Demo
|
||||
//!
|
||||
//! Demonstrates therapist announcement and patient query flow.
|
||||
|
||||
use meshservice::{
|
||||
capabilities,
|
||||
identity::ServiceIdentity,
|
||||
router::ServiceRouter,
|
||||
services::fapp::{create_announce, create_query, FappService, Modality, SlotAnnounce, SlotQuery, Specialism},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
println!("=== FAPP Service Demo ===\n");
|
||||
|
||||
// Create identities
|
||||
let therapist = ServiceIdentity::generate();
|
||||
let patient = ServiceIdentity::generate();
|
||||
let relay = ServiceIdentity::generate();
|
||||
|
||||
println!("Therapist address: {:?}", hex(&therapist.address()));
|
||||
println!("Patient address: {:?}", hex(&patient.address()));
|
||||
println!("Relay address: {:?}\n", hex(&relay.address()));
|
||||
|
||||
// Create router with FAPP service
|
||||
let mut router = ServiceRouter::new(capabilities::RELAY);
|
||||
router.register(Box::new(FappService::relay()));
|
||||
|
||||
// Therapist creates announcement
|
||||
let announce = SlotAnnounce::new(
|
||||
&[Specialism::CognitiveBehavioral, Specialism::TraumaFocused],
|
||||
Modality::VideoCall,
|
||||
"104", // Berlin Kreuzberg
|
||||
)
|
||||
.with_slots(3)
|
||||
.with_profile("https://therapists.de/dr-schmidt")
|
||||
.with_name("Dr. Anna Schmidt");
|
||||
|
||||
println!("Therapist announces:");
|
||||
println!(" Specialisms: CBT, Trauma");
|
||||
println!(" Modality: Video");
|
||||
println!(" Location: 104xx");
|
||||
println!(" Slots: 3");
|
||||
println!(" Profile: https://therapists.de/dr-schmidt\n");
|
||||
|
||||
let msg = create_announce(&therapist, &announce, 1).unwrap();
|
||||
let action = router.handle(msg.clone(), Some(therapist.public_key())).unwrap();
|
||||
println!("Router action: {:?}", action);
|
||||
println!("Stored messages: {}\n", router.store().len());
|
||||
|
||||
// Patient creates query
|
||||
let query = SlotQuery::new(Specialism::CognitiveBehavioral, "104")
|
||||
.with_modality(Modality::VideoCall)
|
||||
.with_max_wait(30);
|
||||
|
||||
println!("Patient queries:");
|
||||
println!(" Looking for: CBT");
|
||||
println!(" Location: 104xx");
|
||||
println!(" Modality: Video");
|
||||
println!(" Max wait: 30 days\n");
|
||||
|
||||
let query_msg = create_query(&patient, &query).unwrap();
|
||||
|
||||
// Find matches
|
||||
let matches = router.query(&query_msg);
|
||||
println!("Found {} matching therapist(s):", matches.len());
|
||||
|
||||
for (i, m) in matches.iter().enumerate() {
|
||||
if let Ok(data) = meshservice::services::fapp::SlotAnnounce::from_bytes(&m.message.payload) {
|
||||
println!(" {}. {} in {}xx ({} slots)",
|
||||
i + 1,
|
||||
data.display_name.as_deref().unwrap_or("Unknown"),
|
||||
data.postal_prefix,
|
||||
data.available_slots
|
||||
);
|
||||
if let Some(profile) = &data.profile_url {
|
||||
println!(" Verify: {}", profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n=== Demo Complete ===");
|
||||
}
|
||||
|
||||
fn hex(bytes: &[u8]) -> String {
|
||||
bytes.iter().map(|b| format!("{b:02x}")).collect()
|
||||
}
|
||||
97
crates/meshservice/examples/housing_service.rs
Normal file
97
crates/meshservice/examples/housing_service.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
//! Housing Service Demo
|
||||
//!
|
||||
//! Demonstrates landlord listing and seeker query flow.
|
||||
|
||||
use meshservice::{
|
||||
capabilities,
|
||||
identity::ServiceIdentity,
|
||||
router::ServiceRouter,
|
||||
services::housing::{
|
||||
amenities, create_announce, create_query, HousingService, ListingAnnounce, ListingQuery,
|
||||
ListingType,
|
||||
},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
println!("=== Housing Service Demo ===\n");
|
||||
|
||||
// Create identities
|
||||
let landlord1 = ServiceIdentity::generate();
|
||||
let landlord2 = ServiceIdentity::generate();
|
||||
let seeker = ServiceIdentity::generate();
|
||||
|
||||
// Create router with Housing service
|
||||
let mut router = ServiceRouter::new(capabilities::RELAY);
|
||||
router.register(Box::new(HousingService::relay()));
|
||||
|
||||
// Landlord 1: Kreuzberg apartment
|
||||
let listing1 = ListingAnnounce::new(ListingType::Apartment, 65, 950, "104")
|
||||
.with_rooms(2)
|
||||
.with_amenities(amenities::FURNISHED | amenities::BALCONY | amenities::INTERNET)
|
||||
.with_title("Sunny 2-room in Kreuzberg");
|
||||
|
||||
println!("Landlord 1 announces:");
|
||||
println!(" {} sqm {} in {}xx", listing1.size_sqm, "Apartment", listing1.postal_prefix);
|
||||
println!(" Rent: {} EUR/month", listing1.rent_euros());
|
||||
println!(" Rooms: {}", listing1.rooms);
|
||||
println!(" Amenities: Furnished, Balcony, Internet\n");
|
||||
|
||||
let msg1 = create_announce(&landlord1, &listing1, 1).unwrap();
|
||||
router.handle(msg1, Some(landlord1.public_key())).unwrap();
|
||||
|
||||
// Landlord 2: Neukölln shared flat room
|
||||
let listing2 = ListingAnnounce::new(ListingType::Room, 18, 450, "120")
|
||||
.with_rooms(1)
|
||||
.with_amenities(amenities::WASHING_MACHINE | amenities::INTERNET)
|
||||
.with_title("Room in friendly WG");
|
||||
|
||||
println!("Landlord 2 announces:");
|
||||
println!(" {} sqm {} in {}xx", listing2.size_sqm, "Room", listing2.postal_prefix);
|
||||
println!(" Rent: {} EUR/month", listing2.rent_euros());
|
||||
println!(" Amenities: Washing machine, Internet\n");
|
||||
|
||||
let msg2 = create_announce(&landlord2, &listing2, 1).unwrap();
|
||||
router.handle(msg2, Some(landlord2.public_key())).unwrap();
|
||||
|
||||
println!("Total listings in store: {}\n", router.store().len());
|
||||
|
||||
// Seeker 1: Looking for affordable apartment
|
||||
println!("--- Seeker Query 1: Affordable apartment ---");
|
||||
let query1 = ListingQuery::new("10", 800) // Any 10xxx area, max 800 EUR
|
||||
.with_type(ListingType::Apartment)
|
||||
.with_min_size(40);
|
||||
|
||||
println!(" Area: 10xxx");
|
||||
println!(" Type: Apartment");
|
||||
println!(" Max rent: 800 EUR");
|
||||
println!(" Min size: 40 sqm\n");
|
||||
|
||||
let query_msg1 = create_query(&seeker, &query1).unwrap();
|
||||
let matches1 = router.query(&query_msg1);
|
||||
println!("Found {} matches:", matches1.len());
|
||||
for m in &matches1 {
|
||||
if let Ok(l) = ListingAnnounce::from_bytes(&m.message.payload) {
|
||||
println!(" - {} ({}xx, {} EUR)", l.title.as_deref().unwrap_or("No title"), l.postal_prefix, l.rent_euros());
|
||||
}
|
||||
}
|
||||
|
||||
// Seeker 2: Looking for any cheap room
|
||||
println!("\n--- Seeker Query 2: Any room under 500 EUR ---");
|
||||
let query2 = ListingQuery::new("1", 500); // Any 1xxxx area
|
||||
|
||||
let query_msg2 = create_query(&seeker, &query2).unwrap();
|
||||
let matches2 = router.query(&query_msg2);
|
||||
println!("Found {} matches:", matches2.len());
|
||||
for m in &matches2 {
|
||||
if let Ok(l) = ListingAnnounce::from_bytes(&m.message.payload) {
|
||||
println!(" - {} ({}xx, {} sqm, {} EUR)",
|
||||
l.title.as_deref().unwrap_or("No title"),
|
||||
l.postal_prefix,
|
||||
l.size_sqm,
|
||||
l.rent_euros()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n=== Demo Complete ===");
|
||||
}
|
||||
89
crates/meshservice/examples/multi_service.rs
Normal file
89
crates/meshservice/examples/multi_service.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
//! Multi-Service Demo
|
||||
//!
|
||||
//! Shows how multiple services can run on the same mesh router.
|
||||
|
||||
use meshservice::{
|
||||
capabilities,
|
||||
identity::ServiceIdentity,
|
||||
router::ServiceRouter,
|
||||
service_ids,
|
||||
services::{
|
||||
fapp::{create_announce as fapp_announce, FappService, Modality, SlotAnnounce, Specialism},
|
||||
housing::{
|
||||
amenities, create_announce as housing_announce, HousingService, ListingAnnounce,
|
||||
ListingType,
|
||||
},
|
||||
},
|
||||
verification::{TrustedVerifiers, Verification, VerificationLevel},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
println!("=== Multi-Service Mesh Demo ===\n");
|
||||
|
||||
// Create a router that handles both FAPP and Housing
|
||||
let mut router = ServiceRouter::new(capabilities::RELAY | capabilities::CONSUMER);
|
||||
router.register(Box::new(FappService::relay()));
|
||||
router.register(Box::new(HousingService::relay()));
|
||||
|
||||
println!("Registered services:");
|
||||
for (id, name) in router.services() {
|
||||
println!(" 0x{:04x} - {}", id, name);
|
||||
}
|
||||
println!();
|
||||
|
||||
// Create identities
|
||||
let therapist = ServiceIdentity::generate();
|
||||
let landlord = ServiceIdentity::generate();
|
||||
let registry = ServiceIdentity::generate();
|
||||
|
||||
// Setup trusted verifiers
|
||||
let mut verifiers = TrustedVerifiers::new();
|
||||
verifiers.add(
|
||||
registry.public_key(),
|
||||
"Health Registry",
|
||||
VerificationLevel::RegistryVerified,
|
||||
);
|
||||
router.set_trusted_verifiers(verifiers);
|
||||
|
||||
// Therapist announcement with verification
|
||||
println!("--- Adding FAPP announcement ---");
|
||||
let fapp_data = SlotAnnounce::new(&[Specialism::Psychoanalysis], Modality::InPerson, "104")
|
||||
.with_profile("https://kbv.de/therapists/12345");
|
||||
|
||||
let mut fapp_msg = fapp_announce(&therapist, &fapp_data, 1).unwrap();
|
||||
|
||||
// Registry verifies therapist
|
||||
let verification = Verification::registry(
|
||||
®istry,
|
||||
&therapist.address(),
|
||||
"licensed_therapist",
|
||||
"KBV-12345",
|
||||
);
|
||||
fapp_msg.add_verification(verification);
|
||||
|
||||
router.handle(fapp_msg, Some(therapist.public_key())).unwrap();
|
||||
println!("FAPP announcement stored (with registry verification)\n");
|
||||
|
||||
// Housing announcement
|
||||
println!("--- Adding Housing announcement ---");
|
||||
let housing_data = ListingAnnounce::new(ListingType::Studio, 35, 700, "104")
|
||||
.with_amenities(amenities::FURNISHED | amenities::INTERNET)
|
||||
.with_title("Cozy studio near therapist offices");
|
||||
|
||||
let housing_msg = housing_announce(&landlord, &housing_data, 1).unwrap();
|
||||
router.handle(housing_msg, Some(landlord.public_key())).unwrap();
|
||||
println!("Housing announcement stored\n");
|
||||
|
||||
// Summary
|
||||
println!("--- Store Summary ---");
|
||||
println!("FAPP messages: {}", router.store().service_count(service_ids::FAPP));
|
||||
println!("Housing messages: {}", router.store().service_count(service_ids::HOUSING));
|
||||
println!("Total messages: {}", router.store().len());
|
||||
|
||||
println!("\n=== Multi-Service Demo Complete ===");
|
||||
println!("\nThe mesh can route and store messages for multiple services");
|
||||
println!("using a single router instance. Each service has its own:");
|
||||
println!(" - Payload format");
|
||||
println!(" - Query matching logic");
|
||||
println!(" - Handler implementation");
|
||||
}
|
||||
Reference in New Issue
Block a user