a lightweight distributed consensus engine and replicated key-value database built in raw go from first principles. no etcd/raft, no hashicorp/raft, no heavy third party consensus libraries. just the raft paper and go's standard library.
- features
- architecture
- real-time web dashboard
- quickstart and API reference
- project structure
- how raft works in hotaru
- blog
- acknowledgments
core raft consensus
- leader election with randomized timeouts (150ms to 300ms) to prevent split vote scenarios
- log replication with strict term matching and safety checks
- automatic state recovery and persistence across node restarts
real-time web dashboard
- live visualizer built with next.js, tailwind css, framer motion, and lenis smooth scroll
- server-sent events (SSE) streaming real-time leader elections, log propagation, and state transitions directly from the go binary
- interactive console to issue proposals (SET, GET, DEL) and scale cluster membership (JOIN, LEAVE)
log compaction and snapshotting
- state machine snapshotting to trim historical log entries and bound memory usage
InstallSnapshotRPC to fast forward slow or rejoining followers
dynamic cluster membership
- live cluster scaling with
/joinand/leaveHTTP endpoints, implementing diego ongaro's single server change algorithm without cluster downtime - non voting staging phase for new nodes to catch up on logs before gaining voting rights
- automatic self removal step-down for leaders
HTTP client API and proxying
- RESTful endpoints (
/set,/get,/del,/join,/leave,/status,/events) - follower to leader request proxying for write operations
- linearizable reads via heartbeat leader validation (
VerifyLeadership)
performance optimizations
- fast forward log conflict recovery (
ConflictIndexandConflictTerm) to eliminate 1-by-1 RPC step-backs - eager replication triggers for sub 15ms proposal latency
hotaru includes an interactive real-time dashboard built with next.js and tailwind css that connects directly to the go cluster via server-sent events (SSE).
cluster topology view displaying leader status, follower roles, terms, commit indices, and live state transitions.
interactive console and log propagation visualizer showing real-time log entries replicated across all nodes.
in your terminal, launch the 3-node go cluster with dashboard mode enabled:
go run main.go --dashboardthis starts three raft nodes on ports 8000, 8001, and 8002, with HTTP API / SSE servers listening on ports 8010, 8011, and 8012.
in a separate terminal window, start the next.js development server:
cd dashboard
npm install
npm run devopen http://localhost:3000 in your browser.
- cluster topology & quorum: monitor active node roles (
leader,follower), term numbers, commit indices, last applied entries, and total log sizes in real time. - log propagation & commit state: view uncommitted vs. committed log entries replicated live across
node-0,node-1, andnode-2. - live event stream: watch real-time cluster events (leader heartbeats, elections, proposals, snapshot saves) emitted via SSE.
- interactive console:
- kv store tab: target any cluster node (e.g.
node-0 (:8010)) and executeSET,GET, orDELoperations directly. - membership tab: dynamically propose
JOINorLEAVEoperations to add or remove nodes live without cluster downtime.
- kv store tab: target any cluster node (e.g.
git clone https://github.com/pranav718/hotaru.git
cd hotaru
go run main.go| endpoint | method | query parameters | description |
|---|---|---|---|
/set |
POST |
key=foo&value=bar |
propose a key-value pair to the raft leader |
/get |
GET |
key=foo |
perform a linearizable read query |
/del |
DELETE |
key=foo |
propose deletion of a key |
/join |
POST |
id=3&addr=127.0.0.1:8003 |
stage and join a new node dynamically |
/leave |
POST |
id=3 |
safely remove a node from the cluster |
/status |
GET |
none | fetch current node status and cluster state |
/events |
GET |
none | SSE event stream emitting real-time cluster events |
# write a key-value pair
curl -X POST "http://127.0.0.1:8010/set?key=gopher&value=hotaru"
# read a key (linearizable)
curl "http://127.0.0.1:8010/get?key=gopher"
# delete a key
curl -X DELETE "http://127.0.0.1:8010/del?key=gopher"
# dynamically join a node (ID 3)
curl -X POST "http://127.0.0.1:8010/join?id=3&addr=127.0.0.1:8003"
# dynamically remove a node (ID 3)
curl -X POST "http://127.0.0.1:8010/leave?id=3"hotaru/
├── main.go # entry point, cluster bootstrap, dashboard flag
├── go.mod
├── architecture.png
├── raft/
│ ├── raft.go # core raft node state machine
│ ├── election.go # leader election and vote handling
│ ├── replication.go # log replication and AppendEntries
│ ├── rpc.go # RequestVote, AppendEntries, InstallSnapshot RPCs
│ ├── persistence.go # state persistence and recovery
│ ├── kvstore.go # replicated key-value state machine
│ ├── http.go # HTTP API server and SSE event streaming
│ └── events.go # event types and SSE broadcast
└── dashboard/
├── app/
│ ├── layout.tsx # root layout with bg.png backdrop
│ ├── page.tsx # main dashboard page
│ └── globals.css # global styles and tailwind config
├── components/
│ ├── ClusterTopology.tsx # cluster topology header and node grid
│ ├── NodeCard.tsx # individual node status card
│ ├── ControlPanel.tsx # interactive console (kv store + membership)
│ ├── LogBar.tsx # per-node log propagation bar
│ ├── LogEntry.tsx # individual log entry pill
│ ├── EventFeed.tsx # live event stream container
│ ├── EventItem.tsx # individual event log row
│ ├── InteractiveLink.tsx # micro-interaction hover links
│ └── LenisProvider.tsx # smooth scroll provider
├── hooks/
│ └── useClusterData.ts # SSE connection and cluster state hook
├── types/
│ └── raft.ts # typescript type definitions
└── public/
└── bg.png # dashboard background texture
hotaru implements the raft consensus algorithm in three phases:
-
leader election: when a cluster starts (or the current leader goes silent), nodes run randomized election timeouts. the first node to time out becomes a candidate, requests votes from peers, and wins if it gets a majority. randomized timeouts (150ms to 300ms) prevent split votes.
-
log replication: once a leader is elected, all client write requests (
SET,DEL) go through it. the leader appends the command to its local log, then replicates it to followers viaAppendEntriesRPCs. once a majority of nodes have the entry, the leader commits it and applies it to the key-value state machine. -
safety and persistence: each node persists its current term, voted-for state, and log to disk. if a node crashes and restarts, it recovers from disk and rejoins the cluster. the leader's log is always authoritative, followers that fall behind get fast-forwarded via conflict resolution or
InstallSnapshot.
i wrote a detailed blog post explaining how raft consensus works from scratch, using a playground analogy to make the hard parts click, and then walking through how i actually built hotaru step by step.
read it here: raft consensus explained simply, then built in go
- in search of an understandable consensus algorithm by diego ongaro and john ousterhout
- raft visualization for helping internalize the protocol


