| name | raft-node-js-impl |
| description | Node.js-native Raft consensus implementation for embedding distributed consensus directly into agent processes. In-process leader election, log replication via TCP, and state machine callbacks without external services. Sources: skiff-project/skiff (MIT). |
/raft-node-js-impl
When to Use
- Embed Raft consensus inside the Node.js agent process — no external etcd/ZooKeeper
- Yamtam Swarm: elect a coordinator agent from a cluster of 3+ agents in-process
- Lightweight: no JVM, no separate binary — pure Node.js TCP-based Raft
- Prototype consensus behavior before committing to a full distributed database
Do NOT use for
- Production clusters requiring battle-tested Raft (use [[etcd-distributed-config]])
- Cross-language clusters (this is Node.js only)
Skiff node setup
import Skiff from 'skiff'
const node = Skiff('agent-1', {
location: './data/raft/agent-1',
peers: [
{ id: 'agent-2', hostname: '10.0.0.2', port: 9001 },
{ id: 'agent-3', hostname: '10.0.0.3', port: 9001 },
],
transport: {
connections: {
hostname: '0.0.0.0',
port: 9001,
},
},
electionTimeout: { from: 150, to: 300 },
heartbeatInterval: 50,
})
State machine: apply committed commands
node.on('applied', (entry) => {
const cmd = JSON.parse(entry.toString())
console.log('[raft-node] applied:', cmd)
switch (cmd.type) {
case 'SET':
stateStore.set(cmd.key, cmd.value)
break
case 'DELETE':
stateStore.delete(cmd.key)
break
}
})
node.on('leader', () => console.log('[raft-node] became leader'))
node.on('follower', () => console.log('[raft-node] became follower'))
node.on('candidate', () => console.log('[raft-node] election in progress'))
Write through the cluster
async function clusterWrite(key: string, value: unknown) {
if (!node.is('leader')) {
throw new Error(`[raft-node] not leader — current leader: ${node.leader()}`)
}
await new Promise<void>((resolve, reject) => {
const cmd = JSON.stringify({ type: 'SET', key, value })
node.command(Buffer.from(cmd), (err) => {
if (err) reject(err)
else resolve()
})
})
}
async function safeWrite(key: string, value: unknown, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await clusterWrite(key, value)
return
} catch (err: any) {
(i === retries - ) err
( (r, ))
}
}
}
Cluster startup sequence
await node.start()
await new Promise<void>((resolve) => {
const check = () => {
if (node.leader()) { resolve(); return }
setTimeout(check, 100)
}
check()
})
console.log('[raft-node] cluster ready, leader:', node.leader())
Anti-Fake-Pass Checklist
❌ Only 2 nodes → majority = 2, one failure = cluster halted; always use odd N ≥ 3
❌ Same port on all nodes on one machine → bind conflict; use distinct ports per node
❌ node.command() before node.start() → commands silently dropped
❌ No retry on write → leader election mid-write returns error; must retry after election
❌ Not handling 'leader' event to update routing table → clients still writing to old leader
❌ location path not persisted → node restarts with empty log, rejoins as fresh peer, replays from peers