# fsearch keeps a trigram index for file contents and rebuilds it from FSEvents, on macOS only

> fsearch is a Rust file search tool for macOS that indexes every file name and, for text files within size limits, their contents. It runs as a CLI with a small daemon, a Rust crate or an MCP server. This article covers its query syntax, what grep skips, the Full Disk Access setup and the author's measurements.

**noahdunnagan/fsearch** — Whole-disk file search for macOS: fuzzy names, typo tolerance, indexed content grep. ~1 ms over 8M files.

- Repository: https://github.com/noahdunnagan/fsearch
- Stars: 2,222 · Forks: 141
- Language: Rust
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/noahdunnagan-fsearch

## It is a macOS tool, so searches for fsearch on Linux or Windows point elsewhere

The project is described as whole-disk file search for macOS, with fuzzy names, typo tolerance and indexed content grep. Everything under the hood is tied to that platform: the crawler calls getattrlistbulk, the change tracker follows FSEvents, and the release archive is named fsearch-aarch64-apple-darwin. There is no Linux or Windows build in the repository, and the only prebuilt binary is for Apple Silicon. Anyone who arrives here looking for a Linux or Windows file search tool, has the wrong project.

The tool comes in three forms: a command-line program with a small background daemon, a Rust crate you link into your own application, and an MCP server for coding agents. All three share one index. When an app and the CLI run together, the first process owns the index and the rest follow it, so you do not pay for two crawls of the disk.

The license is MIT, the crate version in Cargo.toml is 0.2.1 on Rust edition 2024, and the repository has two releases, v0.2.0 and v0.2.1, both published on 12 October 2026.

## Building from source installs to ~/.local/bin, and a release tarball skips Rust

The source install is one line, and the second command copies the binary into place:

```
cargo build --release && ./target/release/fsearch install   # -> ~/.local/bin/fsearch
fsearch fsearch main              # find files by name
fsearch 'ext:rs grep:apply_dir'   # search inside files
```

For Apple Silicon you can skip the Rust toolchain and download a release build:

```
curl -L https://github.com/noahdunnagan/fsearch/releases/latest/download/fsearch-aarch64-apple-darwin.tar.gz | tar xz && ./fsearch install
```

The release profile in Cargo.toml uses opt-level 3, fat link-time optimization, a single codegen unit and stripped symbols. A separate profiling profile keeps debug symbols for profilers and is built with cargo build --profile profiling. A pgo.sh script sits at the repository root and the README does not describe it.

Intel Macs have no prebuilt archive in the instructions, so they build from source. The install location, ~/.local/bin, may not be on your PATH, and the README does not tell you to add it.

## Query tokens combine fuzzy words, filters and git status in one string

A query is a single quoted string that mixes words and filters. Words are fuzzy and forgive a typo at any letter, two typos in words of eight or more letters, so mian.rs finds main.rs. Several words can spell out one name, and accented names match however the filesystem stored them. Operators include 'exact, ^prefix, suffix$ and !exclude.

The filters are ext:, type:, kind:, in:, size:, mtime:, re:, path:, grep:, regex:, sym:, limit: and git:. The git filter takes modified, staged, untracked, clean, tracked, ignored or conflict. Examples from the project:

```
fsearch 'readme in:~/Developer'          # inside a folder
fsearch 'type:image size:>5mb mtime:<7d'
fsearch 'ext:rs regex:fn\s+\w+_dir'      # regex inside files
fsearch 'sym:apply_dir'                  # where it's defined
fsearch 'src/**/*.{c,h} !test/ init'     # globs, in or out
fsearch 'git:modified in:~/Developer/app'
```

Content search is smart-case, and a regex matches within a single line, as in ripgrep. That means a pattern cannot span a line break. The README does not explain how sym: finds definitions, so check src/ before depending on it for languages other than the Rust and C examples it shows.

## Grep skips lockfiles, large JSON, minified bundles and linked worktrees

Content search covers text files up to 1 MB, and up to 10 MB inside git repositories, minus whatever git ignores. It leaves out lockfiles, JSON files of 100 KB and up, minified or bundled assets, linked git worktrees (their main checkout is indexed instead), and build-output folders outside repositories.

The practical consequence is that a grep for a string that lives in package-lock.json or a generated bundle finds nothing, with no error. If you need those, run ripgrep or grep directly on the file. The exception is named in the README: names are always indexed, and a grep inside a file you name still reads it, so fsearch with an explicit path can reach a skipped file.

To exclude more, list folders in ~/Library/Application Support/FSearch/content-skip.txt, one per line, where a tilde and # comments work. That is the only knob described for content scope. There is no setting to raise the 1 MB limit.

The author reports that grep can search 99.9% and 99.8% of the files ripgrep's own set covers, on the Linux and Chromium trees respectively, up from 96% and 92% in the previous version.

## Full Disk Access must be granted again after every rebuild when it runs as a login item

