# Raft Node JS Impl

> Raft Node Js Impl

- Skill: `yanacuti1121/raft-node-js-impl` (Agent Skill)
- Install (CLI): `npx skillmds@latest add yanacuti1121/raft-node-js-impl`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yanacuti1121/raft-node-js-impl/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: yanacuti1121 (https://skillmd.com/u/yanacuti1121)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yanacuti1121/raft-node-js-impl

---


# /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

```javascript
import Skiff from 'skiff'

// Each agent runs one Skiff node
const node = Skiff('agent-1', {
  // Persistent storage path for this node's log
  location: './data/raft/agent-1',

  // Cluster members (must be same across all nodes)
  peers: [
    { id: 'agent-2', hostname: '10.0.0.2', port: 9001 },
    { id: 'agent-3', hostname: '10.0.0.3', port: 9001 },
  ],

  // TCP transport settings
  transport: {
    connections: {
      hostname: '0.0.0.0',
      port:     9001,
    },
  },

  // Election timeout (ms) — randomize to avoid split votes
  electionTimeout:   { from: 150, to: 300 },
  heartbeatInterval: 50,
})
```

---

## State machine: apply committed commands

```javascript
// Commands applied to the state machine after Raft commits them
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

```javascript
// Only the leader can commit writes
async function clusterWrite(key: string, value: unknown) {
  if (!node.is('leader')) {
    // Forward to leader or wait for election
    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()
    })
  })
}

// Write (safe even across leader changes via retry)
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) {
      if (i === retries - 1) throw err
      await new Promise(r => setTimeout(r, 200))  // wait for election
    }
  }
}
```

---

## Cluster startup sequence

```javascript
// Start the node (begins listening for peers)
await node.start()

// Wait for cluster to stabilize (leader elected)
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
```

