Storage shaped by how you read.
You pick the store that matches how you read, and every user, order or device keeps its own queues, maps and files. Give each user a stack of photos and each post a queue of comments: you choose the structure when you declare the bucket, and each reads back in its own order, newest photo first, oldest comment first.
You have been building stacks out of tables
A newest-first list of photos for each user usually means a table, a user
column and a timestamp column, an index over the two, and
ORDER BY … DESC LIMIT 20 on every read. A comment thread is
the same list the other way round. Each one is a data structure you
assembled out of rows and an index.
In hoardDB, the data structure is the thing you declare, on the document it belongs to. You choose it every time: the stack is Ada’s own, the queue is that post’s own.
There are six stores: hash, b-tree, FIFO, LIFO, heap and blob. A bucket declares each as a field, and every document gets its own.
In ring mode a heap store has no push and a blob store has no put yet, and a store's type cannot be changed after it is declared. Indexes are not available in ring mode yet.
Each store reads back in its own order: a hash by key, a b-tree in key order, a FIFO queue oldest first, a LIFO stack newest first, a heap by priority, a blob as raw bytes.
In ring mode a heap store reads by priority but cannot be pushed to yet, and a blob store can be counted but not put to.
Data lives with what it describes: each user, order or device keeps its own stores, one set for each document, rather than a few big shared ones.
How many buckets or documents one node can hold has not been measured. No figure appears on this site until it has.
From source to a working stack in five minutes
Build it, start it, give one user a stack of photos and one post a queue of comments, and read them back.
Building from source is the only install path today; there are no release binaries. The repository is not public yet, so there is nothing to clone.
Build it, choose a root password, start it. No flags, no configuration file.
$ go build -o hoarddb-server ./cmd/server $ go build -o hoarddb ./cmd/cli $ export HOARDDB_ROOT_PASSWORD='<choose one>' $ ./hoarddb-serverWhat the server prints on its first start
{"time":"2026-11-03T11:17:12.349008748Z","level":"INFO","msg":"umask restricted: new files are owner-only","previous":"0002","applied":"0077"} {"time":"2026-11-03T11:17:12.349713622Z","level":"INFO","msg":"hoardDB server starting","node_id":"<this machine>","listen":"127.0.0.1:4433","version":"version=devel commit= build_date=","go_memory_limit":"21.5 GiB, 75% of this node's memory limit of 28.7 GiB (the host's MemTotal; no cgroup memory limit is set); set GOMEMLIMIT to choose another"} {"time":"2026-11-03T11:17:12.34974776Z","level":"INFO","msg":"listen addresses","client_listen":"127.0.0.1:7433","internode_listen":"127.0.0.1:4434","ring_address":"127.0.0.1:4433"} {"time":"2026-11-03T11:17:12.349762646Z","level":"INFO","msg":"this node listens on this machine only (clients on 127.0.0.1:7433). To accept clients from other machines set HOARDDB_TCP_LISTEN=0.0.0.0:7433; to join other machines in a cluster set HOARDDB_LISTEN to this node's address (for example 192.0.2.1:4433), which also moves the internode port. See the security page of the user docs"} {"time":"2026-11-03T11:17:12.350008685Z","level":"INFO","msg":"cluster token generated (first start)","saved_to":"data/cluster.token"} === CLUSTER TOKEN === A cluster token was generated and saved in data/cluster.token (mode 0600). It is not shown here because this output is not a terminal. To read it once the server is up: echo get-token | HOARDDB_DATA_DIR=./data HOARDDB_ROOT_USER=admin HOARDDB_ROOT_PASSWORD='<its password>' hoarddb -insecure or read the "token" field of that file. A node joining this cluster needs the same token: start it with HOARDDB_CLUSTER_TOKEN set to that value. ===================== {"time":"2026-11-03T11:17:12.352174109Z","level":"INFO","msg":"auth signing key ready","kid":"dbf9b066","source":"loaded from data/auth.token.key"} badger 2026/11/03 11:17:12 INFO: All 0 tables opened in 0s badger 2026/11/03 11:17:12 INFO: Discard stats nextEmptySlot: 0 badger 2026/11/03 11:17:12 INFO: Set nextTxnTs to 0 {"time":"2026-11-03T11:17:12.355776237Z","level":"INFO","msg":"memtable: 128 MiB, chosen now for this new store from this node's memory limit of 28.7 GiB (the host's MemTotal; no cgroup memory limit is set). Write bursts can peak near 1.25 GB of memory at this size; the largest atomic batch is 20 MB. The choice is saved in data/store/memtable-choice.json and kept on every restart, even if this host's memory changes. To use another size, set storage.memtable_mb (32 to 1024) in the config file.","component":"storage"} {"time":"2026-11-03T11:17:12.356603075Z","level":"INFO","msg":"capacity gauges are not exported (HOARDDB_METRICS_ENABLED is false); capacity warnings still go to this log and to hoardDB status"} {"time":"2026-11-03T11:17:12.3566574Z","level":"INFO","msg":"encryption at rest","mode":"off"} {"time":"2026-11-03T11:17:12.356700371Z","level":"INFO","msg":"catalog loaded","databases":0,"buckets":0} {"time":"2026-11-03T11:17:12.357522627Z","level":"INFO","msg":"memory limit is the host's MemTotal (28.7 GiB): no cgroup memory limit is set on this process's cgroup or any ancestor it can see","limit_bytes":30786101248} {"time":"2026-11-03T11:17:12.41980968Z","level":"INFO","msg":"root user seeded","username":"admin","roles":["root"]} {"time":"2026-11-03T11:17:12.4200362Z","level":"INFO","msg":"root user bootstrapped into users.json","username":"admin","path":"data/users.json"} badger 2026/11/03 11:17:12 INFO: All 0 tables opened in 0s badger 2026/11/03 11:17:12 INFO: Discard stats nextEmptySlot: 0 badger 2026/11/03 11:17:12 INFO: Set nextTxnTs to 0 2026-11-03T11:17:12.685Z [WARN] raft: heartbeat timeout reached, starting election: last-leader-addr= last-leader-id= {"time":"2026-11-03T11:17:12.687366942Z","level":"INFO","msg":"entry-ring: write-ahead log segments are not preallocated (storage.wal_preallocate=auto: tmpfs is memory, so it would cost RAM)","mode":"off","configured":"auto","filesystem":"tmpfs","reason":"tmpfs is memory, so it would cost RAM","component":"storage"} {"time":"2026-11-03T11:17:12.687418198Z","level":"INFO","msg":"entry-ring node open","dir":"data/ring"} {"time":"2026-11-03T11:17:12.687631719Z","level":"INFO","msg":"TCP driver listener listening","address":"127.0.0.1:7433","fingerprint":"SHA256:6c4d7d6464a078591f46c4c4e0daf07ea854c5024a99e92805468acda94b59cf","node_id":"<this machine>","alpn":"hoarddb/1"} {"time":"2026-11-03T11:17:12.68769492Z","level":"INFO","msg":"internode TCP listener listening","address":"127.0.0.1:4434","alpn":"hoarddb/1"} {"time":"2026-11-03T11:17:12.687703975Z","level":"INFO","msg":"metrics server disabled"}$ test -f data/users.json && echo 'the root user is bootstrapped; only the password hash is stored' $ grep -rlF "$HOARDDB_ROOT_PASSWORD" data/ || echo 'no file under data/ holds the password'the root user is bootstrapped; only the password hash is stored no file under data/ holds the passwordConnect in ring mode. The CLI takes the password you chose, from the environment.
$ ./hoarddb -ring -address 127.0.0.1:7433 -insecureGive one user a stack of photos, and one post a queue of comments.
create database app use app create bucket User { Name string, Photos lifoStore } create bucket Post { Title string, Comments fifoStore } db.User.insert({_id_: "ada", Name: "Ada"}) db.User["ada"].Photos.push({File: "beach.jpg"}) db.User["ada"].Photos.push({File: "dinner.jpg"}) db.User["ada"].Photos.push({File: "sunset.jpg"}) db.Post.insert({_id_: "p1", Title: "Albania"}) db.Post["p1"].Comments.push({By: "lin", Text: "Where was this?"}) db.Post["p1"].Comments.push({By: "ada", Text: "Albania, last June."})WARNING: TLS certificate verification is disabled (--insecure); the connection to 127.0.0.1:7433 cannot be trusted and may be intercepted. Use this only against a local development server. Authenticated as admin Connected to 127.0.0.1:7433 hoardDB CLI devel (ring mode) Type 'help' for help, 'exit' to quit. OK created database app (replication factor 1, this node's default) Next: use app Then: create bucket <name> { Name string, Purchases fifoStore } (declare every field) Switched to database 'app' OK created bucket app.User (2 fields) Fields: Name string, Photos lifoStore OK created bucket app.Post (2 fields) Fields: Title string, Comments fifoStore OK id=ada OK pushed: Photos now holds 1 element OK pushed: Photos now holds 2 elements OK pushed: Photos now holds 3 elements OK id=p1 OK pushed: Comments now holds 1 element OK pushed: Comments now holds 2 elements Goodbye.Read them back. The stack gives the newest photo; the queue gives the oldest comment.
use app db.User["ada"].Photos.peek() db.Post["p1"].Comments.peek() db.User["ada"].Photos.count()WARNING: TLS certificate verification is disabled (--insecure); the connection to 127.0.0.1:7433 cannot be trusted and may be intercepted. Use this only against a local development server. Authenticated as admin Connected to 127.0.0.1:7433 hoardDB CLI devel (ring mode) Type 'help' for help, 'exit' to quit. Switched to database 'app' {"File": "sunset.jpg"} {"By": "lin", "Text": "Where was this?"} 3 Goodbye.
Captured 2026-11-03T11:17:12Z from commit 5ac6c739017c
on linux/amd64. Replaced before publishing: the root password, the cluster token and the machine name; nothing else is edited. A script
builds the server and the CLI from that commit and records what they print;
this page renders the record, and the build refuses a record taken before the
last change to either.
hoardDB is not wire-compatible with any other database and does not aim to be. HQL is inspired by JSON syntax; it is a different language from every other query language.
TLS and a password before you configure anything
hoarddb-server starts with no arguments and no configuration file.
On first start it generates a self-signed TLS keypair and serves over TLS.
The certificate is self-signed: a first client connection trusts it on first use and records its fingerprint. It is not a CA-issued certificate.
Authentication is always required. There is no flag that turns it off.
The CLI's -insecure flag is a client-side TLS verification bypass. It is not a server authentication switch, and there is no server-side equivalent.
The first start creates a root user from the password you choose and keeps only its hash in ./data/users.json; the password is never printed and never written to disk.
One binary. Yours to run.
The database is a single binary, with no sidecar and no separate indexer to run.
The CLI is a second binary, and it is a client rather than a component of the database.
Run one node, or form a cluster: join adds a node to a cluster that holds no data, and gives it a vote while the cluster has a free voter slot.
Join is refused once any database exists, so form the cluster first and create data after. A node that finds no free voter slot joins without a vote, and promote gives it one after a slot is freed. Copying data between nodes is not usable end to end yet, so do not count on a second copy of anything.
Free to self-host, including commercially and in production.
Check it against the code
The documentation on this site is the documentation in the repository. There is no second copy.