Pre-1.0 hoardDB is pre-1.0. Expect breaking changes.
Compatibility and versions
What you can rely on when you upgrade hoardDB: what talks to what, what is read back from disk, what happens when you run two builds at once, which platforms are built, and how a feature is retired.
hoardDB is pre-1.0 and no release has been tagged yet. Where this page says “today”, it describes the build in the repository. Where it states a rule, the rule is the project’s commitment from v1.0 on.
What carries a version
Three things are versioned, separately from the release number, because they have different consequences when they change.
| What | Where you see it | Today |
|---|---|---|
| Wire protocol | hoarddb-server -v and hoarddb -v print protocol=N; the TLS handshake negotiates hoarddb/N | major 1 |
| Data directory | Checked by the server at start | storage layout version 2 (the shared store) |
| Dump format | format_version in a dump’s manifest.json and each bucket’s metadata.json | 3 (format 2 still restores) |
The release (or devel for an untagged build) is printed by -v too:
hoarddb-server -v
hoarddb-server version=devel commit= build_date= protocol=1
The wire protocol
A client and a server speak the same protocol major or they do not talk.
- The major is negotiated before anything is sent. Both sides offer
hoarddb/1in the TLS handshake (the ALPN identifier), and every frame also carries the version in its header, so a connection can never mix versions. - A mismatch is named, not left to a decode failure later. A client that
offers no major the server serves is refused in the handshake, and one that
states a different major at login gets
server speaks protocol 1, client sent 2. - Inside a major, change is additive only. New operations, new optional
fields and new capability names can appear. A client ignores fields it does
not know, and a server ignores capability names it does not know. A server
that is asked for an operation it does not have answers
unsupported opcodeand the opcode’s number, never silence. - A breaking change gets a new major. It is never a silent reinterpretation
of
hoarddb/1. The server keeps serving the previous major for at least one release after a new one ships, and warns on every connection that uses the old one, with the date it will stop.
Today only major 1 exists, so no client has had to move yet. The one thing to
check when you mix versions is the line above: hoarddb -v and
hoarddb-server -v should print the same protocol=.
The data directory
The server reads what is on disk at start and refuses what it does not understand, instead of guessing:
- The storage layout. This build keeps every bucket in one shared store per
node. A data directory written by the earlier layout (it has a
catalog.jsonof its own) is refused at start with a message that says so, and is not converted in place. Back it up with the earlier build’shoarddb dump, then restore into a fresh data directory on this build. See Backup and restore. users.json. It carries its ownformat_version(today1), and a file with another version is refused. It is sealed under a key your backups must keep; see User management.- Encryption. A node’s encryption mode is fixed when its data directory is first started. A data directory started with encryption on is refused by a server configured without it, and the reverse; see Configuration.
- Sealed credential files. On a node with encryption at rest, the cluster
token, the signing key and the TLS private key are sealed under the node key.
A node that finds them in plaintext seals them in place at its next start, and
that is one-way: a build from before this release cannot read a sealed file,
so do not start the data directory with it. A sealed file belongs to its node;
copy a token to a new node with
get-token, not by copying the file. See Security. - The write-ahead log. A log written by an earlier build is read as it is, with nothing to convert; the entries this build adds to it carry more than the earlier ones did.
- The memtable size a store chose at its first start is saved with the store
and kept on every restart (
store/memtable-choice.json). - Dumps.
restoreaccepts the dump format it writes (format_version3) and format2, which a build before the dead-entry section wrote, and refuses any other with the version on both sides in the message. There is no format 1 to restore. A format 3 dump may carry a<bucket>.dead.bsonfile for a queue’s or heap’s dead entries; a restore keeps them dead.
Upgrade every member before a node publishes a client address
A cluster member that sets tcp.advertise (or listens on an address other machines can dial) writes that address into the cluster state. A member running an earlier build cannot read a cluster command or snapshot that carries it: it stops applying changes, its view of the cluster stays behind for good, and it cannot restart from a snapshot taken after the address was written. There is no negotiation between builds before the first release. Upgrade every member first, then add or restart the nodes that publish an address. A member that has already stopped advancing is repaired by replacing it (join --replace).
Going back is not supported
A node that has written to its log on a newer build cannot be started on an older one. The older build opens the data directory but reads the newer log entries as a damaged tail and discards them, and after a crash the copies of a bucket can silently disagree about writes that were acknowledged. The first time a node writes the newer format it logs one line saying so.
A node that keeps a write-ahead log (a replication factor above 1, or seed nodes) writes it in segment format 2 from its first start on this build, whatever storage.wal_preallocate says, and an earlier build refuses to start on it instead of discarding it: it stops with an error that ends parsing sealed file header: flags must be 0x00, got 01 at offset 6 (03 on a sealed node) and changes nothing. That error means this data directory was written by a newer hoardDB: start the newer build, or restore the copy taken before the upgrade.
The log’s segment format is not the setting: storage.wal_preallocate only
turns the zero fill on. Changing that setting is a rolling restart of the same
build: change it on one node and restart it, wait until
hoarddb_replication_peers_not_in_sync is 0 and status shows the expected
WAL row, then do the next. Upgrading to the build that introduces format 2 is
not a rolling operation: stop every node, copy each data directory, install,
start, as below.
So the way back is a backup, not the old binary: copy the whole data directory before you upgrade, and go back by restoring that copy on every node. Limitations and Replication have the details.
Upgrading
No tagged release exists yet, so there is no earlier release to upgrade from. This is the procedure the current builds require, and it is the one to follow for every upgrade that touches a data directory.
- Stop writes and wait one minute. An earlier build has no gauge that tells you the log has caught up, so the wait is the margin.
- Stop every node.
- Copy each node’s whole data directory.
dumpis not a substitute: it leaves out blob payloads and user accounts. A copy of the directory keeps both. - Install the new build on every node.
- Start every node. Check
hoarddb-server -von each, thenstatusandnodesfrom the CLI. The new build reads each node’s log as the earlier build left it.
Rolling upgrades
A cluster is not upgraded one node at a time. Do not run two builds side by
side in one cluster. Builds can disagree about where a bucket lives, and
nothing detects that disagreement; and a node that runs an earlier build cannot
pull data from an upgraded node until it is upgraded too. Data is pulled when a
node joins, when one is removed, when the cluster rebalances and when you run
migrate, so a cluster of mixed builds can stall in the middle of one of those.
The rule that follows: upgrade every node to the same build before you add a
node, remove one or run migrate, and if you find a cluster of mixed builds,
finish the upgrade first. Upgrading a cluster one node at a time, with no outage,
is planned for v1.1. Until a release says it is delivered, the stop-all rule
above is the only supported procedure.
Supported platforms
The server and the CLI are released for two platforms:
| Operating system | Architecture |
|---|---|
| Linux | amd64, arm64 |
Windows is supported through WSL2, using the Linux binaries. The server and the
CLI do not compile for native Windows, so there is no .exe to build. macOS is
not supported natively in this release; on a Mac, run the Docker image. Native
macOS is planned for v1.2.
What “supported” means, precisely: the platform build produces each file and
checks that it is the architecture its name says (see Installation), and the captured session in
Getting started was run end to end on linux/amd64.
A platform outside that table (macOS, for instance) may compile, but it is not
tested or supported before v1.2.
Building from source needs the Go version named in go.mod (Go 1.26.9
at the time of writing).
How a feature is retired
The rule (and the reason for it): hoardDB never breaks your data or your programs silently. Trust is the whole product.
- Deprecate loudly. A feature that is going away — a setting, a CLI verb or flag, an HQL statement, a configuration key — is deprecated in a minor release and keeps working until the next minor release and at least 90 days after the deprecation shipped, whichever is later. Each time it is used it prints a warning on standard error that names what to use instead and the date the old way stops. Removing a deprecated feature does not need a major release; the removal is listed under Removed in the release notes.
- Version explicitly. A change a program could notice — a wire change, a changed meaning of a setting, a new on-disk layout — is named in the release notes under what changed and what to do about it, and a wire change raises the protocol major as described above.
- Refuse, do not guess. When an old form is no longer accepted, it fails
with a message that says what replaced it. For example, the old
define bucketstatement is refused on purpose with the textdefine was renamed: use 'create bucket <name> { type: hash }', so an old script stops at once instead of doing something different. - Never lose data on the way. An upgrade that cannot read your data stops and says so. It does not discard what it does not understand.
Before v1.0 this was not always possible. Changes made before a release —
for instance, the environment variable prefix that became HOARDDB_ — were
made without a deprecation period, and the release notes record them. From v1.0 the
rules above apply.
Source: docs/user/compatibility.md in the repository.