v0.21.0 uses the local model for every query that gives it text, and removes the fixed limits on files, matches, time, and lock waits. The default path, the output, and the exit codes change, and every index rebuilds: read Breaking changes before you upgrade.
Breaking changes
- Default path. With no path, Seek searches the current folder, not the whole Git repository. From a subfolder, give the repository root to search all of it.
- Headers. A file header is the path as typed plus the path below it, as in
rgandgit grep, not a path relative to the repository.file:matches this path. With two or more nodes, a[git]or[folder]tag follows. - New output lines. When the model runs, copies with the same content and match print once, with an
also: <path>, ...line. A last line counts the name-only files that could also match. - Exit codes. A failed node gives a warning, and the other nodes still answer. So Git missing from
PATH, ortype:repo,type:filematch, and a negatedcase:(sourcegraph/zoekt#1189), give exit code 0 or 1, not 2. - Index rebuild. Every index rebuilds, and the first search also builds the vector index. On a large repository, this takes tens of seconds or more and several GiB of memory. Vector indexes are larger: about 1.3 million rows on a Kubernetes checkout, against 204,694. Old indexes stay until the cache budget needs their space;
seek gc --allremoves every index that no search uses. - Slower one-word queries. A one-word query now runs the model, so it takes more time and memory (see Speed). Use
--lexical-onlyfor a fast exact lookup. - Every match. Seek keeps every match, so a very common word on a large repository costs more time and memory.
SEEK_GC_MAX_AGEis gone. UseSEEK_CACHE_BUDGET.
Search
- The model runs for every query that gives it text: one word, an identifier, a phrase, an
orof descriptions, or filter patterns (sym:FoogivesFoo). Before, only a description of two or more words used it. - Every node can add files by meaning, also dirty and untracked Git files. Before, only one clean Git tree, file, or folder could.
- A file that only the model adds must pass a new threshold over the score of the same query against an empty document. So query words with no topic do not admit files. Write a description of three to six words.
- The model scores every candidate, not at most 128 files. A long query keeps every token, in up to 127 windows of 128 tokens. Every byte of a file reaches a vector, also in generated files.
- Text ranking no longer changes with the split of files into shards, nodes, or path scopes.
- A regular expression that Go rejects, such as
a{1,501}, is a query error, not a crash. -Aand-Ctake any value of 0 or more, not at most 512.
Limits and cache
- Gone: the 10,000-match cap, the 60 s search limit, the folder caps (1,000,000 files or 10 GiB), the Git caps (10,000,000 files or 10 GiB), and the 10 s and 60 s lock-wait deadlines.
SEEK_CACHE_BUDGET(for example20G) sets one budget for the index cache: by default, 10 % of the cache volume, or less to keep 5 % free. A search that does not fit stops with exit code 2. After a build, Seek removes the least recently used indexes until the cache fits.seek gcalso removes the runtimes of other Seek versions whose folders are older than 14 days.- During lock waits, the first Ctrl-C skips only the waiting nodes, and a second one ends the command.
Git and indexing
- In a partial clone, Seek finds a missing file by name and indexes it when it arrives. This needs Git 2.45 or later, or a 2024 maintenance release (2.39.4, 2.40.2, 2.41.1, 2.42.2, 2.43.4, 2.44.1). With an older Git, Seek skips that node.
- Seek finds by name only a file that it cannot read, and a macOS cloud-only file, which it never downloads. Before, it dropped an unreadable file with no notice.
- Seek no longer reads
.sourcegraph/ignoreor thezoekt.*Git config.repo:matches only Seek's owngit-<hash>name. - The "folder changed during index build" error is gone: Seek reads a changed node again. Seek now also sees a rewrite of the same size with the old mtime.
Security
-
Each Git command turns off the repository settings that run a program: the fsmonitor hook, hooks,
core.sshCommand, signature checks, the external diff,ext::transports, and local or worktree filter drivers. Global and system filter drivers, such as Git LFS, stay on. If Seek cannot read the driver list of a repository, it skips that node. -
A search uses no network:
GIT_NO_LAZY_FETCH=1stops partial-clone fetches. -
Seek no longer writes repository config. v0.5.0 to v0.20.1 set
core.untrackedCache=trueand, with Git 2.36 or later,core.fsmonitor=true, when the config did not name them. To remove them, run in each repository:git config --unset core.fsmonitor git config --unset core.untrackedCache git fsmonitor--daemon stop # macOS -
govulncheck reports GO-2026-6809 (
crypto/x509, fixed in Go 1.27.3), and GO-2026-6860 and GO-2026-6866 (x/net/html, fixed in v0.62.0). govulncheck finds no call to a vulnerable function. This release uses Go 1.27.2 andx/netv0.61.0.
Speed
Each row compares the code before and after one change, on a shared 18-CPU Mac.
| Case | Before | After | Why |
|---|---|---|---|
| Remove 1 file from a 3,000-file folder | 7.9 s | 0.10 s | Zoekt writes tombstones in place of a full build |
| Change 3,000 of 6,000 folder files | 3.8 s | 1.2 s | Delta builds use all CPUs |
| Change 1 file in a folder of more than 64 full-build shards (test fixture) | 5.0 s | 0.16 s | The delta cap counts only the shards above the last full build, so the change stays a delta |
| Index size after removing 36,000 of 40,000 files | 396 MB | 38 MB | A full build runs when the dead content reaches the live content |
| Vector build after a 1-file commit (Kubernetes) | 19.0–21.3 s | 3.6–3.9 s | Seek embeds only new windows and reuses unchanged graph shards |
| Vector scoring of 5 descriptions (Kubernetes) | 301–438 ms | 29–74 ms | NumKong kernels score the vectors; warm descriptions are 12–20 % faster |
| Cold text search of 48 repositories of 40 files | 1.80 s | 1.59 s | All nodes build at the same time, with build slots from the read budget in place of 4 workers |
| Time to see a free build lock (median) | 1.32 s | 6.5 ms | Lock waits poll every 1–10 ms, not every 100 ms–2 s |
| One-word query, clean 50-file repository, warm (Core ML) | 82 ms, 44 MiB | 0.5 s, 640 MiB | The query now runs the model (0.95 s on the CPU provider); use --lexical-only to skip it
|
Fixes
- A damaged Zoekt shard no longer stops the process: Seek rebuilds the index once. Seek repairs damaged vector data once, not at each search.
- A commit in a new month no longer rebuilds the Git index in full.
- A commit in a nested repository no longer changes the state of its parent.
- A failed publish step moves the live files back, so the index keeps its content.
- A failed update of uncommitted files no longer rebuilds the committed index at each later search.
- An edit during a build shows in the next search.
- A FIFO, socket, device, or symlink that replaces a file after the walk no longer blocks a build.
- Two builds no longer remove each other's temporary files.
- A failed step, such as ctags, reports its own error, not "context canceled".
- A
.gitentry that points to the Git directory of another work tree fails its node, so a search never shows or changes that repository.
Dependencies
- USearch 2.26.2 to 2.26.5, with its int8 cosine metric. On Kubernetes, unreachable keys fell from 3.68 % to 0.0041 %.
- NumKong 7.8.4 is new, embedded for each target.
gobwas/globis no longer a dependency. Zoekt, ONNX Runtime 1.30.0, the model, and Go 1.27.2 do not change.
Known limits
- A one- or two-word description adds few files by meaning, and a long one finds few. Files that contain every word still print.
- If a command stops responding while it holds a lock, other searches of that node wait until you stop them.
- On macOS, a non-ASCII path operand in a Git repository can find nothing, because Git stores NFC names and Seek can pass NFD names.
- On a case-sensitive volume, the Git path operand
a.goalso returnsA.go. - During another command's build, a dirty file that changed again can show its old uncommitted text.
- Upstream defects:
- Equal vectors in one USearch graph shard can be unreachable (unum-cloud/USearch#815).
- A file whose ctags parse takes more than 1 minute has no symbols (sourcegraph/zoekt#1204).
- The vector build drops a ctags tag of 4,096 bytes or more with no error (sourcegraph/go-ctags#27).
- A shard that ends inside a number field can make a Zoekt read use more memory with no end (sourcegraph/zoekt#1205).
- On macOS,
rs/xidv1.6.0 adds about 20 ms to each process start (rs/xid#121).
Router plugin
- A pathless
rg,git grep, orfdcommand searches the current folder, as the original command does. -Aand-Cpass to Seek up to the int64 maximum, with noseek --helpcheck.fdroutes only a literal name with no/and no regular-expression operator.
Maintenance
- CI also builds and tests on macOS 27 arm64 (the
xcode-27preview image). make rerank-assets-upgradeis gone; usemake search-assets-upgrade.
Packages
Four archives for macOS 15 or newer and glibc Linux (built on Ubuntu 24.04; no musl), on amd64 and arm64, with SHA-256 checksums, a CycloneDX SBOM, and build-provenance and SBOM attestations. Each archive holds the binary (with its model and native libraries), the license, and the third-party notices. Seek needs Universal Ctags, and Git 2.36 or later for Git repositories.
Install
curl -sSfL \
https://raw.githubusercontent.com/dualeai/seek/v0.21.0/install.sh |
sh -s -- v0.21.0Full changelog: v0.20.1...v0.21.0