Library / SDK
otoolep/hraftd avatar
otoolep/hraftd

hraftd: a key-value store built on Hashicorp's Raft, small enough to read

A reference use of Hashicorp's Raft implementation

1,103 stars143 forksGoMIT

At a glance

What is it?
hraftd is a Go reference implementation of Hashicorp's Raft library that exposes a replicated in-memory key-value store over HTTP, built to be studied rather than deployed. It suits an engineer who wants to see consensus in a program they can hold in their head, and the README is candid about the two things it deliberately leaves out.
Who is it for?
hraftd fits a developer who wants to understand Raft by reading a running system rather than a paper, because the whole thing is an HTTP API in front of a Raft log with a BoltDB store, and because the cluster walkthrough in CLUSTERING.md is a working script. Skip it if you need a production store, because the README points at rqlite for that and because hraftd has no read consistency control and no leader forwarding, so a client that writes to the wrong node gets an error.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 68 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Raft is for, in the project's own framing

hraftd is a reference example use of the Hashicorp Raft implementation. Raft is a distributed consensus protocol, meaning its purpose is to ensure that a set of nodes, a cluster, agree on the state of some arbitrary state machine, even when nodes are vulnerable to failure and network partitions. Distributed consensus is a fundamental concept when it comes to building fault-tolerant systems.

That framing matters because it defines what the project is demonstrating. Raft itself is not a key-value store. It is a mechanism for making a set of machines agree on an ordered log of commands, and you then apply that log to whatever state you care about. hraftd applies it to a map of strings.

The README says a simple example system like hraftd makes it easy to study the Raft consensus protocol in general, and Hashicorp's Raft implementation in particular, and that it can be run on Linux, macOS and Windows.

The project is explicitly a teaching artefact, and it says so more than once. The reference implementation is a very simple in-memory key-value store. And in the stale reads discussion it repeats that hraftd is a simple program, for the purpose of demonstrating a distributed key-value store.

There is a companion body of material behind it, which is the real difference between this and a toy. A blog post on building a distributed key-value store using Raft is the background reading, and there is a GopherCon 2023 talk called Build Your Own Distributed System Using Go, with the video and the slides linked, which explains step by step how to use the Hashicorp Raft library.

So the project is a code base with the thinking behind it, which is the right shape for a reference implementation. The Go module is `github.com/otoolep/hraftd`, the licence is MIT, and the last push was on 2026-08-16.

Two curl commands, and the HTTP interface is the whole API

The key-value interface is deliberately minimal, and it is worth noting that it is minimal in a way that makes the Raft behaviour visible.

You set a key by sending a request to the HTTP bind address, which defaults to `localhost:11000`:

bash
curl -XPOST localhost:11000/key -d '{"foo": "bar"}'

And you read it back:

bash
curl -XGET localhost:11000/key/foo

That is the entire user-facing API. There is no schema, no query language and no transaction API, because all of that would be a state machine layered on top and the point of the exercise is the state machine's substrate.

The fact that writes are a POST to a single path and reads are a GET on the key is what makes the leader-forwarding limitation legible. Because the write has to reach the leader, and because the write path does not forward automatically, a client that posts to a follower gets an error rather than a silent success. That is the correct behaviour for a system that would rather tell you than lie, and it is also a limitation you can only really understand by seeing it.

The repository structure is small and maps onto that scope. There is a `main.go` at the root, an `http/` directory for the HTTP layer, and a `store/` directory for the state machine, plus `go.mod` and `go.sum`, a `CLUSTERING.md` with the cluster walkthrough, an `appveyor.yml` for Windows CI and a `.circleci/` directory for the other platforms.

The Go module shows the dependency shape and it is worth reading. The direct requirements are `github.com/hashicorp/raft` at v1.7.0 and `github.com/hashicorp/raft-boltdb/v2` at v2.3.0, so the Raft library and the BoltDB-backed store implementation. Everything else is indirect, and the list tells you what Hashicorp's Raft pulls in: go-metrics for the metrics interface, boltdb and bbolt for the storage engine, go-hclog for logging, go-immutable-radix and go-lru for the in-memory index, go-msgpack for the wire encoding, and fatih/color with mattn/go-colorable and go-isatty for terminal output.

