Exact .fts5.db file, owning vault root, and optional tokenizer.
Audit the physical FTS rows for one content kind without mutating them.
A declared file is complete only when it has a positive integer
n_chunks, the same number of physical rows, and one valid row at every
integer index in 0..n_chunks - 1. The audit also rejects invalid line
ranges or raw-content storage, chunk-only paths, a different kind on a
declared path, missing/invalid source revisions, quarantine markers for
the requested kind, and invalid kinds anywhere in the index. Those global
checks deliberately fail closed so a scoped markdown or PDF audit cannot
certify an index whose other rows have unknown provenance.
Content-source kind to audit.
Declared and physical file/chunk counts plus the number of unique mismatched paths.
Remove the index file + WAL/SHM/rollback-journal sidecars after validating every present leaf. Missing files are idempotent; directories, special objects, and non-ENOENT inspection/deletion failures refuse the operation.
true when at least one artifact was removed.
Close the underlying SQLite handle synchronously and begin best-effort asynchronous lifetime release. This preserves the historical synchronous API; callers that own process shutdown must await closeAndRelease to prove both lease markers are gone. A release rejection is observed here (never unhandled), retained, and retried by a later awaited close.
Close SQLite without reopening it and await exact family-then-namespace lifetime release. If a previous best-effort close failed, this call makes one new attempt through the same core handle; terminal integrity failures remain fail-closed while retryable failures can complete.
Only after the SQLite handle is closed and both markers are gone.
Verify a bounded receipt batch in one synchronous SQLite read snapshot. Empty input returns immediately. Oversized input is rejected before any allocation or SQLite work; malformed entries receive false verdicts while preserving positional association for the accepted batch.
Persisted source receipts to verify, in caller order.
One current/not-current verdict per accepted input receipt.
Diff the on-disk source_state against the live vault snapshot. Returns
categorized lists; caller is expected to feed added + updated paths
back into reindexFile() and pass deleted to dropFile().
v2.8.0: optional kind filter — when set, the diff only considers
source_state rows of that kind. Lets the markdown-sync and PDF-sync
paths run independently against the same DB without one's "missing
files" being mistakenly deleted by the other. Default undefined
means "all kinds" (used by older callers + diff queries that want
a global view).
Optionalkind: ChunkKindDrop a file's chunks, state row, and quarantine marker. Idempotent.
v3.7.18 R-8 — wrapped in db.transaction() for atomicity. Pre-3.7.18
the two DELETE statements ran independently; a crash / SIGKILL / DB
lock contention between them could leave source_state saying "this
file is indexed at mtime X" while chunks had no rows — causing the
next watcher event to skip re-indexing (state matches) but search to
miss the file (no chunks). Sibling of v3.7.10 audit #10 fix that
wrapped reindexFile / reindexPdfFile / source_state in a txn for
the same reason. Caught by round-20 external audit.
Hash the exact physical source declarations and FTS chunk payload for one content kind without materializing all rows in memory.
The manifest is intended for before/after integrity checks in strict
evidence runs. It includes source mtimes, monotonic revision tombstones,
timestamps, every stored searchable/metadata column of BOTH searchable
tables — chunks and the v7 sibling chunk_parts, whose rows decide what
a search finds — and durable quarantine markers, so an in-place mutation
that keeps aggregate counts unchanged still changes the digest.
Generation v3 added the chunk_parts rows; a digest taken before v7 is
not comparable with one taken after it.
Content-source kind to fingerprint.
Lowercase SHA-256 digest of the ordered physical rows.
Fetch a single raw chunk with the stable receipt-free public shape. This compatibility wrapper delegates to getChunkWithReceipt and strips the internal authority fields.
Exact vault-relative source path from a prior FTS hit.
Zero-based chunk index within that source.
Verbatim chunk content and line bounds, or null when unavailable.
Fetch a receipt-bound chunk by (rel_path, chunk_index). Backs the
obsidian://chunk/{chunkIndex}/{+notePath} resource so MCP clients can
deep-link into specific chunks returned by a prior search. Returns the
RAW chunk text (the unenriched original); the FTS5 content column
additionally carries a synthetic wikilink-targets meta-line for recall,
which would otherwise pollute resource responses (audit v0.10.4 P1). The
returned source mtime and monotonic revision are selected in the same SQL
snapshot as the bytes; callers that own a live Vault must compare
both before exposing persisted content.
Exact vault-relative source path from a prior FTS hit.
Zero-based chunk index within that source.
Receipt-bound raw content, or null when the row is absent, orphaned, invalid-kind, or quarantined.
Verify that one internal FTS receipt still names the source generation currently eligible for egress. The monotonic revision closes same-mtime replacement and delete/re-add ABA gaps that an mtime-only comparison cannot distinguish. This convenience wrapper uses the same atomic batch verifier as multi-hit callers.
Vault-relative source path carried by the hit.
Content-source kind carried by the hit.
Source mtime committed with the indexed bytes.
Monotonic revision committed with the indexed bytes.
True only for a finite, safe, non-quarantined current receipt.
Open the SQLite database, admit only a fresh or same-vault FTS schema,
bootstrap the FTS5 virtual table + helpers, then enable WAL and best-effort
tighten file perms to 0o600 on the db + sidecars. A populated foreign, malformed,
or newer-schema database is refused before Enquire issues persistent
PRAGMA, DDL, DML, or chmod operations. SQLite itself may still take locks
or perform recovery while the live handle reads ownership metadata. Before
dependency loading and again immediately before native open, the main,
WAL, SHM, and rollback-journal leaves must be wholly absent or every
present leaf must be a singly linked regular file; orphan sidecars refuse.
Idempotent — a second open() call is a no-op.
OptionalexpectedDiscovery: FtsIndexDiscovery
Optional readonly preflight result to bind this mutating open to. No argument preserves the low-level intentional-rebuild contract; a supplied stale result is refused before bootstrap.
Hide a source's retained rows from every public FTS read until the same source is successfully reindexed or dropped. The marker is durable so a failed refresh cannot become visible again after process restart.
Vault-relative source path whose indexed bytes are stale.
Content-source kind; defaults to Markdown for watcher/sync callers.
Nothing.
Re-chunk a single markdown file, replacing its existing chunks atomically.
v3.7.10 (external audit #10) — wrapped DELETE + N×INSERT + source_state
UPDATE in a single SQLite transaction. Pre-fix a crash/error between
statements could leave partially-updated chunks (some new, some stale)
with a stale source_state row pointing at the wrong chunk count. The
transaction guarantees all-or-nothing atomicity. better-sqlite3
db.transaction() wraps + auto-rolls back on throw.
v2.8.0 — re-chunk a single PDF, replacing its existing chunks atomically.
Caller pre-extracts page text via extractPdfText (src/pdf.ts) so this
method stays decoupled from pdfjs-dist (which is an optionalDependency).
Page boundaries are preserved as [page: N] markers in the joined text
before chunking — the chunker may split a page across chunks or merge
short pages, but the markers travel with the text so search snippets
carry page citations. Same chunkContent pipeline as markdown so chunk
IDs match across the BM25 / TF-IDF / embeddings rankers (RRF requires
stable IDs).
BM25-ranked search with the stable, receipt-free public result shape. This compatibility wrapper applies the same provenance and quarantine joins as searchWithReceipts, then strips internal receipt fields.
User query string. Whitespace-only returns [].
Optionalfolder?: stringVault-relative prefix filter.
Optionallimit?: numberMax results. Default 25.
OptionalsinceMtimeMs?: numberRecency filter in source mtime milliseconds.
Optionaltag?: stringExact-tag membership filter.
Receipt-free hits sorted by descending score.
BM25-ranked search over chunk content with persisted source receipts.
Folder + tag + recency filters
are pushed down to the SQL layer. Hyphenated identifiers (e.g.
"claude-telegram") are quote-escaped via safeFts5Query so
FTS5 doesn't interpret - as the NOT operator.
User query string. Whitespace-only returns [].
Optionalfolder?: stringVault-relative prefix filter.
Optionallimit?: numberMax results. Default 25.
OptionalsinceMtimeMs?: numberRecency filter — only return chunks from files modified at or after this mtime.
Optionaltag?: stringExact-tag membership filter (only matches the full
tag, not core-team for core).
Provenance-bound, non-quarantined hits sorted by descending
score. The ranked chunks pass runs first and is scored exactly as it
was before v7; only if it under-fills limit is a second pass over the
sibling chunk_parts table appended, with score 0 — those hits are
FOUND by the words an identifier is spelled from, never RANKED by them.
Each hit carries the source_state mtime and monotonic revision
committed with its indexed bytes; callers that own a live Vault
must compare both before exposing persisted text. Empty array if no
usable query tokens or no matches.
Total chunks across the index. Used by stats / banner / UI.
Total source files (notes + PDFs) tracked in source_state. Used by
the ready banner so users can verify the index actually built.
SQLite FTS5 inverted index over chunked note content. Opt-in via
--persistent-index. The production evidence linked in this module reports 50–100ms BM25 top-10 at 1,771 chunks / 368 files; other corpora and hardware must be measured independently. Falls back transparently to the in-memory parallel-scan path whenbetter-sqlite3isn't installed.Construct, then call
open(), then drive incremental sync via diff + reindexFile / reindexPdfFile / dropFile. Query with search; deep-link to individual chunks with getChunk.Example