Overhaul pairing docs

This commit is contained in:
Jamie Pine 2025-12-02 06:02:16 -08:00
parent f9169473eb
commit e3ba15632f

View File

@ -11,19 +11,42 @@ Pairing uses a 12-word code to create a secure connection between two devices. T
### The Pairing Code ### The Pairing Code
Instead of complex cryptographic hashes, Spacedrive uses BIP39 mnemonic codes: Spacedrive uses BIP39 mnemonic codes for pairing, which come in two formats:
#### Text Format (Local Network Only)
A 12-word BIP39 mnemonic for manual entry:
``` ```
brave-lion-sunset-river-eagle-mountain-forest-ocean-thunder-crystal-diamond-phoenix brave lion sunset river eagle mountain forest ocean thunder crystal diamond phoenix
``` ```
These codes are: This format:
- Works only on the same local network (mDNS discovery)
- Easy to read and type - Easy to read and type
- Contain 128 bits of entropy - Contains 128 bits of entropy
- Valid for 5 minutes - Valid for 5 minutes
- Never reused - Never reused
#### QR Code Format (Local + Internet)
A JSON structure that enables both local and cross-network pairing:
```json
{
"version": 2,
"words": "brave lion sunset river eagle mountain forest ocean thunder crystal diamond phoenix",
"node_id": "6jn4e7l3pzx2kqhv..."
}
```
This format:
- Works across different networks and the internet
- Includes the initiator's node_id for pkarr discovery
- Enables automatic relay fallback
- Same 5-minute expiration
- Recommended for most use cases
### Security Model ### Security Model
The pairing protocol provides multiple security guarantees: The pairing protocol provides multiple security guarantees:
@ -33,6 +56,34 @@ The pairing protocol provides multiple security guarantees:
**Integrity**: Challenge-response prevents tampering **Integrity**: Challenge-response prevents tampering
**Forward secrecy**: New keys for each session **Forward secrecy**: New keys for each session
## Choosing a Pairing Method
### When to Use Text Codes
Text-based codes are best for:
- Devices on the same local network (home, office)
- Quick pairing without scanning QR codes
- Situations where QR scanning is inconvenient
**Limitations:**
- Only works on the same subnet
- Cannot traverse NATs or firewalls
- Requires both devices to be on the same physical or virtual network
### When to Use QR Codes
QR codes are recommended for:
- Pairing across different networks
- Remote device pairing over the internet
- Maximum reliability (falls back to relay if needed)
- Most production use cases
**Benefits:**
- Works anywhere with internet connectivity
- Automatic relay fallback for NAT traversal
- Faster on local networks (dual-path discovery)
- More reliable overall
## Pairing Process ## Pairing Process
### For the Initiator ### For the Initiator
@ -41,19 +92,27 @@ The pairing protocol provides multiple security guarantees:
<Step title="Generate Code"> <Step title="Generate Code">
Call the pairing API to generate a code: Call the pairing API to generate a code:
```typescript ```typescript
const code = await client.action("network.pair.generate", {}); const result = await client.action("network.pair.generate", {});
console.log(`Share this code: ${code.code}`);
// For local network pairing (manual entry)
console.log(`Share this code: ${result.code}`);
// For cross-network pairing (QR code)
console.log(`QR code data: ${result.qr_json}`);
// Contains: { version: 2, words: "...", node_id: "..." }
``` ```
</Step> </Step>
<Step title="Wait for Connection"> <Step title="Wait for Connection">
The device advertises on the network and waits for a joiner. The code expires The device advertises via mDNS (local) and pkarr (internet) and waits for a joiner. The code expires after 5 minutes.
after 5 minutes.
**Advertisement includes:**
- Session ID (via mDNS user_data)
- Node address published to dns.iroh.link (via pkarr)
</Step> </Step>
<Step title="Verify Joiner"> <Step title="Verify Joiner">
When a joiner connects, the initiator sends a cryptographic challenge to When a joiner connects, the initiator sends a cryptographic challenge to verify they have the correct code and own their device keys.
verify they have the correct code.
</Step> </Step>
<Step title="Complete Pairing"> <Step title="Complete Pairing">
@ -65,17 +124,33 @@ After verification, both devices exchange session keys and save the pairing rela
<Steps> <Steps>
<Step title="Enter Code"> <Step title="Enter Code">
Enter the 12-word code from the initiator: Enter the code from the initiator (text or QR):
```typescript ```typescript
// Manual entry (local network only)
await client.action("network.pair.join", { await client.action("network.pair.join", {
code: "brave-lion-sunset-..." code: "brave lion sunset river eagle mountain forest ocean thunder crystal diamond phoenix"
});
// QR code scan (local + internet)
await client.action("network.pair.join", {
code: '{"version":2,"words":"brave lion sunset...","node_id":"..."}'
});
// Manual entry with node_id (enables internet pairing)
await client.action("network.pair.join", {
code: "brave lion sunset...",
node_id: "6jn4e7l3pzx2kqhv..."
}); });
``` ```
</Step> </Step>
<Step title="Discover Device"> <Step title="Discover Device">
The system searches for the initiator using: - Local network discovery (mDNS) The system searches for the initiator using:
- Internet discovery (DHT lookup) - Relay servers (if needed) - **Local network** (mDNS) - Scans for matching session_id
- **Internet** (pkarr/DNS) - Queries dns.iroh.link for node address (requires node_id)
- **Relay servers** - Automatic fallback if direct connection fails
With QR codes, both paths run simultaneously and the first to succeed wins.
</Step> </Step>
<Step title="Prove Identity"> <Step title="Prove Identity">
@ -167,51 +242,90 @@ pub struct PairingSession {
## Discovery Mechanisms ## Discovery Mechanisms
Devices find each other through multiple methods: Devices find each other through multiple methods, depending on the pairing code format:
### Local Network (mDNS) ### Local Network (mDNS)
On the same network, devices discover each other instantly: On the same network, devices discover each other instantly using multicast DNS:
```rust ```rust
// Automatic local discovery // Initiator broadcasts session_id via user_data
discovery.add_mdns(); endpoint.set_user_data_for_discovery(Some(session_id));
// Broadcasts: "I'm pairing with session X" // Joiner listens for matching session_id
// Listens for: "I have session X" discovery_stream.filter(|item| {
item.node_info().data.user_data() == session_id
});
``` ```
### Internet (DHT) **How it works:**
- Initiator includes session_id in mDNS broadcasts
- Joiner scans local network for matching session_id
- Typically connects in 1-3 seconds
- Only works on the same subnet
For pairing across networks, devices use a distributed hash table: ### Internet (Pkarr/DNS)
For pairing across networks, Spacedrive uses pkarr to publish and resolve node addresses via DNS:
```rust ```rust
// Publish to DHT // Automatic pkarr publishing (done by Iroh)
let key = session_id.to_bytes(); .add_discovery(PkarrPublisher::n0_dns()) // Publish to dns.iroh.link
let record = PairingAdvertisement { .add_discovery(DnsDiscovery::n0_dns()) // Resolve from dns.iroh.link
device_info,
addresses: endpoint.my_addresses(),
};
dht.put_record(key, record);
// Query DHT // Joiner queries by node_id
let addresses = dht.get_record(session_id).await?; let node_addr = NodeAddr::new(node_id); // Pkarr resolves in background
endpoint.connect(node_addr, PAIRING_ALPN).await?;
``` ```
**How it works:**
- Initiator automatically publishes its address to `dns.iroh.link` via pkarr
- Record includes relay_url and any direct addresses
- Joiner queries `dns.iroh.link` with the node_id from QR code
- Pkarr returns all connection options (relay + direct)
- Takes 5-15 seconds including DNS resolution
<Info>
Pkarr uses DNS-based discovery backed by the Mainline DHT. It's more reliable than traditional DHT for NAT traversal and works globally.
</Info>
### Dual-Path Discovery
When using QR codes (with node_id), Spacedrive races both discovery methods:
```rust
tokio::select! {
result = try_mdns_discovery(session_id) => {
// Fast path: local network
}
result = try_relay_discovery(node_id) => {
// Reliable path: internet via pkarr
}
}
// First to succeed wins, other is canceled
```
This approach optimizes for speed on local networks while ensuring reliability across the internet.
### Relay Servers ### Relay Servers
When direct connection fails, devices connect through relay servers: When direct connection fails, devices automatically connect through relay servers:
```rust ```rust
// Automatic relay fallback // Relay mode configured at startup
if direct_connection_failed { .relay_mode(RelayMode::Default) // Uses n0's production relays
connection = relay.connect(remote_id).await?;
} // Automatic relay fallback during connection
endpoint.connect(node_addr, PAIRING_ALPN).await?; // Tries direct, then relay
``` ```
**Current Configuration:**
- Uses n0's default relay servers (North America, Europe, Asia-Pacific)
- Relay URLs discovered automatically via pkarr
- Custom relay support coming soon (configurable per-node)
<Info> <Info>
Relay servers only forward encrypted traffic. They cannot read your data or Relay servers only forward encrypted QUIC traffic. They cannot decrypt your data or compromise security.
compromise security.
</Info> </Info>
## Cryptographic Details ## Cryptographic Details
@ -250,6 +364,32 @@ let (tx_key, rx_key) = hkdf::expand(
); );
``` ```
### Pkarr Implementation
Spacedrive uses pkarr for decentralized node address resolution:
```rust
// Automatic publishing (initiator)
let endpoint = Endpoint::builder()
.add_discovery(PkarrPublisher::n0_dns()) // Publishes to dns.iroh.link
.bind().await?;
// Automatic resolution (joiner)
let endpoint = Endpoint::builder()
.add_discovery(DnsDiscovery::n0_dns()) // Resolves from dns.iroh.link
.bind().await?;
// Discovery happens automatically during connection
endpoint.connect(NodeAddr::new(node_id), PAIRING_ALPN).await?;
```
**How Pkarr Works:**
- Uses DNS TXT records backed by the Mainline DHT
- Records include relay URL and direct addresses
- Automatic publishing every time the node's address changes
- TTL-based caching for performance
- No manual DHT interaction required
### Transport Security ### Transport Security
All pairing communication uses encrypted channels: All pairing communication uses encrypted channels:
@ -407,10 +547,12 @@ async fn test_full_pairing_flow() {
### For Users ### For Users
1. **Share codes securely**: Use encrypted messaging or voice calls 1. **Prefer QR codes**: Use QR codes for reliability across any network
2. **Complete quickly**: Codes expire in 5 minutes 2. **Share codes securely**: Use encrypted messaging or voice calls for text codes
3. **Verify device names**: Check the paired device is correct 3. **Complete quickly**: Codes expire in 5 minutes
4. **One code at a time**: Cancel old attempts before starting new ones 4. **Verify device names**: Check the paired device is correct
5. **One code at a time**: Cancel old attempts before starting new ones
6. **Check network connectivity**: For cross-network pairing, ensure internet access
### For Developers ### For Developers
@ -431,12 +573,16 @@ Check:
### Cannot Find Device ### Cannot Find Device
Try: **For text-based codes:**
- Ensure both devices are on the same local network
- Check that mDNS is not blocked by firewalls
- Text codes only work locally - use QR codes for cross-network pairing
- Ensuring both devices are online **For QR codes:**
- Checking they're on compatible networks - Ensure both devices have internet connectivity
- Using relay servers if behind strict NATs - Check that the node_id is included in the QR code
- Generating a fresh code - Verify dns.iroh.link is accessible (not blocked by corporate firewalls)
- Try generating a fresh code
### Code Invalid or Expired ### Code Invalid or Expired