Bringing up three nodes on one host

The cluster walkthrough is the part of the documentation that teaches the most, because it shows the joining protocol in action.

Building requires Go 1.20 or later, and the documentation points at gvm as a great tool for installing and managing your versions of Go. The build sequence uses the old GOPATH layout:

bash
mkdir work
cd work
export GOPATH=$PWD
mkdir -p src/github.com/otoolep
cd src/github.com/otoolep/
git clone [email protected]:otoolep/hraftd.git
cd hraftd
go install

Then a single node starts with an id and a data directory:

bash
$GOPATH/bin/hraftd -id node0 ~/node0

And you can immediately write and read:

bash
curl -XPOST localhost:11000/key -d '{"user1": "batman"}'
curl -XGET localhost:11000/key/user1

The next two nodes join the first one:

bash
$GOPATH/bin/hraftd -id node1 -haddr localhost:11001 -raddr localhost:12001 -join :11000 ~/node1
$GOPATH/bin/hraftd -id node2 -haddr localhost:11002 -raddr localhost:12002 -join :11000 ~/node2

The `-join` argument is the Raft cluster join interface, and it tells a new node which existing node to contact. The `-haddr` and `-raddr` flags are the two addresses a node needs, the HTTP address for the client API and the Raft address for the peer-to-peer protocol, and the example gives each node a different port of each because they share a host. The documentation notes that this would not be necessary if each node ran on a different host.

After joining, all three nodes know the key, which you can check by reading from each one in turn. And a second key set on the first node is visible from all three, which is the replication working.

A three node cluster can tolerate the failure of a single node, and a five node cluster can tolerate the failure of two nodes, but the five node case requires that the leader contact a larger number of nodes before any change, for example setting a key's value, can be considered committed. That is the quorum arithmetic of Raft made explicit, and it is the kind of thing that is much clearer after you have run the three node version yourself.

What the state machine is, and why it is a map

The design choice that makes hraftd small is the choice of state machine, and it is worth being explicit about what was given up.

A key-value store is about the simplest state machine that is still interesting, because it has a write path that goes through consensus and a read path that does not. Set a key and the write has to be proposed, replicated and committed. Read a key and, in this implementation, any node will answer from its own copy.

The storage underneath follows from that choice. The direct dependency `github.com/hashicorp/raft-boltdb/v2` provides the BoltDB-backed implementation of Raft's log and stable storage, and the indirect dependencies show the rest of the machinery: `github.com/boltdb/bolt` and `go.etcd.io/bbolt` for the storage engine itself, `github.com/hashicorp/go-immutable-radix` for the immutable radix tree that Raft uses to track log membership and index entries, `github.com/hashicorp/go-lru` for the cache, and `github.com/hashicorp/go-msgpack/v2` for the encoding of entries in the log.

Two of those deserve a word. The immutable radix tree is what makes Raft's log index cheap to query, and the msgpack encoding is what the log entries are serialised with. Neither is chosen by hraftd, both arrive with the Raft library, and together they are a fair indication of what the machinery underneath costs.

The state itself, the map of keys, is in memory. The README calls it a very simple in-memory key-value store, and that word in-memory is the one to hold on to, because it means the durable part is the Raft log in BoltDB and the map is a projection of it that is rebuilt on restart. A node that was down while others took writes rejoins and learns about the updates that occurred while it was down, which is exactly the behaviour of a state machine catching up by replaying the log.

The metrics dependency, `github.com/armon/go-metrics`, is worth noticing too. Raft itself is instrumented, and the library can be wired to a metrics sink, which is how an operator would see election counts, commit latencies and log sizes. In a reference implementation the metrics are there because the library has them, but they are the first thing to reach for when you move from reading the code to running it.

