HeatherDB

Search documentation

Loading the documentation index…

Download
HeatherDB documentation
DocumentationReference

Parameter reference

Every knob, what it controls, and what a wrong value looks like from outside.

Configuration says where each setting lives and what its default is. This page says how to choose one.

Source of truth: heather_db/src/config.rs, db_config.rs, collection.rs, heather_server/src/main.rs, limits.rs, models.rs.

Mutability

Three classes, and the first two are the ones that hurt.

ClassKnobsRule
Immutable after creationdimension (eam.d)The stored codebook is dimensioned. Create a new database and re-write the data. A mismatch is loud (400 DimensionMismatch)
Restart requiredmap_size_mb, every [eam] field, [dream], [audit]Edit db.toml and restart. LMDB refuses to reopen an environment with different options in the same process
Per requeststrategy, scale, exclude_id, alpha, threshold, power, max_cross_k, routing_sharpness, n, cleanup, kappa, lambda (compress)Free to vary call to call

No EAM knob can differ per collection. A collection is mounted with the database's config (hive.rs:99), so two workloads that need different beta, k or tau_overload need two databases.

Six [eam] fields — k, t_max, beta, neighbor_cap, num_landmarks, l_0 — are settable at creation through POST /db's eam object. The rest need a file edit.

Dimension and storage

KnobSet atControlsWrong value looks likeChoosing
dimension / eam.dPOST /db, --dimension for default on first boot onlyVector width for the whole database. ImmutableToo low: reads blend, schema slots collapse, capacity walls arrive early. Too high: proportional memory and CPU per read, nothing elseChoose for the interference you want, not for the encoder's native width. An encoder wider than the database is projected down (see Encodings); one narrower is zero-padded. Start from Capacity: D/32 items per pool, √(D/32) schema slots. 512–1024 covers most workloads; 4096+ only for deep schemas
map_size_mbPOST /db, db.toml; --map-size-mb for default on first boot onlyLMDB address-space ceiling for one database's environmentMDB_MAP_FULL on write, at the ceiling. A generous value costs address space, not disk — the map is sparseSet well above the projected dataset. Grow only — never below what is already stored. Raising it is an edit and a restart

Read shape

KnobSet atControlsWrong value looks likeChoosing
betadb.toml [eam], POST /dbSoftmax inverse temperature for reads. Low β averages many locations; high β concentrates on the winnerToo low: reads blend several stored patterns into a plausible vector matching none. Too high: a read is an exact nearest-neighbour lookup and the memory stops generalisingMust scale with pool size. At D=1024 the default β=5 holds ≥90% recall only to N≈16; β=30 reaches the exact-NN ceiling. Use attention/calibrate to pick one per collection, or attention/mdl per query
cleanup_betarequest cleanup: {"beta": b}Temperature for denoising a recovered filler against the codebook, in multi-role scoringBelow 30.0 it is clamped, silently, and the value actually used comes back in the response's cleanup_beta. A soft β yields a plausible-looking but wrong orderingβ=10 recovers 60% of the available signal, β=30 recovers 81%, flat from 100 up. Send "mdl" (the default) unless you have measured otherwise
kdb.toml [eam], POST /dbNearest locations activated per read and per writeToo high: more locations enter the mix, reads blend. Too low: the read misses the right location and reconstructs from neighboursDefault 20. Lower it as a capacity lever before touching beta. k ≥ the location count is a full softmax
t_maxdb.toml [eam]Maximum Hopfield iterations per readToo low: analyze reports converged: false and the result is mid-descent. Too high: latency, with no accuracy change past convergenceDefault 10. Check analyze's iterations and converged on representative queries; raise only if converged is false
epsilondb.toml [eam]Convergence threshold for the Hopfield loopToo large: the loop stops early on a partly-settled vector. Too small: every read runs to t_maxDefault 1e-6. Leave it; reach for t_max instead
strategyrequest field on read, analyze, batch_analyze"iterative" (default) settles the Hopfield loop; "fast" takes a single step"fast" on a heavily corrupted query returns a partial reconstruction. Any other string is a 400"iterative" for reconstruction, "fast" for a similarity probe where the exact vector does not matter
scalerequest field on attentionInverse temperature for the raw dot-product attention read. Analogous to β, but on Q·Kᵢ rather than a cosineToo low: uniform blend. Too high: single-key lookupMeasurement-driven. Depends on the vectors' norms, which the engine does not normalise on this path. Use attention/mdl to have the engine pick, or attention/calibrate to fix one
exclude_idrequest field on attentionDrops one stored location from the activated set (leave-one-out)An id past the location count is a 400Only for leave-one-out evaluation; leave unset in production
routing_sharpnessrequest field on compose/readInverse temperature for the softmax over per-collection match scoresToo low: the read averages across every shard and the answer is mush. Too high: the read commits to one shard and a query that straddles two loses half its evidenceDefault 20.0. Raise when shards are well separated, lower when they overlap by design

