Quick Start
Set up an encrypted P2P channel in 15 minutes. This guide walks through installing cairn, pairing two peers, establishing a session, and sending messages -- in all 5 supported languages.
Step 1: Create a Node
A Node is your local peer. Creating one generates an Ed25519 identity and starts the transport layer with zero-config defaults.
- Rust
- TypeScript
- Go
- Python
- PHP
use cairn_p2p::{Node, CairnConfig, create};
let node = create(CairnConfig::default())?;
node.start().await?;
println!("Peer ID: {}", node.peer_id());
import { Node } from 'cairn-p2p';
const node = await Node.create();
console.log(`Peer ID: ${node.peerId}`);
import cairn "github.com/moukrea/cairn/packages/go/cairn-p2p"
node, err := cairn.Create()
if err != nil { log.Fatal(err) }
fmt.Println("Peer ID:", node.PeerID())
from cairn import create
node = await create()
print(f"Peer ID: {node.peer_id}")
use Cairn\Node;
$node = Node::create();
echo "Peer ID: " . $node->peerId() . "\n";
Step 2: Pair with a Peer
Pairing establishes mutual trust between two devices using a shared secret (PIN, QR code, or link). One peer initiates, the other responds.
PIN Pairing
Initiator -- generates a PIN and displays it:
- Rust
- TypeScript
- Go
- Python
- PHP
let pairing_data = node.pair_generate_pin().await?;
println!("PIN: {}", pairing_data.pin); // e.g., "A1B2-C3D4"
const { pin } = await node.pairGeneratePin();
console.log(`PIN: ${pin}`);
data, err := node.PairGeneratePin()
fmt.Println("PIN:", data.Pin)
data = await node.pair_generate_pin()
print(f"PIN: {data.pin}")
$data = $node->pairGeneratePin();
echo "PIN: " . $data->pin . "\n";
Responder -- enters the PIN displayed by the initiator:
- Rust
- TypeScript
- Go
- Python
- PHP
let peer_id = node.pair_enter_pin("A1B2-C3D4").await?;
println!("Paired with: {}", peer_id);
const peerId = await node.pairEnterPin('A1B2-C3D4');
console.log(`Paired with: ${peerId}`);
peerId, err := node.PairEnterPin("A1B2-C3D4")
fmt.Println("Paired with:", peerId)
peer_id = await node.pair_enter_pin("A1B2-C3D4")
print(f"Paired with: {peer_id}")
$peerId = $node->pairEnterPin('A1B2-C3D4');
echo "Paired with: " . $peerId . "\n";
Step 3: Establish a Session
After pairing, open an encrypted session. Sessions use a Noise XX handshake followed by a double ratchet for forward secrecy.
- Rust
- TypeScript
- Go
- Python
- PHP
let session = node.connect(&peer_id).await?;
const session = await node.connect(peerId);
session, err := node.Connect(peerId)
session = await node.connect(peer_id)
$session = $node->connect($peerId);
Step 4: Send a Message
Send encrypted data over the session. Messages are delivered through the best available transport.
- Rust
- TypeScript
- Go
- Python
- PHP
session.send("chat", b"hello from Rust").await?;
await session.send('chat', Buffer.from('hello from TypeScript'));
err = session.Send("chat", []byte("hello from Go"))
await session.send("chat", b"hello from Python")
$session->send('chat', 'hello from PHP');
Step 5: Receive Messages
Register a handler to receive incoming messages on a channel.
- Rust
- TypeScript
- Go
- Python
- PHP
let mut events = node.subscribe();
while let Some(event) = events.recv().await {
match event {
Event::MessageReceived { peer_id, channel, data } => {
println!("[{}] {}: {}", channel, peer_id, String::from_utf8_lossy(&data));
}
_ => {}
}
}
node.on('message', (peerId, channel, data) => {
console.log(`[${channel}] ${peerId}: ${data.toString()}`);
});
for event := range node.Events() {
if msg, ok := event.(cairn.MessageEvent); ok {
fmt.Printf("[%s] %s: %s\n", msg.Channel, msg.PeerID, msg.Data)
}
}
async for event in node.events():
if event.type == NodeEventType.MESSAGE_RECEIVED:
print(f"[{event.channel}] {event.peer_id}: {event.data.decode()}")
$node->on('message', function (string $peerId, string $channel, string $data) {
echo "[$channel] $peerId: $data\n";
});
Step 6: Handle Reconnection
Sessions automatically reconnect after network disruptions. Listen for state changes to update your UI.
- Rust
- TypeScript
- Go
- Python
- PHP
match event {
Event::StateChanged { peer_id, state } => {
println!("Peer {} state: {:?}", peer_id, state);
}
_ => {}
}
node.on('session_state', (peerId, state) => {
console.log(`Peer ${peerId} state: ${state}`);
});
if sc, ok := event.(cairn.StateChangedEvent); ok {
fmt.Printf("Peer %s state: %s\n", sc.PeerID, sc.State)
}
if event.type == NodeEventType.STATE_CHANGED:
print(f"Peer {event.peer_id} state: {event.state}")
$node->on('session_state', function (string $peerId, string $state) {
echo "Peer $peerId state: $state\n";
});
Connection states: connecting -> connected -> reconnecting -> connected (or disconnected).
Next Steps
- First App -- Build a complete runnable P2P chat in under 50 lines
- Guides -- Deep dive into pairing methods, sessions, and channels
- Demo Applications -- Explore working example applications