The two things hraftd does not do, and rqlite

The limitations section of the README is short and both items are consequential, so it is worth reading them as design decisions rather than as omissions.

The first is stale reads. Because any node will answer a GET request, and nodes may fall behind updates, stale reads are possible. The documentation says again that hraftd is a simple program for the purpose of demonstrating a distributed key-value store, and points at rqlite for anyone particularly interested in learning more about the issue.

The reason rqlite is the reference is that it allows the client to control read consistency, allowing the client to trade off read-responsiveness and correctness. Read-consistency support could be ported to hraftd if necessary, and that sentence is an important admission: the feature is absent, the author knows it, and the fix is stated.

This is the honest shape of a teaching system. A client can hit any node, which is convenient for a demo where you curl three different ports to prove replication, and it is wrong for a system where a read immediately after a write must see that write. The demo optimises for legibility, and the cost is that the guarantee is weaker.

The second limitation is leader-forwarding. Automatically forwarding requests to set keys to the current leader is not implemented. The client must always send requests to change a key to the leader or an error will be returned.

So the client bears the responsibility of knowing who the leader is. That is a real simplification, because a system that forwards for you has to deal with the retry semantics, the ordering, and the possibility of a leadership change mid-request. hraftd declines that and makes the client responsible instead.

For production use of Raft, the documentation points at rqlite again, described as a production-grade example of using Hashicorp's Raft implementation, and it replicates a SQLite database rather than a map of strings. The difference in approach is instructive. rqlite puts a real database behind the log and adds the consistency controls; hraftd puts a map behind the log and leaves the consistency controls to whoever needs them.

One more behaviour is worth knowing, and it is the payoff for running the cluster at all. The README describes tolerating failure: kill the leader process and watch one of the other nodes be elected leader. The keys are still available for query on the other nodes, and you can set keys on the new leader. Furthermore, when the first node is restarted, it will rejoin the cluster and learn about any updates that occurred while it was down.

Editorial conclusion

hraftd fits a developer who wants to understand Raft by reading a running system rather than a paper, because the whole thing is an HTTP API in front of a Raft log with a BoltDB store, and because the cluster walkthrough in CLUSTERING.md is a working script. Skip it if you need a production store, because the README points at rqlite for that and because hraftd has no read consistency control and no leader forwarding, so a client that writes to the wrong node gets an error. Before you rely on it, read the stale reads section so the behaviour is not a surprise, and note that the go.mod requires Go 1.21 with a toolchain directive of go1.23.3, while the README says Go 1.20 or later. The last push was on 2026-08-16.

Frequently asked questions

What is hraftd?

It is a reference example use of the Hashicorp Raft implementation, a very simple in-memory key-value store that runs on Linux, macOS and Windows and is meant for studying the Raft consensus protocol and that library in particular.

How do I set and read a key with hraftd?

Send a POST to the HTTP bind address, which defaults to localhost:11000, with `curl -XPOST localhost:11000/key -d '{"foo": "bar"}'`, and read it back with `curl -XGET localhost:11000/key/foo`.

How do I start a three node hraftd cluster?

Start the first node with `hraftd -id node0 ~/node0`, then start two more with `-haddr`, `-raddr` and `-join :11000` pointing at the first node, using different ports for each since they share a host.

What are the limitations of hraftd?

Stale reads are possible because any node answers a GET and nodes may fall behind, there is no read consistency control, and leader-forwarding is not implemented, so the client must send write requests to the leader or it gets an error.

What should I use instead of hraftd in production?

The documentation points at rqlite, described as a production-grade example of using Hashicorp's Raft implementation, which replicates a SQLite database and lets the client control read consistency.

Official sources

  1. Issues
  2. License: MIT
  3. otoolep/hraftd on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/otoolep-hraftd.svg)](https://hysenlabs.com/projects/otoolep-hraftd)

Was this useful?