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):

VariableDefaultMeaning
HOARDB_TCP_LISTEN0.0.0.0:7433Driver (client) TCP+TLS listen address
HOARDB_DATA_DIR./dataWhere catalogs, store files and keys live
HOARDB_ROOT_USERadminRoot user the server authenticates
HOARDB_ROOT_PASSWORDgeneratedRoot password; if unset, generated once and saved to <HOARDB_DATA_DIR>/root.password (0600)
HOARDB_TLS_MODEautoauto generates a self-signed Ed25519 keypair, custom uses HOARDB_TLS_CERT/HOARDB_TLS_KEY
HOARDB_LOG_LEVELinfodebug, 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.

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 with get instead.
  • Cluster membership (join, remove-node) and replication — see replication.md for current status; this page assumes a single node.
  • Recreating user accounts from a backuphoardDB dump/restore (below) back up and restore data and bucket definitions; they do not carry user accounts. See Backup and restore.

Next Steps

Source: docs/user/getting-started.md in the repository.