Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/estebanrfp/gdb/llms.txt

Use this file to discover all available pages before exploring further.

Cells — Technical Documentation

Introduction

Cells is a P2P cellular mesh protocol that organizes peers into interconnected cells to achieve efficient and scalable broadcast communication.

Key Features

  • Cellular Architecture: Peers grouped into cells with bridges connecting adjacent cells
  • Dynamic CellSize: Automatically adjusts based on network size
  • Deterministic Bridges: Bridge election is a pure function of the shared roster (per-edge hash ranking)
  • Dynamic TTL: Message time-to-live calculated based on topology
  • Deduplication: Duplicate message prevention with tracking sets
  • Heartbeat: Periodic synchronization and cleanup of inactive peers

Architecture

Cell Topology

Peers are organized into a chain of cells, with power-of-two skip links between cells keeping the network diameter at O(log C):
cell-0 ←──→ cell-1 ←──→ cell-2 ←──→ cell-3 ←──→ cell-4
   │           │           │           │           │
 peers       peers       peers       peers       peers

Peer-to-Cell Assignment

Each peer maps to the cell where its rendezvous (HRW) hash score is highest:
// cell(peer) = argmax over c of hash(`${peerId}:${c}`)
const cellId = `cell-${computeCellForPeer(peerId, totalCells)}`;
Assignment is deterministic and churn-stable: every peer derives the same mapping from the shared roster, and changing the cell count relocates only a minimal fraction of peers.

Bridges

Bridges are peers selected to connect adjacent cells. Only bridges can forward messages between cells.
cell-0          cell-1
┌─────┐        ┌─────┐
│ A   │        │ D   │
│ B ●─┼────────┼─● E │  ← B and E are bridges
│ C   │        │ F   │
└─────┘        └─────┘

Bridge Selection

Bridges are elected per edge (lo, hi) as a pure function of the shared roster: candidates from both cells are ranked by a per-edge hash, and the best candidate from each side is always included — guaranteeing egress in both directions — before filling up to bridgesPerEdge.
const rank = id => hash(`${id}@${lo}~${hi}`); // per-edge ranking spreads load
Every peer derives the same election independently, so connection admission stays symmetric with no coordination messages.

Configuration

import { gdb } from 'genosdb';

// Cells with default options
const db = await gdb('mydb', { rtc: { cells: true } });

// Cells with custom options
const db = await gdb('mydb', { 
  rtc: { 
    cells: { 
      cellSize: 'auto',
      bridgesPerEdge: 2,
      maxCellSize: 50,
      targetCells: 100,
      debug: false
    }
  }
});

// With custom relay + cells
const db = await gdb('mydb', { 
  rtc: { 
    relayUrls: ['wss://my-relay.com'],
    cells: { cellSize: 10 }
  }
});

// Access room and mesh
const room = db.room;
const mesh = room.mesh;
const selfId = db.selfId;

Parameters

ParameterTypeDefaultDescription
cellSize'auto' | number'auto'Number of peers per cell. In 'auto' mode it’s calculated dynamically
bridgesPerEdgenumber2Number of bridges per connection between adjacent cells
maxCellSizenumber50Maximum peers per cell in auto mode
targetCellsnumber100Target number of cells in the network (auto mode)
debugbooleanfalseEnables debug logs in console
Note: For direct usage with GenosRTC (without GenosDB), see genosrtc-guide.md.

Dynamic CellSize

When cellSize: 'auto', the cell size is calculated automatically:
const computeOptimalCellSize = (peerCount, targetCells, maxCellSize) => {
  if (peerCount <= 10) return Math.max(2, peerCount); // single cell until 11 peers
  const computed = Math.ceil(peerCount / targetCells);
  return Math.max(10, Math.min(maxCellSize, computed));
}

Calculation Table

PeersFormulacellSize
10single cell10
100100/100 = 1 → floor10
500500/100 = 5 → floor10
1,0001000/100 = 1010
5,0005000/100 = 5050
10,00010000/100 = 100 → capped50
The cellSize is recalculated on each refreshState() to adapt to network changes.

Metrics System (PeerMetrics)

