Skip to content

Mesh Networking

WARNING

This page documents the old Node.js API (createMeshNode, @vibecook/truffle). The v2 architecture (RFC 012) replaced the entire networking stack with a Rust-native Node API. The NAPI-RS bindings have not yet been updated. For the current API, see the Rust Node API and use the CLI.

Creating a MeshNode (Legacy Node.js API)

typescript
import { createMeshNode } from '@vibecook/truffle';

const node = createMeshNode({
  deviceId: 'unique-id',
  deviceName: 'My Device',
  deviceType: 'desktop',
  hostnamePrefix: 'myapp',
  sidecarPath: './sidecar',
  stateDir: './.truffle-state',
});

Configuration

FieldTypeRequiredDescription
deviceIdstringYesUnique device identifier
deviceNamestringYesHuman-readable name
deviceTypestringYesType (desktop, mobile, server)
hostnamePrefixstringYesPeer filter prefix on tailnet
sidecarPathstringYesPath to sidecar binary
stateDirstringYesTailscale state directory
authKeystringNoTailscale auth key
capabilitiesstring[]NoAdvertised capabilities
metadataRecord<string, unknown>NoCustom metadata
loggerLoggerNoCustom logger
timingMeshTimingConfigNoTiming overrides

Timing Configuration

typescript
const node = createMeshNode({
  // ... required fields
  timing: {
    announceIntervalMs: 30000,   // Device announce interval
    discoveryTimeoutMs: 5000,    // Peer discovery timeout
    heartbeatPingMs: 2000,       // Heartbeat ping interval
    heartbeatTimeoutMs: 5000,    // Heartbeat timeout
  },
});

Events

typescript
node.on('started', () => { /* Node is running */ });
node.on('stopped', () => { /* Node stopped */ });
node.on('deviceDiscovered', (device) => { /* New peer found */ });
node.on('deviceOffline', (deviceId) => { /* Peer went offline */ });
node.on('devicesChanged', (devices) => { /* Device list updated */ });
node.on('authRequired', (authUrl) => { /* Tailscale auth needed */ });
node.on('error', (error) => { /* Error occurred */ });

Using the Message Bus

typescript
const bus = node.getMessageBus();

// Subscribe to messages in a namespace
const unsubscribe = bus.subscribe('chat', (message) => {
  console.log(`${message.from}: ${message.type}`, message.payload);
});

// Broadcast to all devices
bus.broadcast('chat', 'message', { text: 'Hello everyone!' });

// Send to a specific device
bus.publish('device-id', 'chat', 'message', { text: 'Hello!' });

// Unsubscribe
unsubscribe();

Lifecycle

typescript
// Start the node
await node.start();

// Check status
console.log(node.isRunning());      // true
console.log(node.getDevices());     // BaseDevice[]
console.log(node.getLocalDevice()); // BaseDevice

// Stop the node
await node.stop();

Released under the MIT License.