Pre-1.0 hoardDB is pre-1.0. Expect breaking changes.

Limitations

hoardDB is pre-1.0. This page is the honest list of what it does not do or does not do the way you might expect. Read it before you design around it.

User management and authorization

  • Role changes apply at the next AUTH. Authorization is decided per connection from the identity established at AUTH. A grant, revoke, or alter user ... roles does not change what an already authenticated connection may do and does not terminate it; the change takes effect when that client authenticates again. If you need the change to bite immediately, change the password or disable the user (below).
  • Disable and drop close that user’s sessions. These two operations are the deliberate exception: alter user <name> disable and drop user <name> close every session that user has open on the node serving the change. grant and revoke do not.
  • User management is unavailable during a membership transition. While a node is being added to or removed from the cluster, a user mutation is refused with a retryable error. Queuing it would mean accepting a write the cluster cannot yet place. On a large cluster a rebalance is not a brief window.
  • A follower that falls behind refuses logins and refreshes. In a cluster, every node holds a full copy of the user store. A node that has not synced with the master for longer than auth_config_max_staleness_seconds (default 300, HOARDB_AUTH_CONFIG_MAX_STALENESS) refuses new password AUTHs and token refreshes until it syncs. This is the deliberate trade-off: refusing logins is an outage, accepting a deleted or disabled credential is a security failure. Set the bound to what your recovery time allows.
  • A new cluster node with no user store blocks AUTH until it receives one. A node that has a users.json serves from it while it reconciles; a node with none will not accept logins from an empty user list.
  • Revocation is per user, not per token. Changing a password, disabling a user, or a role change bumps that user’s epoch and refuses all their outstanding tokens at their next refresh. There is no way to revoke one individual token without affecting the rest.
  • users.json contains every password hash in the cluster. Decommissioning a node means deleting its users.json, exactly as it means wiping its cluster token. A member removed from the ring keeps its copy until you delete it.
  • The user store is sealed under a key your backups must keep. users.json is encrypted under the node master key when encryption at rest is on, and under the node’s auth signing key when it is off. A node that starts without that key material refuses to start rather than reconciling, and --reset-password cannot recover it — it reads the same file. Losing the key loses every user, role and epoch in the store, not just the ability to verify stored hashes. This is a wider failure than a lost password pepper used to be.

Bucket engines

  • A field’s engine is chosen when you declare it, and that ships in v1.0. Declare a store field at create bucket — Friends heapStore — or add one later with alter bucket <name> { … }. Adding a declared field is allowed in v1.0; removing one is refused, because it destroys every document’s elements with no cascade the user can see first. The model is docs/specs/nested-buckets-spec.md.
  • You cannot change a bucket’s own engine after you create it. The engine is fixed at create bucket, and indexes are declared there too. There is no alter bucket <name> { type: ... } in v1.0, so a wrong choice means dropping the bucket and creating it again. Changing the bucket’s engine is planned for v1.1, and it will land in the client libraries as well as the server: alter bucket <name> { type: <type> } converts the bucket’s engine in place, and the conversion is expensive by nature, because it walks the source engine and rewrites every entry. The design is written up in docs/specs/store-type-conversion-spec.md.

Queries

  • A scan reads one node, not the whole cluster. find() with no key, and find({filter}), are served by the node that receives them. A bucket is held by its ring owner and replicas, so a request that reaches a node that is not a holder is refused rather than forwarded: the error names the ring owner and its cluster address, and nodes lists every member. Send the query to a node that holds the bucket. A scan is an ordinary read, so on a replica that is behind, the count reflects that replica; consistent get remains the verb for a read-your-writes guarantee.
  • Paging is skip/limit over a live bucket, not a snapshot — and sort does not fix it. There is no server-side cursor: each page is a fresh scan, and a sort orders the whole matched set again before skip and limit are applied. A concurrent write between two pages can move a document across the page boundary, so a document can be seen twice (an insert ahead of the boundary) or missed entirely (a delete ahead of it). Sorting makes this worse rather than better, because a paged listing ordered by something like created_at looks reliable and is not. Stop writes before exporting a bucket that must be consistent, or read it in a single page with a limit above the match count.
  • Blob buckets do not scan. A blob’s value is binary content rather than a document; find() on a blob bucket refuses and points at info/get for a single key.
  • Date-range queries on non-key fields are full bucket scans. A btree bucket orders only its key, not its fields. A query like {ts: {$gt: ISODate("...")}} on a date field that is not the key walks every document in the bucket and filters by type and value, with no index help. Declare the date as the btree key if range queries on it are performance-critical.

Transport and sessions

  • One request in flight per socket, no multiplexing. The protocol has no stream identifier; a client that wants concurrency opens another connection. Pipelining is bounded (4 frames by default) rather than unbounded.
  • A frame is capped at 32 MiB (transport.MaxFrameLength). A blob or document larger than that must be split or streamed, and chunked streaming is not implemented yet.

Cluster

  • Cluster membership and replication are operational, but the observability is minimal. Lag and divergence are exposed through metrics, not through a richness of CLI status commands.
  • hoardDB never removes a node automatically. A peer that stays unreachable is reported; an operator decides whether and when to remove it.
  • Adding a node does not relieve disk pressure; see Is this node full?
  • User-management fencing is a reachability check, not consensus. The master confirms a strict majority of the current members are reachable before accepting a user mutation, and refuses with a retryable “no quorum” error otherwise. It is not a replicated consensus log; two nodes with divergent membership views can each believe they own the reserved key, and the term/version fence is what makes that safe.

Source and distribution

  • The source is not published yet. hoardDB is source-available — the server under Business Source License 1.1, the client libraries under Apache-2.0 — but the public repository does not exist today. The history is being prepared for publication, and the source lands when hoardDB reaches v1.0.

Source: docs/user/limitations.md in the repository.