Pre-1.0 hoardDB is pre-1.0. Expect breaking changes.
Getting Started
Five minutes from install to a working database: start the server, connect the CLI, create a bucket, ingest, query.
Every command on this page has been run against a current build. The prompts and output shown are copied from real sessions.
Prerequisites
- Go 1.25+ to build from source
- Linux or macOS (Windows via WSL2)
- No configuration file is required — the server runs with sane defaults
Install
Build from source
git clone <repo-url>
cd hoardDB/hoardDB
go build -o hoardDB-server ./cmd/server
go build -o hoardDB-cli ./cmd/cli
sudo mv hoardDB-server hoardDB-cli /usr/local/bin/
Two binaries: hoardDB-server runs the database, hoardDB-cli is the client
(an interactive REPL, or a script on stdin).
Start the Server
hoardDB-server
That is the whole first run — there is no separate init step. On first start
the server creates ./data, bootstraps a root credential (generated and printed
once, saved to ./data/root.password at 0600), generates a self-signed TLS
keypair, and starts listening. Set HOARDB_ROOT_USER/HOARDB_ROOT_PASSWORD to
choose your own credential instead:
{"level":"INFO","msg":"TCP driver listener listening","address":"0.0.0.0:7433","fingerprint":"SHA256:79bb6170…"}
Useful environment variables (all optional):
| Variable | Default | Meaning |
|---|---|---|
HOARDB_TCP_LISTEN | 0.0.0.0:7433 | Driver (client) TCP+TLS listen address |
HOARDB_DATA_DIR | ./data | Where catalogs, store files and keys live |
HOARDB_ROOT_USER | admin | Root user the server authenticates |
HOARDB_ROOT_PASSWORD | generated | Root password; if unset, generated once and saved to <HOARDB_DATA_DIR>/root.password (0600) |
HOARDB_TLS_MODE | auto | auto generates a self-signed Ed25519 keypair, custom uses HOARDB_TLS_CERT/HOARDB_TLS_KEY |
HOARDB_LOG_LEVEL | info | debug, info, warn, error |
See configuration.md for the full list.
Connect the CLI
hoardDB-cli -address 127.0.0.1:7433 -insecure
Authenticated as admin
Connected to 127.0.0.1:7433
hoardDB CLI v0.1.0
Type 'help' for help, 'exit' to quit.
[h]oardDB>
With no credential flags the CLI reads ./data/root.password from the server
started alongside it. To authenticate with your own credential, pass
-user/-password, or set the environment, which keeps them out of your shell
history:
HOARDB_ROOT_USER=admin HOARDB_ROOT_PASSWORD=… hoardDB-cli
You can also pipe commands in, which is how the examples below were captured:
hoardDB-cli -address 127.0.0.1:7433 -insecure <<'EOF'
status
exit
EOF
Create a Bucket, Insert, Query
[h]oardDB> create database CliProof
OK created database CliProof
Next: create a bucket with 'create bucket <name> { type: hash }', then run 'use CliProof'
[h]oardDB> use CliProof
Switched to database 'CliProof'
[h]oardDB [CliProof]> create bucket Users { type: hash }
OK created bucket CliProof.Users (type hash)
[h]oardDB [CliProof]> db.Users.insert({Name: "dalek_nathan", Email: "dalek@example.com", Age: 42})
OK id=bf371b61-65c7-48cc-9d4c-efe7592372ba
[h]oardDB [CliProof]> db.Users.find("bf371b61-65c7-48cc-9d4c-efe7592372ba")
{"Age": 42, "Email": "dalek@example.com", "Name": "dalek_nathan", "_id_": "bf371b61-65c7-48cc-9d4c-efe7592372ba"}
[h]oardDB [CliProof]> db.Users.count()
2
insert returns the document key. Pass your own with _id_:
[h]oardDB [CliProof]> db.Ledger.insert({_id_: "k1", amount: 10})
OK id=k1
Note the CLI’s object literals: keys may be quoted or bare — {Name: "dalek"}
and {"Name": "dalek"} are the same thing.
Declare an Index and Search
An index is declared when the bucket is created, and search answers only from
indexed fields:
[h]oardDB [CliProof]> create bucket Users { type: hash, indices: ["Email"] }
OK created bucket CliProof.Users (type hash)
Indexes: [Email]
[h]oardDB [CliProof]> db.Users.insert({Name: "dalek_nathan", Email: "dalek@example.com"})
OK id=bf371b61-65c7-48cc-9d4c-efe7592372ba
[h]oardDB [CliProof]> db.Users.search({Email: "dalek@example.com"})
Found 1 results:
{"Age": 42, "Email": "dalek@example.com", "Name": "dalek_nathan", "_id_": "bf371b61-…"}
[h]oardDB [CliProof]> db.Users.search({Name: "dalek_nathan"})
CLI error: search failed: field Name is not indexed
Searching an unindexed field fails with an explanation rather than reporting no results — “no matches” and “cannot answer that” are different things.
Working with Dates
Dates in hoardDB are stored as UTC and compared by instant, not string. The CLI recognizes date literals and provides a set of date functions.
-- Insert a document with an ISO date literal
[h]oardDB [CliProof]> db.Events.insert({_id_: "e1", Name: "Launch", ts: ISODate("2026-09-20T10:30:00Z")})
{"_id_": "e1", "Name": "Launch", "ts": {"$date": "2026-09-20T10:30:00Z"}}
-- Insert with the server's current time
[h]oardDB [CliProof]> db.Events.insert({_id_: "e2", Name: "Update", ts: now()})
{"_id_": "e2", "Name": "Update", "ts": {"$date": "2026-09-21T15:45:32Z"}}
-- Query by date range (only dates match date fields; strings return nothing)
[h]oardDB [CliProof]> db.Events.find({ts: {$gt: ISODate("2026-09-21T00:00:00Z")}})
{"_id_": "e2", "Name": "Update", "ts": {"$date": "2026-09-21T15:45:32Z"}}
-- Print as Extended JSON to preserve type through dump/restore
[h]oardDB [CliProof]> db.Events.find({}) --extjson
{"_id_": "e1", "Name": "Launch", "ts": {"$date": "2026-09-20T10:30:00Z"}}
-- Use date functions to transform
[h]oardDB [CliProof]> db.Events.insert({_id_: "e3", Name: "Next Week", ts: now().add(7, "d")})
{"_id_": "e3", "Name": "Next Week", "ts": {"$date": "2026-09-28T15:45:32Z"}}
The Other Store Types
Each store type is an engine with the verbs that fit it. create bucket <name> { type: <type> } picks one.
fifo / lifo — queues
[h]oardDB [CliProof]> create bucket Events { type: fifo }
OK created bucket CliProof.Events (type fifo)
[h]oardDB [CliProof]> db.Events.push({event: "first"})
OK id=f0ad5d61-bdee-488c-aeb1-765f24835fab
[h]oardDB [CliProof]> db.Events.length()
2
[h]oardDB [CliProof]> db.Events.peek()
{"_id_": "f0ad5d61-bdee-488c-aeb1-765f24835fab", "event": "first"}
[h]oardDB [CliProof]> db.Events.pop()
{"_id_": "f0ad5d61-bdee-488c-aeb1-765f24835fab", "event": "first"}
peek looks without removing, pop removes and returns. lifo is the same
stack-wise: last in, first out.
heap — priority queue
[h]oardDB [CliProof]> create bucket Tasks { type: heap }
OK created bucket CliProof.Tasks (type heap)
[h]oardDB [CliProof]> db.Tasks.push({priority: 3, task: "medium"})
OK id=918ab07b-dea5-4cfe-8218-88efc57abcc0
[h]oardDB [CliProof]> db.Tasks.pop()
{"_id_": "918ab07b-dea5-4cfe-8218-88efc57abcc0", "task": "medium"}
blob — binary payloads
[h]oardDB [CliProof]> create bucket Files { type: blob }
OK created bucket CliProof.Files (type blob)
[h]oardDB [CliProof]> db.Files.put({_id_: "f1", data: "hello blob", name: "hello.txt"})
OK key=f1 blob=295a63c4-13c1-452d-8f14-9b0f3c126eed 10 B
[h]oardDB [CliProof]> db.Files.get("f1")
Key: f1
Blob: 295a63c4-13c1-452d-8f14-9b0f3c126eed
Size: 10 B
Metadata: {"name": "hello.txt"}
Data:
hello blob
Text payloads print inline; binary payloads are reported as binary rather than dumped at your terminal. Write either to a file:
[h]oardDB [CliProof]> db.Files.get({_id_: "f1", out: "hello.txt"})
OK wrote hello.txt (10 B)
db.Files.info("f1") shows the metadata without the payload.
btree — ordered keys
[h]oardDB [CliProof]> create bucket Ledger { type: btree }
OK created bucket CliProof.Ledger (type btree)
[h]oardDB [CliProof]> db.Ledger.insert({_id_: "k1", amount: 10})
OK id=k1
[h]oardDB [CliProof]> db.Ledger.insert({_id_: "k2", amount: 20})
OK id=k2
[h]oardDB [CliProof]> db.Ledger.range({$gte: "k1", $lte: "k2"})
2 entries in range:
k1 {"_id_": "k1", "amount": 10}
k2 {"_id_": "k2", "amount": 20}
[h]oardDB [CliProof]> db.Ledger.first()
first: k1
{"_id_": "k1", "amount": 10}
[h]oardDB [CliProof]> db.Ledger.last()
last: k3
{"_id_": "k3", "amount": 30}
Drop a bound for an open-ended scan: db.Ledger.range({$gte: "k2"}).
Inspecting the Database
[h]oardDB [CliProof]> show buckets
BUCKETS in CliProof
---
NAME TYPE ENTRIES
Events fifo 2
Files blob 1
Ledger btree 3
Tasks heap 0
Users hash 2
[h]oardDB [CliProof]> describe bucket Ledger
BUCKET CliProof.Ledger
---
Type: btree
Entries: 3
On disk: 32.0 KiB
Indexes: (none — declare them when you define the bucket)
Path: /tmp/hoarddb-proof3/CliProof/Ledger
[h]oardDB [CliProof]> db.Users.stats()
BUCKET CliProof.Users
---
Type: hash
Entries: 2
On disk: 64.0 KiB
Indexes: Email (equality)
show databases, describe database, status, and nodes cover the rest.
Sizes are the space the bucket actually occupies on disk, not the apparent size
of preallocated storage files.
Data Survives Restarts
Database and bucket definitions — including declared indexes — are written to
<HOARDB_DATA_DIR>/catalog.json and reloaded at start-up, along with every
document:
pkill -x hoardDB-server
HOARDB_ROOT_USER=admin HOARDB_ROOT_PASSWORD=changeme hoardDB-server
[h]oardDB [CliProof]> show buckets
NAME TYPE ENTRIES
Events fifo 2
Ledger btree 3
Users hash 2
[h]oardDB [CliProof]> db.Users.search({Email: "dalek@example.com"})
Found 1 results:
Indexes are rebuilt from the catalog on start, so searches keep working.
What Is Not Available Yet
These fail loudly with an explanation rather than pretending to succeed:
- Altering a bucket — declare indexes at creation time; otherwise drop and recreate the bucket.
- Streaming (
db.Files.stream(...)) — read whole blobs withgetinstead. - Cluster membership (
join,remove-node) and replication — see replication.md for current status; this page assumes a single node. - Recreating user accounts from a backup —
hoardDB dump/restore(below) back up and restore data and bucket definitions; they do not carry user accounts. See Backup and restore.
Next Steps
- Store Types — which engine for which access pattern
- HQL Spec — the full command language
- Configuration — every environment variable
- Replication — cluster design and current status
- Backup and restore —
dump/restore, with a real drill
Source: docs/user/getting-started.md in the repository.