1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# infino default configuration.
#
# This file is compiled into the binary via include_str! and acts as
# the floor of the config stack. Override at runtime via (in order of
# increasing precedence):
#
# 1. /etc/infino/config.yaml (system-wide)
# 2. $XDG_CONFIG_HOME/infino/config.yaml (user; falls back to
# $HOME/.config/infino/...)
# 3. ./infino.yaml (per-project / per-cwd)
#
# Each layer is a partial override — keys not set in a higher layer
# fall through to lower layers.
#
# Environment variables never override this configuration: engine
# behavior is set in YAML only, so a run's effective config is
# readable from files instead of reconstructed from process env.
# Supertable runtime knobs. Reader fan-out (skip + per-superfile
# search + top-k merge) and writer commit-time rayon-shard run on
# separate pools so a long-running commit can't spike reader p99 or
# vice versa.
#
# Thread-count values:
# - `auto` — resolves at runtime to num_cpus for reader_threads
# and max(1, num_cpus / 2) for writer_threads. Hardware-portable
# default; the same shipped config works on a 4-core dev laptop
# and a 96-core production node.
# - a positive integer — explicit override. Use when you've
# measured a workload-specific tuning.
supertable:
reader_threads: auto
writer_threads: auto
# Name of the system-managed primary-key column the supertable
# injects on every append(). The column type is fixed by the
# supertable layer; only the name is configurable. Leading
# underscore signals a system-owned field. Change this if
# `_id` collides with a business field name; otherwise leave
# as default.
id_column: _id
# Threshold above which the supertable's writer triggers an
# internal commit() to flush the in-memory buffer to disk-
# equivalent (one superfile per writer-pool thread). Specified
# in mebibytes (1 MiB = 1024 × 1024 bytes). Set 0 to disable
# auto-flush — only caller-driven commit() will produce
# superfiles.
commit_threshold_size_mb: 1024
# Verify the trailing whole-blob CRC and per-subsection CRCs
# on every SuperfileReader::open. Defaults to true. Set to
# false only when the underlying storage already validates
# checksums (content-addressed object store, ZFS, etc.) —
# skipping the scan trades that storage-layer guarantee for
# faster cold opens.
verify_crc_on_open: true
# Persistent storage wiring. Defaults to in-memory-only. Set
# `backend: s3` plus `bucket` / `prefix` to make
# SupertableOptions::apply_config attach an S3StorageProvider.
# `backend: azure` attaches an AzureStorageProvider, with `bucket`
# naming the Azure container. `backend: gcs` attaches a
# GcsStorageProvider, with `bucket` naming the GCS bucket and
# `storage_options` carrying `google_*` config keys.
storage:
backend: none
local_root:
bucket:
prefix: ""
# Object-store credentials and tuning, keyed by object_store's config
# strings. Infino reads no credentials from the environment — set them
# here (or via ConnectOptions). Empty → ambient cloud identity (IAM
# instance role / managed identity). Examples:
# backend: s3
# storage_options:
# aws_access_key_id: ...
# aws_secret_access_key: ...
# aws_region: us-east-1
# # aws_endpoint: http://localhost:9000 # MinIO / R2 / Ceph
# backend: azure # `bucket` names the container
# storage_options:
# azure_storage_account_name: ...
# azure_storage_account_key: ...
storage_options:
# When set, storage-backed reads use DiskCacheStore. For S3,
# lazy_foreground_with_background_fill is the object-store-native
# cold path: foreground opens/searches with exact range GETs (or zero
# superfile GETs at open when manifest open-batch bytes are present)
# while background fill promotes the full superfile to mmap.
disk_cache_root:
disk_budget_bytes: 10737418240
# Byte budget for the content-addressed manifest-part cache, held in a
# manifest-parts/ subdirectory of disk_cache_root. On a hit the part
# loader reads bytes from local disk instead of object storage; parts
# are content-addressed, so cached files are never stale and survive
# restarts. Independent of disk_budget_bytes. Default 2 GiB.
manifest_disk_budget_bytes: 2147483648
cold_fetch_mode: lazy_foreground_with_background_fill
cold_fetch_streams: 8
cold_fetch_chunk_bytes: 4194304
mmap_cold_threshold_secs: 300
mmap_sweep_interval_secs: 75
# Compaction merges the small superfiles produced by individual
# commits into one target-sized superfile, cutting query fan-out.
# Sizes are in mb (1 MiB = 1024 × 1024 bytes).
compaction:
# Size a compacted output aims for.
target_superfile_size_mb: 1024
# Minimum estimated live bytes to trigger a merge,
# as a percentage of `target_superfile_size_mb`.
# E.g. at 80% with a 1 GiB target, two 200 MiB superfiles (400 MiB) do not compact.
min_fill_percent: 80
# Maximum memory budget for materializing inputs during a single merge, in MiB.
max_memory_mb: 3072
# Vector-index build / search / drain tuning. Every value below
# defaults to the engine's built-in behavior — a fresh install never
# needs to touch this section. These are the levers that used to be
# one-off INFINO_* env vars; they are YAML-only now.
vector:
# Default rerank codec for cosine vector columns. Non-cosine metrics
# still use locally fitted sq8_residual (values are not bounded to
# [-1, 1]). Override per column at table-create time when needed.
# sq8_fixed_residual — fixed absolute grid (default; portable bytes)
# sq8_residual — per-cluster fitted quantizer
# fp32 — full-precision rerank
# rabitq_only — no rerank column
rerank_codec: sq8_fixed_residual
# Absolute cap on fine IVF centroids probed per vector search.
# Empty (~) derives the budget from nprobe × eligible superfiles at
# query time; set an integer to force exactly that many.
inner_budget: ~
# Per-cell fine-probe floor: the minimum fine IVF clusters probed inside
# each selected cell. The user-table routing default takes this; the
# hidden index's per-table stamp in the manifest overrides it. Pairs with
# fine_nprobe_pct as max(floor, floor(pct × cell fine-cluster count)).
fine_nprobe_floor: 4
# Proportional fine-probe fraction for UNFILTERED vector search: probe
# floor(pct × the cell's fine-cluster count), so depth tracks cell size.
# 0.0 turns the proportional depth off (fixed floor); > 0 opts in — the
# recall lever for large cells. Filtered queries ignore this.
fine_nprobe_pct: 0.0
# K-means training points per centroid for the drain's per-cell
# sub-builds.
kmeans_pts_per_centroid: 64
# Doc count above which a merged cell superfile is split into two
# sub-cells during hidden-index maintenance.
#
# Max docs in a global cell before it is split (hard ceiling). With the
# modality trigger on by default (cell_split_modality_d > 0) this cap is the
# upper backstop; set cell_split_modality_d = 0 to make the cap the sole
# trigger.
#
# 500K, deliberately large. A brief experiment lowered this to 40K (≈ the size
# 256 cells reach at 10M) on the theory that cells must be kept small — that was
# wrong. On a synthetic multi-modal stress corpus at nprobe=1, native no-split
# cells at 100M/256 (~390K docs/cell) hold ~0.98 recall@10 — stable, not
# decaying — so the split is NOT needed to keep recall from cratering at these
# sizes; forcing a split down to 40K instead over-fragments the grid under
# streaming (cell count runs away → boundary loss → recall craters). The large
# cap therefore makes the default path native-ish: it splits only genuinely
# huge cells (100M/256 = 390K < 500K → no split; ~1B/256 = 3.9M → splits).
# ~0.98 here is the nprobe=1 floor on this stress corpus, not a target — to lift
# it, raise fine_nprobe_pct (within-cell depth) or enable cell_split_modality_d
# (finer grid, ~0.99 measured). Lower this cap only if a measured within-cell
# ceiling shows up at some large docs/cell.
cell_split_doc_cap: 500000
# Ashman-D threshold for a modality-driven split. Spread measured on the axis
# between a tentative two-means partition's centroids (so it isn't diluted by
# the ~1023 dimensions the split doesn't separate). A k=2 split of a single mode
# scores ~3 (half-normal baseline); two separated modes score in the
# tens-to-hundreds. With this > 0, cell_split_doc_cap becomes a hard ceiling and
# cells track natural cluster boundaries: the recursion counts modes K, a cell
# with K > MODALITY_MODES_PER_CELL (=4) splits into ceil(K/4) children, and the
# executor self-tunes k up for route fidelity.
#
# 8.0 (ON) is the default. The modality split lifts nprobe=1 recall above the
# fixed 256-cell grid's ~0.98 floor where the grid is too coarse — most clearly
# at small scale (~0.998 vs ~0.94 at 100K, ~0.997 vs ~0.962 at 1M) and slightly
# ahead at scale (0.991 vs 0.983 at 10M). The finer grid (~14x the cells) adds
# per-cell drain generations, but compaction consolidates them
# (compaction_min_fill_percent = 0), so post-compact cold GET stays bounded.
# Set 0.0 to fall back to the fixed-grid path (cap trigger only).
cell_split_modality_d: 8.0
# How user-superfile clusters align to the global cell grid:
# local — per-superfile k-means (default).
# global — cluster c == cell c, so the drain routes cluster → cell
# without re-scoring.
user_centroids: local
# User superfiles the hidden-index drain materializes per batch
# before publishing that batch's cell superfiles (bounds drain RAM
# to O(batch)). -1 = unbounded (one merge, O(corpus) RAM);
# 0 = skip the drain entirely.
drain_batch_superfiles: 64
# Target storage amplification for boundary-only replication (shared
# by commit and drain). <= 1.0 disables replication (the default).
# Replication was measured a net loss at 10M: its extra boundary copies
# inflated cell size (159K -> 232K rows/cell), which crowded the RaBitQ
# shortlist and displaced true neighbors before rerank — dropping
# post-drain recall 0.997 -> 0.975 while adding ~50% storage and ~35%
# more GETs/query. Grid+fine union routing carries boundary coverage
# instead. Raise above 1.0 only with evidence it helps at your scale.
drain_replica_target_factor: 1.0
# Per-cell consolidation op the drain applies:
# kmeans — re-cluster each cell's rows (default).
# splice — keep each superfile's local clusters verbatim.
drain_consolidate: kmeans
# Read fan-out for the drain's superfile opens. `auto` resolves to
# one in-flight read per hardware thread, floored at the
# background-fill default and capped at 64; or set a positive
# integer.
drain_read_concurrency: auto
# CPU threads for maintenance-compaction compute (cell splits'
# k-means and child builds, probe-law recalibration) — the `optimize()` path; the
# ingest commit path does not ride this pool. `auto` = all hardware
# threads (an explicit optimize owns its machine). Set an integer to
# cap it when optimize runs concurrently with latency-critical
# foreground work.
maintenance_threads: auto
# Cell count for the USER table's grid, trained at the first commit —
# controls user-superfile cell packing and pre-drain query routing. Finer
# than the hidden grid on purpose: smaller cells make the single pre-drain
# probe more precise and cheaper. Stamped at create; changing it later
# affects new tables only.
user_cell_count: 256
# Cell count for the HIDDEN vector index grid, trained at the same first
# commit. The drain reads this grid verbatim; post-drain routing runs at
# this granularity. Equal to user_cell_count: one 256-cell grid drives
# packing, pre-drain routing, the drain, and post-drain routing (user_grid
# degenerates to None when the counts match).
hidden_cell_count: 256
# Hidden vector-index compaction (distinct from the top-level
# `compaction:` section, which governs the user table). A packed cell
# shard stays a merge candidate until it reaches compaction_target_mb,
# so incremental deltas are absorbed into it instead of being sealed
# off as separate fragments. compaction_max_memory_mb caps the input
# bytes packed into one merge pass and must stay >= the target, or the
# target is never reached.
#
# 0 disables the byte floor, so a cell consolidates on fragment count alone
# (the >= 2 merge floor): drain generations collapse and post-compact cold
# GET stays at the post-drain level. Safe because the cell split is now the
# in-place append-child split (validated), so a merge that pushes a cell over
# the cap (or into multimodality with the trigger on) splits it correctly
# rather than hitting the old broken post-merge split path. Contrast the user
# table, which keeps an 80% floor to amortize writes on its transient data.
compaction_target_mb: 2048
compaction_min_fill_percent: 0
compaction_max_memory_mb: 4096
# Diagnostic and hardware-capability toggles. Each gates
# instrumentation or forces a slower code path for A/B measurement;
# none of them change query results. Leave everything off unless you
# are profiling or benchmarking.
diagnostics:
# Accumulate per-phase timers during the vector drain build.
drain_build_timers: false
# Emit the FTS builder's finish-phase profile.
fts_profile: false
# Capture the object-store I/O timeline.
io_timeline: false
# Force the AVX2 vector-distance path even where AVX-512 is available.
disable_avx512: false
# Force the scalar vector-distance path even where AVX2 is available.
disable_avx2: false
# Skip the disk cache's lazy background fill so foreground-only read
# behavior can be measured.
disable_background_fill: false
# Per-connection memory budget: a ceiling on the anonymous heap the query
# and ingest paths allocate (result batches, the vector shortlist, ingest
# buffers), so one connection can't grow memory until the process is OOM-
# killed. The memory-mapped superfiles and the disk cache are bounded
# separately; this bounds heap only.
#
# Applies to connections built from this config file. Code that opens a
# connection programmatically sets the budget on ConnectOptions
# (with_connection_memory_budget_bytes) instead.
memory:
# Bytes. 0 (default) is measure-only: usage is tracked but never refused.
# A positive value enforces the ceiling (the engine reserves at 90% of it,
# leaving headroom for small untracked allocations).
connection_budget_bytes: 0