Write shape

KnobSet atControlsWrong value looks likeChoosing
eta_0db.toml [eam]Initial learning rate — how far a location's address migrates toward each writeToo high: addresses chase the most recent write and earlier patterns drift out. Too low: the codebook barely adapts and everything looks novelDefault 0.01. Measurement-driven; raise only for a stream whose distribution genuinely moves
eta_mindb.toml [eam]Floor the decayed learning rate cannot fall below0 is rejected. Too high defeats the decayDefault 0.001. Must satisfy 0 < eta_min ≤ eta_0
lambdadb.toml [eam]Learning-rate decay per write. eta ← max(eta·λ, eta_min)Too low: the rate hits eta_min within a few hundred writes and the memory freezes. 1.0 disables decayDefault 0.9999 — the rate halves at roughly 7000 writes. Must be in (0, 1]
tau_dampdb.toml [eam]Damping time constant applied to the learning rateInteracts with lambda; the observable is the same as a mis-set lambdaDefault 10.0, > 0. Measurement-driven — change lambda first
gammadb.toml [eam]Conscience factor in winner selection: biases against locations that keep winning0 disables the conscience and a hot location absorbs everything. Too high spreads writes across locations that do not fit them, blurring the codebookDefault 1.0, ≥ 0. Raise on a skewed key distribution where stats.max_write_count far exceeds avg_write_count
tau_splitdb.toml [eam]Novelty threshold (cosine). A write below it against its winner spawns a new locationToo high: every write spawns a location and the collection becomes a list. Too low: dissimilar patterns merge into one blurred addressDefault 0.3. Must be < tau_merge or the config is rejected
tau_mergedb.toml [eam]Merge threshold (cosine). Two locations above it fold togetherToo low: distinct clusters merge and recall drops. Too high: near-duplicate locations accumulateDefault 0.95, ≤ 1
tau_overloaddb.toml [eam]Write count at which a saturating location splitsToo high: a location holds more patterns than D/32 and reads from it blend. Too low: constant splitting, an inflated location count and a large indexDefault max(d/32, 8), which tracks the measured per-read capacity wall. Override only when running a non-default beta — a sharper β supports a larger pool
l_0db.toml [eam], POST /db, --l0 for defaultInitial hard-location countA positive value pre-seeds random unit vectors, so early reads match noiseLeave at 0 (data-seeded). Positive only when a random initial codebook is explicitly wanted
competitiveDatabase creation, [eam]true learns shared patterns; false appends individual vectors without activation, merge, migration or normalisationWith false, adaptive write knobs have no effect; the location count equals the write countBoth modes support associative recall. Choose shared adaptive locations or separately retained vector locations. See Choose a memory mode
mdl_gatedb.toml [eam]Replaces tau_split and tau_overload with one rule: allocate a location iff accumulated surprise clears engram_bits = log2(L+1) + log2(d/32)With it on, a high-traffic but coherent location carries ~0 debt and does not split even past tau_overload — max_write_count climbs while num_locations stays flatfalse by default. Turn it on when you want the location count to self-calibrate to the data's structure rather than to two hand-set thresholds. The spawn bar self-anneals as L grows

Graph

KnobSet atControlsWrong value looks likeChoosing
neighbor_capdb.toml [eam], POST /dbNon-zero enables the navigable graph. Effective capacity is computed dynamically as max((k-1)·⌈ln L⌉, 2k)0 disables the graph and every read is an exhaustive scan — correct, but linear in L. Non-zero makes graph quality depend on write order, since pruning drops the oldest edgesDefault max(d/4, 20). Set 0 when L is small, or when write order is uncontrollable and exact recall matters more than latency
num_landmarksdb.toml [eam], POST /dbLandmark entry points for graph searchToo few: the search enters in the wrong region and misses the true winner. Too many: every read pays a wider entry scanDefault 32. Must be > 0 whenever neighbor_cap > 0