Each peer keeps health metrics for monitoring and diagnostics (bridge election itself is deterministic — see above):
class PeerMetrics {
  peerId         // Unique peer ID
  joinedAt       // Initial connection timestamp
  lastSeen       // Last recorded activity
  rttSamples[]   // Last 10 latency samples
  stability      // 0.0 - 1.0 (decreases with reconnections)
  reconnects     // Reconnection counter
  isResponsive   // true if responded to last ping
  connectedCells // Set of cells where peer has been seen
}

Computed Properties

PropertyCalculation
uptimeDate.now() - joinedAt
avgRttAverage of rttSamples (∞ if empty)
isStaleDate.now() - lastSeen > 30000
healthScoreComposite score 0.0 - 1.0

Health Score

healthScore = 
  (rttScore * 0.25) +           // 25%: Low latency = better
  (uptimeScore * 0.25) +        // 25%: Longer connected = better
  (stabilityScore * 0.30) +     // 30%: Fewer reconnections = better
  (responsivenessScore * 0.20)  // 20%: Responds to pings = better

Dynamic TTL

The message Time-To-Live is calculated based on network size:
const dynamicTTL = () => {
  const totalCells = Math.ceil(roster.length / cellSize);
  return Math.min(150, Math.ceil(Math.log2(totalCells + 1)) * 2 + 3);
}
PeersCellsTTL
5059
2002013
1,00010017
10,00020019
TTL decreases by 1 per hop. Messages with TTL ≤ 0 are not forwarded.

Message Flow

Internal Message Types

TypePurpose
statePeer state broadcast (cell, bridges, health)
msgUser message (payload from mesh.send())
pingLatency measurement request
pongPing response with timestamp

Message Structure

{
  t: 'msg',              // Type
  id: 'abc:123:456',     // Unique ID (selfId:timestamp:seq)
  ttl: 53,               // Time-to-live
  data: { ... },         // Payload
  origin: 'peer-abc',    // Originating peer
  originCell: 'cell-0'   // Origin cell
}

Routing

  1. Message in my cell: Delivered locally and bridges forward to neighboring cells
  2. Message from neighbor cell: Bridge injects into its cell and forwards to other neighbors
  3. Deduplication: Already seen messages (seen set) are not processed again
┌─────────────────────────────────────────────────────────────┐
│  Peer A sends message from cell-0                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  cell-0 ──→ cell-1 ──→ cell-2 ──→ cell-3                   │
│    │   B01    │   B12    │   B23    │                      │
│    ↓          ↓          ↓          ↓                      │
│  [A,B,C]    [D,E]     [F,G,H]    [I,J]                     │
│                                                             │
│  Route: A → cell-0 → B01 → cell-1 → B12 → cell-2 → ...    │
└─────────────────────────────────────────────────────────────┘

Heartbeat

The system periodically broadcasts state and cleans up inactive peers:
const HEARTBEAT_INTERVAL = 2000;  // 2 seconds
const PEER_TIMEOUT = 30000;       // 30 seconds

setInterval(() => {
  sendState();  // Gossip own state on change, plus a ~10s keep-alive
  
  // Remove stale peers
  for (const [id, metrics] of peerMetrics) {
    if (metrics.isStale) {
      peerInfo.delete(id);
      peerMetrics.delete(id);
    }
  }
}, HEARTBEAT_INTERVAL);

Protections

Deduplication

const seen = new Set();      // Message IDs for relay (max 5000)
const delivered = new Set(); // Message IDs delivered to handlers

Valid Peer Filtering

Before sending to specific targets, their existence is verified:
const currentPeers = new Set(Object.keys(room.getPeers() || {}));
const validTargets = targets.filter(id => currentPeers.has(id));

Stale Verification

Only peers with lastSeen within the last 30 seconds are considered active.

Public API

Connection

import { gdb } from 'genosdb';

const db = await gdb('mydb', { rtc: { cells: true } });
const room = db.room;
const mesh = room.mesh;
const selfId = db.selfId;

Messaging

// Send broadcast message
mesh.send({ type: 'chat', text: 'Hello world' });

// Receive messages
const unsubscribe = mesh.on('message', (data, fromPeerId) => {
  console.log(`Message from ${fromPeerId}:`, data);
});

// Stop listening
unsubscribe();

State

