quick_start
#!/bin/bash
Mycelia Network - Quick Start Script
This gets you running in 5 minutes!
set -e
Colors for output
RED=β\033[0;31mβ GREEN=β\033[0;32mβ BLUE=β\033[0;34mβ YELLOW=β\033[1;33mβ NC=β\033[0mβ # No Color
echo -e β${BLUE}π Mycelia Network Quick Start${NC}β echo -e β${YELLOW}Building the future of decentralized social networking!${NC}β echo ββ
Check prerequisites
echo -e β${BLUE}π Checking prerequisitesβ¦${NC}β
if ! command -v cargo &> /dev/null; then echo -e β${RED}β Rust/Cargo not found. Please install Rust first:${NC}β echo βcurl βproto β=httpsβ βtlsv1.2 -sSf https://sh.rustup.rs | shβ exit 1 fi
if ! command -v node &> /dev/null; then echo -e β${YELLOW}β οΈ Node.js not found. Desktop app wonβt work without it.${NC}β else echo -e β${GREEN}β Node.js found${NC}β fi
echo -e β${GREEN}β Rust/Cargo found${NC}β
Create workspace
WORKSPACE=βunivrs-mycelia-quickstartβ echo -e β${BLUE}π Creating workspace: $WORKSPACE${NC}β
if [ -d β$WORKSPACEβ ]; then echo -e β${YELLOW}β οΈ Directory exists. Remove it? (y/n)${NC}β read -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then rm -rf β$WORKSPACEβ else echo βExitingβ¦β exit 1 fi fi
mkdir β$WORKSPACEβ && cd β$WORKSPACEβ
Initialize Cargo workspace
echo -e β${BLUE}π§ Setting up Cargo workspaceβ¦${NC}β cat > Cargo.toml << βEOFβ [workspace] members = [βmycelia-simpleβ] resolver = β2β
[workspace.dependencies] libp2p = β0.53β tokio = { version = β1.0β, features = [βfullβ] } serde = { version = β1.0β, features = [βderiveβ] } bincode = β1.3β blake3 = β1.5β uuid = { version = β1.0β, features = [βv4β] } chrono = { version = β0.4β, features = [βserdeβ] } env_logger = β0.10β rand = β0.8β hex = β0.4β ed25519-dalek = β2.0β clap = { version = β4.0β, features = [βderiveβ] } EOF
Create simple node implementation
echo -e β${BLUE}π Creating simple Mycelia nodeβ¦${NC}β cargo new βbin mycelia-simple cd mycelia-simple
Setup dependencies
cat > Cargo.toml << βEOFβ [package] name = βmycelia-simpleβ version = β0.1.0β edition = β2021β
[dependencies] libp2p = { workspace = true, features = [βtcpβ, βnoiseβ, βyamuxβ, βgossipsubβ, βmdnsβ] } tokio = { workspace = true } serde = { workspace = true } bincode = { workspace = true } blake3 = { workspace = true } uuid = { workspace = true } chrono = { workspace = true } env_logger = { workspace = true } rand = { workspace = true } hex = { workspace = true } ed25519-dalek = { workspace = true } clap = { workspace = true } EOF
Create the main implementation
cat > src/main.rs << βEOFβ //! Simple Mycelia Network Node //! A minimal implementation to get started quickly
use libp2p::{ gossipsub::{self, MessageAuthenticity, ValidationMode, Event as GossipEvent}, mdns::{Event as MdnsEvent}, noise, tcp, yamux, swarm::{NetworkBehaviour, SwarmEvent, SwarmBuilder}, PeerId, Multiaddr, }; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::io; use tokio::io::AsyncBufReadExt; use clap::Parser;
#[derive(Parser)] #[command(name = βmycelia-simpleβ)] #[command(about = βA simple Mycelia Network nodeβ)] struct Args { #[arg(short, long, default_value = β0β)] port: u16,
#[arg(short, long)]
peer: Option<Multiaddr>,
#[arg(short, long, default_value = "Anonymous")]
name: String,}
#[derive(NetworkBehaviour)] struct MyceliaBehaviour { gossipsub: gossipsub::Behaviour, mdns: libp2p::mdns::tokio::Behaviour, }
#[derive(Debug, Clone, Serialize, Deserialize)] struct SimplePost { id: String, author: String, content: String, timestamp: chrono::DateTimechrono::Utc, }
impl SimplePost { fn new(author: String, content: String) -> Self { Self { id: uuid::Uuid::new_v4().to_string(), author, content, timestamp: chrono::Utc::now(), } } }
struct MyceliaNode { swarm: libp2p::Swarm, posts: HashMap<String, SimplePost>, node_name: String, }
impl MyceliaNode { async fn new(node_name: String) -> Result<Self, Box> { let local_key = libp2p::identity::Keypair::generate_ed25519(); let local_peer_id = PeerId::from(local_key.public());
println!("π Local peer id: {local_peer_id}");
println!("π€ Node name: {node_name}");
let transport = tcp::tokio::Transport::default()
.upgrade(libp2p::core::upgrade::Version::V1)
.authenticate(noise::Config::new(&local_key)?)
.multiplex(yamux::Config::default())
.boxed();
let gossipsub_config = gossipsub::ConfigBuilder::default()
.heartbeat_interval(std::time::Duration::from_secs(10))
.validation_mode(ValidationMode::Strict)
.build()?;
let gossipsub = gossipsub::Behaviour::new(
MessageAuthenticity::Signed(local_key),
gossipsub_config,
)?;
let mdns = libp2p::mdns::tokio::Behaviour::new(
libp2p::mdns::Config::default(),
local_peer_id,
)?;
let behaviour = MyceliaBehaviour { gossipsub, mdns };
let swarm = SwarmBuilder::with_tokio_executor(transport, behaviour, local_peer_id).build();
Ok(Self {
swarm,
posts: HashMap::new(),
node_name,
})
}
async fn start_listening(&mut self, port: u16) -> Result<(), Box<dyn std::error::Error>> {
let addr = if port == 0 {
"/ip4/0.0.0.0/tcp/0".parse()?
} else {
format!("/ip4/0.0.0.0/tcp/{}", port).parse()?
};
self.swarm.listen_on(addr)?;
// Subscribe to the main topic
let topic = gossipsub::IdentTopic::new("mycelia-chat");
self.swarm.behaviour_mut().gossipsub.subscribe(&topic)?;
Ok(())
}
async fn connect_to_peer(&mut self, addr: Multiaddr) -> Result<(), Box<dyn std::error::Error>> {
self.swarm.dial(addr)?;
Ok(())
}
fn publish_post(&mut self, content: String) -> Result<(), Box<dyn std::error::Error>> {
let post = SimplePost::new(self.node_name.clone(), content);
let post_id = post.id.clone();
// Serialize and publish
let message = bincode::serialize(&post)?;
let topic = gossipsub::IdentTopic::new("mycelia-chat");
self.swarm.behaviour_mut().gossipsub.publish(topic, message)?;
// Store locally
self.posts.insert(post_id.clone(), post);
println!("π€ Published post: {}", post_id);
Ok(())
}
fn handle_received_post(&mut self, post: SimplePost) {
println!("π¨ Received post from {}: {}", post.author, post.content);
self.posts.insert(post.id.clone(), post);
}
fn list_posts(&self) {
if self.posts.is_empty() {
println!("π No posts yet. Type 'post <message>' to create one!");
return;
}
println!("π Recent posts:");
let mut posts: Vec<_> = self.posts.values().collect();
posts.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
for (i, post) in posts.iter().take(10).enumerate() {
let time = post.timestamp.format("%H:%M:%S");
println!(" {}. [{}] {}: {}", i + 1, time, post.author, post.content);
}
}
async fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let mut stdin = tokio::io::BufReader::new(tokio::io::stdin()).lines();
println!("π Mycelia node is running!");
println!("π¬ Commands:");
println!(" post <message> - Publish a post");
println!(" list - Show recent posts");
println!(" peers - Show connected peers");
println!(" quit - Exit");
println!();
loop {
tokio::select! {
line = stdin.next_line() => {
if let Ok(Some(line)) = line {
self.handle_input(line.trim()).await?;
}
}
event = self.swarm.select_next_some() => {
self.handle_swarm_event(event).await?;
}
}
}
}
async fn handle_input(&mut self, input: &str) -> Result<(), Box<dyn std::error::Error>> {
let parts: Vec<&str> = input.splitn(2, ' ').collect();
match parts[0] {
"post" => {
if parts.len() > 1 {
self.publish_post(parts[1].to_string())?;
} else {
println!("Usage: post <message>");
}
}
"list" => {
self.list_posts();
}
"peers" => {
let peer_count = self.swarm.connected_peers().count();
println!("π€ Connected peers: {}", peer_count);
for peer in self.swarm.connected_peers() {
println!(" - {}", peer);
}
}
"quit" | "exit" => {
println!("π Goodbye!");
std::process::exit(0);
}
"help" => {
println!("π¬ Available commands:");
println!(" post <message> - Publish a post");
println!(" list - Show recent posts");
println!(" peers - Show connected peers");
println!(" quit - Exit");
}
"" => {
// Empty input, do nothing
}
_ => {
println!("β Unknown command: {}. Type 'help' for available commands.", parts[0]);
}
}
Ok(())
}
async fn handle_swarm_event(
&mut self,
event: SwarmEvent<MyceliaBehaviourEvent>,
) -> Result<(), Box<dyn std::error::Error>> {
match event {
SwarmEvent::NewListenAddr { address, .. } => {
println!("π‘ Listening on {address}");
}
SwarmEvent::Behaviour(MyceliaBehaviourEvent::Gossipsub(GossipEvent::Message {
message,
..
})) => {
if let Ok(post) = bincode::deserialize::<SimplePost>(&message.data) {
// Don't show our own posts again
if post.author != self.node_name {
self.handle_received_post(post);
}
}
}
SwarmEvent::Behaviour(MyceliaBehaviourEvent::Mdns(MdnsEvent::Discovered(peers))) => {
for (peer_id, _) in peers {
println!("π€ Discovered peer: {peer_id}");
self.swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id);
}
}
SwarmEvent::Behaviour(MyceliaBehaviourEvent::Mdns(MdnsEvent::Expired(peers))) => {
for (peer_id, _) in peers {
println!("π Peer expired: {peer_id}");
self.swarm.behaviour_mut().gossipsub.remove_explicit_peer(&peer_id);
}
}
SwarmEvent::ConnectionEstablished { peer_id, .. } => {
println!("π Connected to peer: {peer_id}");
}
SwarmEvent::ConnectionClosed { peer_id, .. } => {
println!("β Disconnected from peer: {peer_id}");
}
_ => {}
}
Ok(())
}}
#[tokio::main] async fn main() -> Result<(), Box> { env_logger::init();
let args = Args::parse();
let mut node = MyceliaNode::new(args.name).await?;
node.start_listening(args.port).await?;
if let Some(peer_addr) = args.peer {
println!("π Connecting to peer: {}", peer_addr);
node.connect_to_peer(peer_addr).await?;
}
node.run().await} EOF
Build the project
echo -e β${BLUE}π¨ Building the projectβ¦${NC}β cargo build βrelease
Create a demo script
echo -e β${BLUE}π Creating demo scriptβ¦${NC}β cd .. cat > demo.sh << βEOFβ #!/bin/bash
Mycelia Network Demo
echo βπ Mycelia Network Demoβ echo βThis will start 3 nodes that can talk to each otherβ echo ββ
Start first node (bootstrap)
echo βStarting bootstrap node on port 4001β¦β RUST_LOG=info cargo run βbin mycelia-simple β βport 4001 βname βBootstrapβ & BOOTSTRAP_PID=$!
sleep 2
Start second node
echo βStarting second node on port 4002β¦β RUST_LOG=info cargo run βbin mycelia-simple β βport 4002 βname βAliceβ βpeer /ip4/127.0.0.1/tcp/4001 & ALICE_PID=$!
sleep 2
Start third node
echo βStarting third node on port 4003β¦β RUST_LOG=info cargo run βbin mycelia-simple β βport 4003 βname βBobβ βpeer /ip4/127.0.0.1/tcp/4001 & BOB_PID=$!
echo ββ echo βπ Network started!β echo βπ± Open 3 terminals and connect to each node:β echo β Node 1 (Bootstrap): telnet localhost 4001β echo β Node 2 (Alice): telnet localhost 4002β echo β Node 3 (Bob): telnet localhost 4003β echo ββ echo βπ¬ Try posting messages and see them propagate!β echo β Commands: post , list, peers, quitβ echo ββ echo βPress Ctrl+C to stop all nodesβ¦β
Handle cleanup
trap βecho ββ; echo βStopping nodesβ¦β; kill $BOOTSTRAP_PID $ALICE_PID $BOB_PID 2>/dev/null; exitβ INT
wait EOF
chmod +x demo.sh
Create README
cat > README.md << βEOFβ
π Mycelia Network - Quick Start
Youβve successfully set up a minimal Mycelia Network node!
What You Have
A working P2P network node built with libp2p
Peer discovery using mDNS (finds other nodes automatically)
Content sharing via gossipsub protocol
Simple command-line interface
Running Your First Network
Single Node
cargo run --bin mycelia-simple -- --name "YourName"Multi-Node Network
# Terminal 1 - Bootstrap node
cargo run --bin mycelia-simple -- --port 4001 --name "Bootstrap"
# Terminal 2 - Connect to bootstrap
cargo run --bin mycelia-simple -- --port 4002 --name "Alice" --peer /ip4/127.0.0.1/tcp/4001
# Terminal 3 - Another peer
cargo run --bin mycelia-simple -- --port 4003 --name "Bob" --peer /ip4/127.0.0.1/tcp/4001Or Use the Demo Script
./demo.shCommands
Once your node is running, try these commands:
post Hello, Mycelia Network!- Publish a messagelist- Show recent postspeers- Show connected peershelp- Show all commandsquit- Exit
Whatβs Next?
This is just the beginning! From here you can:
Add Math Integration: Connect your dynamic-math platform for social algorithms
Create Communities: Build governance and moderation features
Add Desktop UI: Create the Tauri app for a better user experience
Deploy to Cloud: Set up bootstrap nodes for a real network
Build Mobile Apps: Create React Native or Flutter apps
Features to Add
User reputation systems
Community governance
Content moderation
File sharing
End-to-end encryption
Mobile apps
Web interface
Architecture
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Node A βββββΊβ Node B βββββΊβ Node C β
β (Bootstrap)β β (Alice) β β (Bob) β
β β β β β β
β Port: 4001 β β Port: 4002 β β Port: 4003 β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β² β² β²
β β β
βββββββββββββββββββββΌββββββββββββββββββββ
β
π‘ P2P Network
(libp2p + gossipsub)Happy building! π EOF
echo ββ echo -e β${GREEN}β Mycelia Network Quick Start is ready!${NC}β echo ββ echo -e β${BLUE}π To get started:${NC}β echo -e β 1. cd $WORKSPACEβ echo -e β 2. cargo run βbin mycelia-simple β βname "YourName"β echo -e β 3. Type βpost Hello, Mycelia!β to publish your first messageβ echo ββ echo -e β${BLUE}π For a multi-node demo:${NC}β echo -e β 1. cd $WORKSPACEβ echo -e β 2. ./demo.shβ echo ββ echo -e β${YELLOW}π Read README.md for more details${NC}β echo ββ echo -e β${GREEN}π Welcome to the Mycelia Network!${NC}β echo -e β${GREEN} βOf the people, by the people, and for the people!β${NC}β EOF
chmod +x quick_start.sh
Last updated