Algebra and compress

KnobSet atControlsWrong value looks likeChoosing
alpharequest field on algebra/scaleMultiplier applied to every location's counter—Operands have mass. Balance by the mass ratio before subtracting: 0.02 is a no-op, 0.5 subtracts cleanly, 2.0 overshoots into noise. Scale the subtrahend to the minuend's counter magnitude first, then algebra/sub
thresholdrequest field on algebra/intersectCosine floor for a cross-pair to count as sharedToo low: the intersection is the cross product and the location cap fires. Too high: an empty targetDefault 0.95. A non-positive value bypasses the fast path and the result is bounded by --max-algebra-locations
powerrequest field on algebra/permuteExponent k in ρ^k. Negative applies ρ⁻¹A power that does not undo the one used to encode leaves the vector permuted — no error, just no matchDefault 1. Use -k to invert. Identify the permutation with exactly one of seed or name; neither or both is a 400
max_cross_krequest field on algebra/add, sub, bindCaps how many of source_b's locations each of source_a's pairs withUnset, an n×m cross product from a small JSON body can allocate gigabytes, and --max-algebra-locations refuses it with a 400Set it whenever both sources are large. The right value is the number of b-locations that are plausibly relevant to one a-location
kapparequest field on compressBits charged to specify one location (the model term in L·kappa + W·log2 L)Too low: compression barely merges. Too high: it merges distinct clustersDefaults to the collection's d. Raise to compress harder
lambda (compress)request field on compressWeight on a merge's data-fit cost (the variance increase)Too low: aggressive merging, recall drops. Too high: no merges pay and the location count is unchangedDefault 1.0. Compare description_length_before/_after and locations_after across values

Documents

KnobSet atControlsWrong value looks likeChoosing
nrequest field on documents/queryNumber of results returnedWith role_pairs, a small n silently drops documents that would rank well under your weighting — ranking ignores the pairsDefault 10. Over-fetch whenever role_pairs or client-side weighting is in play, and re-rank in the consumer
unbind_rolerequest field on documents/queryReads query as the filler expected at this role instead of comparing the stored superposition wholeMutually exclusive with role_pairsUse when the question is "which document has X at this role", not "which document is like X"
cleanuprequest field on documents/query"mdl" (default), "off", or {"beta": b}"off" scores the raw recovered filler, crosstalk and all — a genuine accuracy-for-speed trade, honoured exactly. {"beta": b} below 30 is clamped"mdl" unless profiling says the cleanup transform is the bottleneck

Server limits

Process-wide, set by flag or environment variable, 0 disables. They bound the result of a request rather than its wire size: a 2 MB body can describe a computation that allocates gigabytes.

KnobDefaultControlsWrong value looks likeChoosing
--request-timeout30 sPer-request deadline408 Request Timeout on a large algebra op or a wide batch_analyzeRaise for algebra-heavy workloads. The compose file already defaults to 600 for that reason
--max-body-size2 MiBMaximum request body413 Payload Too Large. A 1024-dim bulk_load of 100 items is already ~2 MB of JSON; /vec/bundle hits it at 128 terms of 1024 dimensionsRaise for wide-dimension bulk work, or split the batch
--max-bulk-items100 000Items per bulk_load400 naming the cap and the flagBound by memory: one location is 2·d f64s in flight
--max-batch-queries1 000Queries per batch_analyze400 naming the capBound by --request-timeout × per-query latency
--max-algebra-locations250 000Location count of an algebra result. Applies to add, sub, bind, and intersect with a non-positive threshold400 before the allocation, not an OOM after itPrefer setting max_cross_k on the request to raising this

Validation

Rejected at every config build or merge, so a bad db.toml fails at mount rather than at first read:

  • d > 0, k > 0, t_max > 0
  • eta_0 > 0, eta_min > 0, eta_min ≤ eta_0
  • lambda in (0, 1]
  • tau_split ≥ 0, tau_merge ≤ 1, and tau_split < tau_merge
  • gamma ≥ 0, tau_damp > 0, tau_overload > 0, beta > 0, epsilon > 0
  • num_landmarks > 0 whenever neighbor_cap > 0
  • every float finite