From a terminal that has Full Disk Access, fsearch indexes everything. Run as a login item with fsearch install --login, the binary itself needs access: grant ~/.local/bin/fsearch in System Settings, under Privacy & Security, and repeat that after each rebuild. A rebuilt binary is a new file to macOS, so the old grant no longer applies.

The failure mode is quiet. Without access, fsearch skips protected folders instead of prompting. Files in places such as Desktop or Documents can therefore be missing from results while everything else works. If a file you know exists never shows up, check the permission before suspecting the index.

On a first run, a search in a folder indexes that folder first, so a name lookup does not wait for a whole-disk crawl. After the first crawl with getattrlistbulk, the daemon follows FSEvents. Changes show up in about 15 ms, and a restart replays only what changed since it stopped. The daemon runs with malloc's space-efficient mode, so it returns memory to the system when idle.

## The index is memory-mapped names plus a trigram content index with per-file filters

Names live in one memory-mapped file, laid out folder by folder, so the in: filter is a range lookup. A bitmap of each name's letters rules out most names before the fuzzy scoring runs. Content search is a trigram index plus a per-file filter, so a query only reads files likely to match. Identical files are indexed once, and deleted files' entries are compacted away. Matches are read fresh from disk, and files edited since the last index sync are read directly, which is why an edited file is greppable almost at once.

The author's table compares this version with the previous one, b88f4e9, on an M4 Mac mini against the Linux and Chromium source trees, as medians. Finding the right file first with two typos rises from 11% and 15% to 74% and 93%. An edited file becomes greppable in 1.3 ms instead of 2.1 seconds. The index on disk shrinks from 503 and 893 MB to 309 and 600 MB, and peak memory while indexing falls from 1.13 and 1.37 GB to 0.77 and 0.94 GB.

There are costs. Finding a file by name goes from 0.045 and 0.10 ms to 0.054 and 0.12 ms, and the grep first page from 0.29 and 0.48 ms to 0.33 and 0.65 ms. Daemon memory on the Chromium tree drops from 100 to 35 MB, but on the Linux tree it rises from 19 to 21 MB. These are the author's numbers on one machine.

## Agents reach it through a JSON-lines socket or an MCP server with three tools

The daemon speaks JSON lines over ~/Library/Application Support/FSearch/fsearch.sock, or through fsearch stdio. A request is one object per line:

```json
{"q": "fsearch main", "limit": 20, "offset": 20}
{"op": "grep", "pattern": "apply_dir", "in": "~/Developer", "context": 2}
{"q": "main.rs", "boost": true, "near": "~/Developer/app/src/lib.rs", "git": true}
{"op": "open", "path": "~/Developer/app/src/main.rs"}
{"op": "recent", "in": "~/Developer/app"}
```

The open operation tells fsearch what you opened. With "boost": true, those files rank higher for a while, near favors files close to the one you are in, and recent lists what you worked on lately. "git": true adds each hit's git status.

For coding agents, fsearch mcp is an MCP server on stdio exposing find_files, grep and multi_grep. The README gives no client configuration for it and no limits on result size.

In Rust, the crate starts an engine with fsearch::Engine::start and runs fsearch::Query::parse followed by engine.search. The repository includes an examples/ directory and demo/abba.py, neither explained in the README.

## Conclusion

Use fsearch if you work on a Mac, search large source trees, and want fuzzy file names, indexed grep and an MCP server for a coding agent from a single binary. Skip it if you need Linux or Windows, since every mechanism in it is macOS specific. Before relying on content search, check the skip rules for your files (1 MB and 10 MB limits, lockfiles, large JSON) and grant Full Disk Access to the binary you actually run.

## FAQ

### What is FSearch?

This fsearch is a whole-disk file search tool for macOS written in Rust. It does fuzzy, typo-tolerant name search and indexed search inside files, as a CLI with a daemon, a crate or an MCP server.

### What is a good alternative to Everything search on Linux?

The repository does not discuss Linux or Everything. This project targets macOS only, using getattrlistbulk and FSEvents, so it is not a Linux option.

### How to install fsearch?

Run cargo build --release && ./target/release/fsearch install to build and copy it to ~/.local/bin/fsearch. On Apple Silicon you can instead download the fsearch-aarch64-apple-darwin release tarball and run ./fsearch install.

### Is there an fsearch alternative for Linux?

This repository offers no Linux build. Every mechanism it uses, from getattrlistbulk to FSEvents, is specific to macOS.

## Sources

- [Issues](https://github.com/noahdunnagan/fsearch/issues)
- [License: MIT](https://github.com/noahdunnagan/fsearch/blob/main/LICENSE)
- [noahdunnagan/fsearch on GitHub](https://github.com/noahdunnagan/fsearch)
- [README](https://github.com/noahdunnagan/fsearch/blob/main/README.md)
- [Releases](https://github.com/noahdunnagan/fsearch/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/noahdunnagan-fsearch