const state = mesh.getState();
// {
//   cellId: "cell-2",
//   isBridge: true,
//   bridges: ["cell-1", "cell-3"],
//   cellSize: 5,
//   dynamicTTL: 23,
//   totalCells: 20,
//   knownCells: 18,
//   health: {
//     cellId: "cell-2",
//     memberCount: 5,
//     avgHealth: 0.85,
//     responsiveRatio: 1.0
//   }
// }

Metrics

// Metrics for a specific peer
const metrics = mesh.getMetrics(peerId);
// { uptime, avgRtt, healthScore, isStale, stability, ... }

// Cell health
const health = mesh.getCellHealth('cell-2');
// { cellId, memberCount, avgHealth, responsiveRatio }

// Ping a peer (returns RTT in ms)
const rtt = await mesh.ping(peerId);

Network Information

// Info for all known peers
const peerInfo = mesh.getPeerInfo();
// Map<peerId, { cell, isBridge, bridges }>

// Roster of active peers (not stale)
const roster = mesh.getStableRoster();
// ['peer-a', 'peer-b', 'peer-c', ...]

// Known cells
const cells = mesh.getKnownCells();
// Map<cellId, { lastSeen, peerId }>

// Current cellSize
const size = mesh.getCellSize();
// 5

Cleanup

mesh.destroy();  // Stops heartbeat and cleans up resources

Events

Room Events (GenosRTC)

room.on('peer:join', peerId => { ... });
room.on('peer:leave', peerId => { ... });

Mesh Events

room.on('mesh:state', state => {
  // Own state updated
  // { cellId, isBridge, bridges, dynamicTTL, cellSize }
});

room.on('mesh:peer-state', data => {
  // Remote peer state received
  // { id, cell, bridges, health, timestamp }
});

room.on('mesh:health', healthData => {
  // Cell health update
  // { cellId, isBridge, health: { memberCount, avgHealth, ... } }
});

Internal Constants

ConstantValueDescription
SEEN_MAX5000Maximum IDs in seen set
RTT_TIMEOUT3000Ping timeout (ms)
PEER_TIMEOUT30000Time to mark peer as stale
HEARTBEAT_INTERVAL2000Heartbeat interval (ms)

Complete Example

import { gdb } from 'genosdb';

async function main() {
  // Connect with cells enabled
  const db = await gdb('my-app', { 
    rtc: { 
      cells: { cellSize: 'auto', bridgesPerEdge: 2 } 
    }
  });

  const room = db.room;
  const mesh = room.mesh;
  const selfId = db.selfId;

  // Listen for events
  room.on('peer:join', id => console.log('New peer:', id));
  room.on('peer:leave', id => console.log('Peer left:', id));

  room.on('mesh:state', state => {
    console.log(`I'm in ${state.cellId}, bridge: ${state.isBridge}`);
  });

  // Receive messages
  mesh.on('message', (data, from) => {
    console.log(`[${from}]:`, data);
  });

  // Send message
  document.getElementById('sendBtn').onclick = () => {
    const text = document.getElementById('input').value;
    mesh.send({ type: 'chat', text, author: selfId });
  };

  // Monitoring
  setInterval(() => {
    const state = mesh.getState();
    console.log(`Cells: ${state.totalCells}, TTL: ${state.dynamicTTL}`);
  }, 10000);
}

main();

Scalability

PeersCellsMax HopsConnections
10010~10~459
1,000100~100~4,599
10,000200~150~45,999
Large scale1,000+~150 (max)Scales linearly

Connection Formula

connections ≈ (peers × cellSize) + (cells × bridgesPerEdge × 2)
Compared to traditional mesh (N × (N-1) / 2), the reduction is 100x to 1000x for large networks.

Recommendations

Use CaseGDB Configuration
General chat{ rtc: { cells: true } }
Real-time games{ rtc: { cells: { cellSize: 5, bridgesPerEdge: 2 } } }
IoT / Sensors{ rtc: { cells: { cellSize: 'auto', targetCells: 200 } } }
Low latency{ rtc: { cells: { cellSize: 3, bridgesPerEdge: 2 } } }
High scale{ rtc: { cells: { cellSize: 'auto', maxCellSize: 100 } } }

Usage Comparison

ConfigurationCellsDescription
rtc: true❌ NoBasic RTC without cellular mesh
rtc: { cells: true }✅ YesCellular mesh with defaults
rtc: { cells: { ... } }✅ YesCellular mesh with custom options

Build docs developers (and LLMs) love