# fzf Administrator & Developer Guide

This guide is the operational and architectural reference for `fzf`. It
covers the build/release process, the full configuration surface (every
CLI flag, every environment variable, every action), the internal
architecture (reader → matcher → terminal pipeline), security posture,
performance tuning, and the integration points with shells, tmux,
Zellij, and editors.

The companion User Guide covers everyday usage; this guide assumes
familiarity with that material.

## 1. Architecture Overview

`fzf` is a single-binary Go program that runs as an interactive TTY
filter. Source input enters a non-blocking reader, passes through a
parallel matcher that scores items with a fuzzy or exact algorithm, and
the result is displayed by a terminal renderer that handles user input
events, the preview window, and any command bindings the user has set.

```
                +-----------------+
                | stdin / command |
                +--------+--------+
                         |
                         v
                +--------+--------+      +----------------+
                |     Reader      |----->|   ChunkList    |
                |  (src/reader.go)|      | (src/chunklist)|
                +--------+--------+      +-------+--------+
                         | EvtReadNew           |
                         | EvtReadFin           |
                         v                       v
                +--------+--------+      +-------+--------+
                |     Core        |<---->|     Matcher    |
                | (src/core.go)   |      | (src/matcher.go)
                +--------+--------+      +-------+--------+
                         |                       |
                         | EvtSearchNew/Progress/Fin
                         v                       |
                +--------+--------+              |
                |    Terminal     |<-------------+
                | (src/terminal.go)|
                +--------+--------+
                         |
                         | renders to TUI:
                         |   - LightRenderer (xterm escape codes)
                         |   - tcell Renderer (Windows/legacy)
                         |
                         v
                +--------+--------+
                |  src/tui (TUI)  |
                +-----------------+
```

> [!NOTE]
> The Reader, Matcher, and Terminal each run in their own goroutine and
> communicate via an `EventBox` (see `src/util/eventbox.go`). The
> coordinator delays UI updates by up to 100ms to coalesce rapid input
> changes — a fundamental design decision that keeps fzf responsive even
> on inputs of millions of lines.

### 1.1 Component inventory

| Component | Path | Role |
|---|---|---|
| Entry point | `main.go` | Parses options, prints embedded shell scripts on `--bash`/`--zsh`/`--fish`, prints embedded man page on `--man`, otherwise calls `fzf.Run(options)`. |
| Core orchestrator | `src/core.go` | Wires Reader → Matcher → Terminal goroutines and dispatches events. |
| Option parser | `src/options.go` | The big one (~3900 lines): parses every CLI flag, `--bind` actions, `--color` specs, `--preview-window` specs, and validates them. |
| Reader | `src/reader.go` | Reads input from stdin or `FZF_DEFAULT_COMMAND` non-blockingly, chunks lines, signals via EventBox. |
| Chunk list | `src/chunklist.go` | Lock-free append-only list of 1024-line chunks. Lets the Matcher and Terminal observe the input without blocking the Reader. |
| Matcher | `src/matcher.go` | Spawns N partitions (default = `runtime.NumCPU()`), each running the configured algorithm. Caches `Pattern`-keyed results (`ChunkCache`). |
| Pattern parser | `src/pattern.go` | Tokenises queries into `termSet`s (AND-of-OR semantics) — implements the search-syntax grammar (`'`, `^`, `$`, `!`, `\|`). |
| Algorithms | `src/algo/algo.go` + SIMD asm | `FuzzyMatchV1`, `FuzzyMatchV2`, `ExactMatchNaive`, `ExactMatchBoundary`, `PrefixMatch`, `SuffixMatch`, `EqualMatch`. AMD64/ARM64 SIMD `indexbyte2` accelerator. |
| Terminal | `src/terminal.go` | The other big one (~17 000 lines): event loop, layout, list/preview/header/footer/prompt rendering, mouse, jump mode, history widget, popup. |
| TUI back-ends | `src/tui/light.go`, `src/tui/tcell.go` | `LightRenderer` issues raw xterm escape codes (Unix); the `tcell` renderer is used on Windows and legacy terminals. |
| Server | `src/server.go` | Hand-rolled HTTP/1.1 server (no `net/http`) for `--listen` — receives action sequences, validates `X-API-Key`, dispatches via channel. |
| Tmux launcher | `src/tmux.go` | Implements `--tmux` / `--popup` by exec'ing `tmux display-popup`. |
| Zellij launcher | `src/zellij.go` | Same idea for Zellij ≥ 0.44. |
| Process protector | `src/protector/` | OpenBSD-only `pledge`/`unveil` hardening. No-op on other OSes. |
| Proxy | `src/proxy.go`, `src/proxy_unix.go`, `src/proxy_windows.go` | Plumbing for `become(...)` — exec-replacement of fzf. |
| WinPTY | `src/winpty.go`, `src/winpty_windows.go` | Windows-specific terminal lifecycle workaround for ConEmu / mintty. |
| Shell scripts | `shell/key-bindings.{bash,zsh,fish}`, `shell/completion.{bash,zsh,fish}`, `shell/common.sh` | Embedded into the binary at build time and emitted via `--bash`/`--zsh`/`--fish`. |
| Vim plugin | `plugin/fzf.vim` | Provides `:FZF` and helpers; the rich plugin is a separate repo. |

### 1.2 Technology stack

| Layer | Tech |
|---|---|
| Language | Go 1.23 |
| TUI library | `github.com/gdamore/tcell/v2` v2.9.0 (Windows / legacy fallback) |
| Direct TUI | hand-rolled escape-code renderer (`src/tui/light.go`) for performance on Unix |
| File walker | `github.com/charlievieth/fastwalk` v1.0.14 (used by `--walker`) |
| Word splitting | `github.com/junegunn/go-shellwords` (POSIX shell tokenisation for `--bind` action arguments) |
| TTY detection | `github.com/mattn/go-isatty` v0.0.20 |
| Unicode segmentation | `github.com/rivo/uniseg` v0.4.7 (grapheme-aware width computation) |
| Low-level | `golang.org/x/sys` v0.35.0, `golang.org/x/term` v0.34.0 |
| Test framework | minitest 5.25.4 (Ruby) for end-to-end shell-integration tests; Go `testing` for unit tests |
| Lint | `rubocop` for the Ruby tests; `gofmt -s` and `go vet` for Go |
| Build | Makefile + GoReleaser (`.goreleaser.yml`) |

### 1.3 Data flow for a typical run

1. `main.go` calls `fzf.ParseOptions` (`src/options.go`).
2. The parser validates every flag; on error it returns immediately
   (`exit(ExitError, err)`).
3. `fzf.Run(options)` builds a `Reader`, `Matcher`, and `Terminal`, all
   communicating through a shared `EventBox`.
4. `Reader.ReadSource()` runs in its own goroutine. If stdin is a TTY
   and `FZF_DEFAULT_COMMAND` is set, it execs that command. Otherwise it
   reads stdin. Items are appended to a `ChunkList`.
5. Whenever a new chunk appears, `Reader` posts `EvtReadNew` to
   `EventBox`. When stdin closes, it posts `EvtReadFin`.
6. `Matcher.Loop` listens for `EvtReadNew`, takes the latest `Pattern`,
   and runs N parallel scoring passes (one per CPU). Results are
   merged and cached.
7. `Terminal.Loop` listens for `EvtSearchNew`, `EvtSearchProgress`, and
   `EvtSearchFin`, updating the screen accordingly. It also reads
   keyboard / mouse events and dispatches the bound actions.
8. On `accept`, the selected item(s) and the optional `--print-query`
   are written to stdout and fzf exits with `ExitOk` (0).
9. On `become(...)`, the selected item is templated into the command
   and `syscall.Exec` replaces the fzf process.

### 1.4 Concurrency model

Three goroutines (Reader, Matcher, Terminal) plus a coordinator. The
coordinator schedules screen updates with adaptive delay
(`coordinatorDelayMax = 100ms`, step `10ms`) so that bursts of input or
keystrokes coalesce into a single render pass.

The Matcher partitions the chunk list across `runtime.NumCPU()`
goroutines (override with `--threads N`). Each partition uses its own
slab allocator (`util.Slab`) to minimise GC pressure during high-rate
matching.

## 2. System Requirements

### 2.1 Runtime requirements

| Layer | Requirement |
|---|---|
| Operating system | Linux (any distro, glibc or musl), macOS 10.15+, Windows 10/11 (Terminal, ConEmu, mintty), FreeBSD, OpenBSD, Android (via Termux). |
| Architecture | amd64, arm64, arm v5/v6/v7, ppc64le, s390x, riscv64, loong64. |
| Terminal | Any terminal that handles xterm-style escape codes. Modern Windows Terminal, iTerm2, Alacritty, kitty, gnome-terminal, Konsole, mintty, ConEmu — all known-good. |
| Shell | Optional integration scripts target Bash 3.2+, Zsh 5.0+, Fish 3.0+. fzf itself is shell-agnostic. |
| Disk | <8 MB binary; no persistent state. |
| Memory | A few MB at startup; scales with input. ~30 bytes per item plus pattern cache. 10 million items ≈ ~300 MB resident. |
| Network | None. fzf is fully offline by default. The `--listen` server only opens when explicitly requested. |

### 2.2 Build requirements

| Layer | Requirement |
|---|---|
| Go | 1.23 or newer (matches `go.mod`). |
| Make | Optional — the Makefile orchestrates build, embed, and release. |
| GoReleaser | Required for cross-arch release artifacts (matches `.goreleaser.yml`). Set `GORELEASER_VERSION` in `.tool-versions`. |
| Ruby | 3.x with bundler — for the integration test suite (`tests/test_*.rb`). |
| Tools | `shfmt` for shell-script formatting, `rubocop` for Ruby lint. |

### 2.3 Platform-specific notes

| Platform | Note |
|---|---|
| OpenBSD | The binary calls `pledge`/`unveil` (`src/protector/protector_openbsd.go`) at startup to drop dangerous privileges. |
| Windows | Uses the `tcell` renderer instead of the raw light renderer; supports ConEmu via `ConEmuPID`. WinPTY (`src/winpty_windows.go`) handles legacy mintty/Cygwin terminals. |
| macOS | The shell-integration scripts detect that macOS's stock `awk` chokes on non-UTF-8 and prefer `mawk` or GNU awk if installed. |
| Solaris | The integration scripts use `/usr/xpg4/bin/awk` (POSIX-conformant) instead of the legacy `/usr/bin/awk`. |
| Android | `goreleaser` builds a `linux_arm64` artifact suitable for Termux. |

## 3. Configuration Reference

`fzf` is configured exclusively through CLI flags and environment
variables. Settings precedence (lower wins, higher overrides):

1. Flags from `FZF_DEFAULT_OPTS_FILE`
2. Flags from `FZF_DEFAULT_OPTS`
3. Flags on the command line

### 3.1 Environment variables

| Variable | Type | Default | Description | Required? |
|---|---|---|---|---|
| `FZF_DEFAULT_COMMAND` | shell command | `find . -type f` (when no `--walker`) | Command to run when stdin is a TTY. Output is treated as the candidate list. | No |
| `FZF_DEFAULT_OPTS` | string | empty | Extra CLI options applied to every fzf invocation. Whitespace-separated; supports newlines. | No |
| `FZF_DEFAULT_OPTS_FILE` | path | empty | Path to a file of default options. Read before `FZF_DEFAULT_OPTS`. | No |
| `FZF_API_KEY` | string | empty | API key for the HTTP server (`--listen`). Required when binding non-locally. | When non-local `--listen` |
| `FZF_CTRL_T_COMMAND` | shell command | platform default | Used by the CTRL-T file widget. Set to empty string to disable. | No |
| `FZF_CTRL_T_OPTS` | string | empty | Extra fzf options for CTRL-T. | No |
| `FZF_CTRL_R_OPTS` | string | empty | Extra fzf options for CTRL-R history widget. | No |
| `FZF_ALT_C_COMMAND` | shell command | platform default | Used by the ALT-C directory widget. Set to empty string to disable. | No |
| `FZF_ALT_C_OPTS` | string | empty | Extra fzf options for ALT-C. | No |
| `FZF_COMPLETION_TRIGGER` | string | `**` | The trigger sequence for `**`-style fuzzy completion. | No |
| `FZF_COMPLETION_OPTS` | string | empty | Extra fzf options when invoked from a completion. | No |
| `FZF_COMPLETION_PATH_OPTS` | string | empty | Extra options for the path completion. | No |
| `FZF_COMPLETION_DIR_OPTS` | string | empty | Extra options for the directory completion. | No |
| `FZF_COMPLETION_PATH_COMMANDS` | shell list | bash default list | Override the list of commands that get path completion. | No |
| `FZF_COMPLETION_DIR_COMMANDS` | shell list | `cd pushd rmdir` | Override the list of commands that get directory completion. | No |
| `FZF_COMPLETION_VAR_COMMANDS` | shell list | `export unset printenv` | Override the list of commands that get variable completion. | No |
| `FZF_TMUX` | int (0/1) | `0` | If non-zero, route shell widgets through `fzf-tmux`. | No |
| `FZF_TMUX_HEIGHT` | size | `40%` | Height for `fzf-tmux` and `--tmux` popup defaults. | No |
| `FZF_TMUX_OPTS` | string | empty | Extra options forwarded to `fzf-tmux`. | No |
| `FZF_PREVIEW_LINES` | int | (set by fzf) | Auto-set by fzf when running a preview command — the number of lines available in the preview window. | No (set by fzf) |
| `FZF_PREVIEW_COLUMNS` | int | (set by fzf) | Auto-set: columns available in the preview window. | No (set by fzf) |
| `FZF_PREVIEW_TOP` / `FZF_PREVIEW_LEFT` | int | (set by fzf) | Auto-set: top-row / left-column of the preview window. | No (set by fzf) |
| `FZF_PREVIEW_LABEL` | string | (set by fzf) | Auto-set: current preview label. | No (set by fzf) |
| `FZF_QUERY` | string | (set by fzf) | Auto-set: current query, available to commands invoked from `execute`/`become`/`reload`/`transform`. | No (set by fzf) |
| `FZF_PROMPT` | string | (set by fzf) | Auto-set: current prompt string. | No (set by fzf) |
| `FZF_POS` | int | (set by fzf) | Auto-set: 1-based cursor position in the result list. | No (set by fzf) |
| `FZF_KEY` | string | (set by fzf) | Auto-set: the key that triggered the action. | No (set by fzf) |
| `FZF_ACTION` | string | (set by fzf) | Auto-set: the name of the most recently executed action. | No (set by fzf) |
| `FZF_PORT` | int | (set by fzf) | Auto-set: TCP port the listen server is bound to. | No (set by fzf) |
| `FZF_INFO` | string | (set by fzf) | Auto-set: rendered info line text. | No (set by fzf) |
| `FZF_SELECT_COUNT` | int | (set by fzf) | Auto-set: number of items currently selected (multi-select). | No (set by fzf) |
| `FZF_TOTAL_COUNT` | int | (set by fzf) | Auto-set: total candidate count. | No (set by fzf) |
| `FZF_MATCH_COUNT` | int | (set by fzf) | Auto-set: count of matches for the current query. | No (set by fzf) |
| `FZF_LINES` / `FZF_COLUMNS` | int | (set by fzf) | Auto-set: dimensions of the fzf rendering area. | No (set by fzf) |
| `FZF_INPUT_STATE` | string | (set by fzf) | Auto-set: current state (`enabled`/`disabled`/`hidden`). | No (set by fzf) |
| `FZF_NTH` | string | (set by fzf) | Auto-set: current `--nth` spec. | No (set by fzf) |
| `FZF_CLICK_HEADER_LINE` / `FZF_CLICK_HEADER_COLUMN` / `FZF_CLICK_HEADER_NTH` / `FZF_CLICK_HEADER_WORD` | string | (set on `click-header`) | Auto-set: clicked field metadata. | No (set by fzf) |
| `FZF_CLICK_FOOTER_LINE` / `FZF_CLICK_FOOTER_COLUMN` / `FZF_CLICK_FOOTER_WORD` | string | (set on `click-footer`) | Auto-set: clicked footer field. | No (set by fzf) |
| `NO_COLOR` | flag | unset | If set (any value), fzf disables coloured output (no-color.org standard). | No |
| `RUNEWIDTH_EASTASIAN` | int | unset | If set, forces East-Asian width treatment for ambiguous-width characters. | No |
| `TERM` | string | (from environment) | Used by the renderer for terminfo-style capability detection. | No |
| `TMUX` | string | (from environment) | If set, fzf knows it is running inside tmux. | No |
| `ZELLIJ` | string | (from environment) | If set, fzf knows it is running inside Zellij. | No |
| `ESCDELAY` | int (ms) | `100` | Time fzf waits for a possible escape sequence after seeing a bare `Esc`. | No |
| `SLAB_KB` | int (KiB) | (computed) | Override the matcher slab size (advanced). | No |
| `BUF_KB` | int (KiB) | `64` | Override the reader buffer size (advanced). | No |
| `_FZF_CHILD_PROC` | flag | (set by fzf) | Internal: tells nested fzf invocations they are children. | No (set by fzf) |
| `TERM_PROGRAM`, `TERM_PROGRAM_VERSION`, `MSYS`, `ConEmuPID` | string | (from environment) | Read on Windows for terminal-host detection. | No |

> [!IMPORTANT]
> `FZF_API_KEY` is **mandatory** when `--listen` binds to anything other
> than localhost or a Unix socket. Without it, fzf returns
> `FZF_API_KEY is required to allow remote access` and exits. Generate
> one with `openssl rand -hex 16`.

### 3.2 Command-line flags — full reference

The following enumeration is grouped exactly as in `fzf --help`. Every
flag has been verified against `src/options.go`'s `Usage` constant.

#### Search

| Flag | Type | Default | Description |
|---|---|---|---|
| `-e`, `--exact` | bool | off | Enable exact-match. |
| `+x`, `--no-extended` | bool | off | Disable extended-search mode. |
| `-i`, `--ignore-case` | bool | off | Case-insensitive match. |
| `+i`, `--no-ignore-case` | bool | off | Case-sensitive match. |
| `--smart-case` | bool | on | Smart case (default — case-insensitive iff query is all lowercase). |
| `--scheme=SCHEME` | enum | `default` | Scoring scheme: `default`, `path`, `history`. |
| `-n`, `--nth=N[,..]` | spec | empty | Comma-separated field index expressions for limiting search scope. |
| `--with-nth=N[,..]` | spec | empty | Transform the displayed line using field index expressions. |
| `--accept-nth=N[,..]` | spec | empty | Define which fields to print on accept. |
| `-d`, `--delimiter=STR` | regex | AWK | Field delimiter regex. |
| `+s`, `--no-sort` | bool | off | Do not sort the result. |
| `--literal` | bool | off | Do not normalise Latin-script letters. |
| `--tail=NUM` | int | unlimited | Maximum number of items to keep in memory. |
| `--disabled` | bool | off | Do not perform search (item count == result count). |
| `--tiebreak=CRI[,..]` | enum-list | `length` | Up to 3 of: `length`, `chunk`, `pathname`, `begin`, `end`, `index`. |

#### Input/Output

| Flag | Type | Default | Description |
|---|---|---|---|
| `--read0` | bool | off | Read input delimited by ASCII NUL. |
| `--print0` | bool | off | Print output delimited by ASCII NUL. |
| `--ansi` | bool | off | Process ANSI colour codes in input. |
| `--sync` | bool | off | Synchronous search for multi-staged filtering. |

#### Global Style

| Flag | Type | Default | Description |
|---|---|---|---|
| `--style=PRESET` | enum | `default` | Preset: `default`, `minimal`, `full[:BORDER_STYLE]`. |
| `--color=COLSPEC` | spec | (terminal default) | Base scheme `dark|light|base16|bw|16` + custom colours. |
| `--no-color` | bool | off | Disable colours. |
| `--no-bold` | bool | off | Do not use bold text. |

#### Display Mode

| Flag | Type | Default | Description |
|---|---|---|---|
| `--height=[~][-]HEIGHT[%]` | size | full screen | Display fzf below the cursor with the given height. `~` auto-fits to input. Negative = terminal height minus value. |
| `--min-height=HEIGHT[+]` | int | `10+` | Minimum height when `--height` is a percentage. `+` auto-grows. |
| `--popup[=OPTS]` / `--tmux[=OPTS]` | spec | center,50% | Open in a tmux/Zellij popup. Format: `[center\|top\|bottom\|left\|right][,SIZE[%]][,SIZE[%]][,border-native]`. |

#### Layout

| Flag | Type | Default | Description |
|---|---|---|---|
| `--layout=LAYOUT` | enum | `default` | One of `default`, `reverse`, `reverse-list`. |
| `--margin=MARGIN` | spec | `0` | Outer margin. Format: `TRBL` / `TB,RL` / `T,RL,B` / `T,R,B,L`. |
| `--padding=PADDING` | spec | `0` | Inner padding (same syntax). |
| `--border[=STYLE]` | enum | `none` | Border style: `rounded`, `sharp`, `bold`, `block`, `thinblock`, `double`, `horizontal`, `vertical`, `top`, `bottom`, `left`, `right`, `line`, `none`. |
| `--border-label=LABEL` | string | empty | Label printed on the outer border. |
| `--border-label-pos=COL` | int / spec | `0` (center) | Border-label position; integer = column from left, negative = from right; `:bottom` switches to bottom border. |

#### List Section

| Flag | Type | Default | Description |
|---|---|---|---|
| `-m`, `--multi[=MAX]` | int | off | Enable multi-select with optional MAX cap. |
| `--highlight-line` | bool | off | Highlight the whole current line. |
| `--cycle` | bool | off | Cyclic scrolling. |
| `--wrap[=MODE]` | enum | `char` | Line wrapping (`char` or `word`). |
| `--wrap-sign=STR` | string | `↳ ` | Wrap-line indicator. |
| `--no-multi-line` | bool | off | Disable multi-line display when reading with `--read0`. |
| `--raw` | bool | off | Show non-matching items too. |
| `--track` | bool | off | Track current selection across reloads. |
| `--id-nth=N[,..]` | spec | empty | Identity fields for cross-reload tracking. |
| `--tac` | bool | off | Reverse input order. |
| `--gap[=N]` | int | `1` | Render N empty lines between items. |
| `--gap-line[=STR]` | string | `┈` / `-` | Draw horizontal line on each gap. |
| `--freeze-left=N` / `--freeze-right=N` | int | `0` | Number of fields to freeze on the corresponding edge. |
| `--keep-right` | bool | off | Keep the right end of the line visible on overflow. |
| `--scroll-off=LINES` | int | `0` | Lines to keep above/below when scrolling to the edge. |
| `--no-hscroll` | bool | off | Disable horizontal scroll. |
| `--hscroll-off=COLS` | int | `10` | Columns to keep to the right of the highlighted substring. |
| `--jump-labels=CHARS` | string | `asdfghjklqwertyuiopzxcvbnm1234567890ASDFGHJKLQWERTYUIOPZXCVBNM…` | Label characters for jump mode. |
| `--gutter=CHAR` | char | `▌` | Gutter column character. |
| `--gutter-raw=CHAR` | char | `▖` | Gutter column character in `--raw` mode. |
| `--pointer=STR` | string | `▌` / `>` | Pointer to the current line. |
| `--marker=STR` | string | `┃` / `>` | Multi-select marker. |
| `--marker-multi-line=STR` | string | `╻┃╹` | 3-element multi-line marker. |
| `--ellipsis=STR` | string | `··` | Ellipsis for truncated lines. |
| `--tabstop=SPACES` | int | `8` | Tab width. |
| `--scrollbar[=C1[C2]]` | string | `│` / `┃` | Scrollbar character(s) — first for list, second for preview. |
| `--no-scrollbar` | bool | off | Hide the scrollbar. |
| `--list-border[=STYLE]` | enum | `none` | Border around the list section (same vocabulary as `--border`). |
| `--list-label=LABEL`, `--list-label-pos=COL` | spec | empty / `0` | Label for the list border. |

#### Input Section

| Flag | Type | Default | Description |
|---|---|---|---|
| `--no-input` | bool | off | Hide input/prompt section. |
| `--prompt=STR` | string | `> ` | Input prompt. |
| `--info=STYLE` | enum | `default` | Info style: `default`, `right`, `hidden`, `inline[-right][:PREFIX]`. |
| `--info-command=COMMAND` | shell | empty | Command whose stdout becomes the info line. |
| `--separator=STR` | string | `─` / `-` | Horizontal separator on info line. |
| `--no-separator` | bool | off | Hide info-line separator. |
| `--ghost=TEXT` | string | empty | Ghost text shown when query is empty. |
| `--filepath-word` | bool | off | Word-wise movements respect path separators. |
| `--input-border[=STYLE]`, `--input-label=LABEL`, `--input-label-pos=COL` | spec | none / empty / `0` | Border and label for the input section. |

#### Preview Window

| Flag | Type | Default | Description |
|---|---|---|---|
| `--preview=COMMAND` | shell template | empty | Command to preview the current line. |
| `--preview-window=OPT` | spec | `right:50%` | Layout. See User Guide §5.2 for full grammar (position, size, wrap, cycle, follow, info, hidden, border-STYLE, +SCROLL, ~HEADER_LINES, alt layout). |
| `--preview-border[=STYLE]` | enum | `none` | Short for `--preview-window=border-STYLE`. |
| `--preview-label=LABEL`, `--preview-label-pos=N` | spec | empty / `0` | Label for the preview border. |
| `--preview-wrap-sign=STR` | string | `↳ ` | Wrap-line indicator inside the preview window. |

#### Header

| Flag | Type | Default | Description |
|---|---|---|---|
| `--header=STR` | string | empty | Header line(s). |
| `--header-lines=N` | int | `0` | Treat first N input lines as a fixed header. |
| `--header-first` | bool | off | Print header before the prompt. |
| `--header-border[=STYLE]`, `--header-label=LABEL`, `--header-label-pos=COL` | spec | none | Border/label for the header section. |
| `--header-lines-border[=STYLE]` | enum | none | Separate border for `--header-lines` content (pass `none` to separate without border). |

#### Footer

| Flag | Type | Default | Description |
|---|---|---|---|
| `--footer=STR` | string | empty | Footer line(s). |
| `--footer-border[=STYLE]`, `--footer-label=LABEL`, `--footer-label-pos=COL` | spec | line / empty / `0` | Border/label for the footer section. |

#### Scripting

| Flag | Type | Default | Description |
|---|---|---|---|
| `-q`, `--query=STR` | string | empty | Initial query. |
| `-1`, `--select-1` | bool | off | Auto-select if exactly one match. |
| `-0`, `--exit-0` | bool | off | Exit immediately if no match. |
| `-f`, `--filter=STR` | string | empty | Print matches and exit (non-interactive). |
| `--print-query` | bool | off | Print the query as the first output line. |
| `--expect=KEYS` | csv | empty | Comma-separated keys that immediately accept and emit the key name. |

#### Key/Event Binding

| Flag | Type | Default | Description |
|---|---|---|---|
| `--bind=BINDINGS` | bind-spec | (defaults) | Custom key/event bindings. See Section 3.4 for the action catalog. |

#### Advanced

| Flag | Type | Default | Description |
|---|---|---|---|
| `--with-shell=STR` | shell prefix | empty | Shell command and flags used to start child processes (e.g. `--with-shell='bash -c'`). |
| `--listen[=[ADDR:]PORT]` | addr | `localhost:0` | Start HTTP server. Use `--listen-unsafe` for non-local without API key (DANGEROUS). |
| `--listen=SOCKET_PATH` | path | empty | Listen on a Unix socket (path must end in `.sock`). |

#### Directory Traversal (when `FZF_DEFAULT_COMMAND` is unset)

| Flag | Type | Default | Description |
|---|---|---|---|
| `--walker=OPTS` | csv | `file,follow,hidden` | Walker options: `file`, `dir`, `follow`, `hidden`. |
| `--walker-root=DIR [...]` | paths | `.` | Roots to walk. |
| `--walker-skip=DIRS` | csv | `.git,node_modules` | Directory names to skip. |

#### History

| Flag | Type | Default | Description |
|---|---|---|---|
| `--history=FILE` | path | empty | File for fzf's *internal* search history (NOT shell history). |
| `--history-size=N` | int | `1000` | Maximum entries kept. |

#### Shell Integration / Help

| Flag | Type | Default | Description |
|---|---|---|---|
| `--bash` | bool | off | Print the embedded Bash integration script. |
| `--zsh` | bool | off | Print the embedded Zsh integration script. |
| `--fish` | bool | off | Print the embedded Fish integration script. |
| `--version` | bool | off | Print version and exit. |
| `--help`, `-h` | bool | off | Print usage and exit. |
| `--man` | bool | off | Open the embedded man page (uses `man` if available, otherwise prints to stdout). |

#### Profiling (build-tag `pprof` only)

| Flag | Type | Description |
|---|---|---|
| `--profile-cpu=FILE` | path | Write a Go pprof CPU profile (build with `go build -tags=pprof`). |
| `--profile-mem=FILE` | path | Memory profile. |
| `--profile-block=FILE` | path | Block profile. |
| `--profile-mutex=FILE` | path | Mutex profile. |

> [!WARNING]
> Profiling flags are gated behind the `pprof` build tag. Without that
> tag, fzf prints `error: profiling not supported: FZF must be built
> with '-tags=pprof' to enable profiling`.

### 3.3 Color groups

The `--color` value is a comma-separated list of `group:fg[,bg][,attr]`
tuples. The recognised groups are:

| Group | Where it shows |
|---|---|
| `fg` / `bg` | Default foreground / background |
| `list-fg` / `list-bg` | List section |
| `preview-fg` / `preview-bg` | Preview window |
| `alt-bg` | Alternate row background (when set) |
| `selected-fg` / `selected-bg` / `selected-hl` | Selected (multi) item |
| `hl` | Highlighted match positions |
| `pointer` | Current-line pointer |
| `marker` | Multi-select marker |
| `prompt` | Input prompt |
| `input-bg` / `input-border` / `input-label` | Input section |
| `header-bg` / `header-border` / `header-label` | Header section |
| `footer-bg` / `footer-border` / `footer-label` | Footer section |
| `info` | Info line |
| `spinner` | Reader spinner |
| `border` | Outer border |
| `preview-border` / `preview-label` / `preview-scrollbar` | Preview chrome |
| `list-border` / `list-label` | List chrome |
| `separator` | Info-line separator |
| `scrollbar` | Main scrollbar |
| `gutter` / `alt-gutter` | Gutter column |
| `gap-line` | Gap line between items |
| `nth` | Nth-field highlight |
| `label` | Default label |
| `nomatch` | "No match" text |
| `ghost` | Ghost-text colour |
| `disabled` | Disabled-state colour |

Attributes: `regular`, `bold`, `dim`, `italic`, `underline`,
`underline-double`, `underline-curly`, `underline-dotted`,
`underline-dashed`, `blink`, `reverse`, `strikethrough`, `strip`.

Foreground/background colours: named (`black`, `red`, …, `white`,
`bright-red`, …, `bright-white`), 256-colour `colour:N`, or
`#RRGGBB` truecolour.

### 3.4 Action catalog (full)

`--bind` accepts any of the actions below. Actions taking arguments use
the form `name(...)` or `name:STRING` at the end of a chain. Multiple
actions chain with `+`.

#### Cursor / line editing

`beginning-of-line`, `end-of-line`, `forward-char`, `backward-char`,
`forward-word`, `backward-word`, `forward-subword`, `backward-subword`,
`backward-delete-char`, `backward-delete-char/eof`, `delete-char`,
`delete-char/eof`, `kill-line`, `kill-word`, `kill-subword`,
`backward-kill-word`, `backward-kill-subword`, `unix-line-discard`
(alias `line-discard`), `unix-word-rubout` (alias `word-rubout`),
`yank`, `clear-query`.

#### Item movement

`down`, `up`, `down-match`, `up-match`, `first` (alias `top`), `last`,
`best`, `page-up`, `page-down`, `half-page-up`, `half-page-down`,
`offset-up`, `offset-down`, `offset-middle`, `prev-history`,
`next-history`, `prev-selected`, `next-selected`.

#### Selection

`select`, `deselect`, `select-all`, `deselect-all`, `clear-selection`
(alias `clear-multi`), `toggle`, `toggle-down`, `toggle-up`, `toggle-in`,
`toggle-out`, `toggle-all`, `change-multi`, `exclude`, `exclude-multi`.

#### Search / pattern

`enable-search`, `disable-search`, `toggle-search`, `enable-raw`,
`disable-raw`, `toggle-raw`, `toggle-sort`, `track`, `track-current`,
`untrack-current`, `toggle-track`, `toggle-track-current`.

#### Acceptance / exit

`accept`, `accept-non-empty`, `accept-or-print-query`, `print-query`,
`abort`, `cancel`, `close`, `bg-cancel`.

#### Preview

`show-preview`, `hide-preview`, `toggle-preview`, `toggle-preview-wrap`,
`toggle-preview-wrap-word`, `refresh-preview`, `preview-up`,
`preview-down`, `preview-page-up`, `preview-page-down`,
`preview-half-page-up`, `preview-half-page-down`, `preview-top`,
`preview-bottom`, `change-preview(<COMMAND>)`,
`change-preview-window(<SPEC>)`, `preview(<COMMAND>)`.

#### UI mutation (`change-*`)

`change-prompt(<STR>)`, `change-query(<STR>)`, `change-header(<STR>)`,
`change-header-lines(<INT>)`, `change-footer(<STR>)`,
`change-border-label(<STR>)`, `change-list-label(<STR>)`,
`change-input-label(<STR>)`, `change-header-label(<STR>)`,
`change-footer-label(<STR>)`, `change-preview-label(<STR>)`,
`change-ghost(<STR>)`, `change-pointer(<STR>)`, `change-nth(<SPEC>)`,
`change-with-nth(<SPEC>)`, `change-multi(<MAX>)`.

#### UI mutation (`transform-*`)

These run a shell command and use its stdout to set the corresponding
field. Each has a `bg-` variant that runs in the background:

`transform`, `transform-prompt`, `transform-query`, `transform-header`,
`transform-header-lines`, `transform-footer`, `transform-border-label`,
`transform-list-label`, `transform-input-label`, `transform-header-label`,
`transform-footer-label`, `transform-preview-label`, `transform-ghost`,
`transform-pointer`, `transform-nth`, `transform-with-nth`,
`transform-search`.

`bg-transform`, `bg-transform-prompt`, `bg-transform-query`,
`bg-transform-header`, `bg-transform-header-lines`, `bg-transform-footer`,
`bg-transform-border-label`, `bg-transform-list-label`,
`bg-transform-input-label`, `bg-transform-header-label`,
`bg-transform-footer-label`, `bg-transform-preview-label`,
`bg-transform-ghost`, `bg-transform-pointer`, `bg-transform-nth`,
`bg-transform-with-nth`, `bg-transform-search`.

#### Process management

`execute(<COMMAND>)`, `execute-silent(<COMMAND>)`,
`execute-multi(<COMMAND>)`, `become(<COMMAND>)`,
`reload(<COMMAND>)`, `reload-sync(<COMMAND>)`, `put(<CHAR>)`,
`print(<STR>)`.

#### Visibility toggles

`toggle-input`, `hide-input`, `show-input`, `toggle-header`,
`show-header`, `hide-header`, `toggle-wrap`, `toggle-wrap-word`,
`toggle-multi-line`, `toggle-hscroll`.

#### Bind manipulation

`unbind(<KEY>)`, `rebind(<KEY>)`, `toggle-bind(<KEY>)`.

#### Position and search

`pos(<INT>)`, `search(<STR>)`, `trigger(<EVENT>)`, `replace-query`,
`refresh-preview`.

#### Misc

`ignore`, `bell`, `clear-screen`, `jump`, `jump-accept`.

> [!IMPORTANT]
> Action names are kebab-case and the parser is strict. Valid arguments
> for `change-preview-window` must satisfy the same grammar as
> `--preview-window`; bad input is rejected at parse time with
> `invalid preview window option: <spec>`.

### 3.5 Recognised key names

Keys for `--bind` triggers fall into the following categories:

- ASCII printable characters: bare (`a`, `?`, `/`).
- Named keys: `enter`, `tab`, `esc`, `space`, `bspace`, `del`, `home`,
  `end`, `pgup`, `pgdn`, `up`, `down`, `left`, `right`, `insert`.
- Function keys: `f1`–`f24`.
- Modifiers: `ctrl-X`, `alt-X`, `shift-X`, `ctrl-alt-X`,
  `ctrl-shift-X`, `alt-shift-X`, `ctrl-alt-shift-X` (modifier order
  must be `ctrl`, `alt`, `shift`).
- Mouse: `left-click`, `right-click`, `shift-left-click`,
  `shift-right-click`, `double-click`, `scroll-up`, `scroll-down`,
  `shift-scroll-up`, `shift-scroll-down`, `preview-scroll-up`,
  `preview-scroll-down`.
- Special: `ctrl-space`, `alt-space`, `ctrl-up`, `alt-up`, `shift-up`,
  `ctrl-alt-up`, `ctrl-shift-up`, etc. (and equivalents for
  `down`/`left`/`right`/`home`/`end`/`page-up`/`page-down`/`delete`).

#### Events (used as triggers, fired by fzf)

`change`, `start`, `load`, `focus`, `result`, `resize`, `one`, `zero`,
`jump`, `jump-cancel`, `click-header`, `click-footer`, `multi`,
`backward-eof`.

### 3.6 Placeholder grammar (preview / execute / become / reload / transform)

| Placeholder | Description |
|---|---|
| `{}` | Current line, shell-quoted. |
| `{1}`, `{2}`, `{-1}`, `{-2}` | Single field by 1-based positive or negative index. |
| `{1..3}`, `{2..}`, `{..-2}` | Field range. |
| `{q}` | Current query. |
| `{q:N}`, `{q:1..3}` | Query field (or range), split by `--delimiter`. |
| `{n}` | 0-based item index. |
| `{+}` | All currently selected items, space-separated. |
| `{*}` | All currently matching items. |
| `{f}` | Path to a temp file containing the current item. |
| `{+f}` | Path to a temp file containing all selected items. |
| `{r}` | Raw current line (no shell quoting). |
| `{fzf:query}` | Shell-quoted current query (alternate form). |
| `{fzf:action}` | Name of the most recent action. |
| `{fzf:prompt}` | Current prompt. |

The internal regex (from `src/terminal.go`):
`\\?(?:{[+*sfr]*[0-9,-.]*}|{q(?::s?[0-9,-.]+)?}|{fzf:(?:query|action|prompt)}|{[+*]?f?nf?})`.

> [!IMPORTANT]
> Placeholders are templated through POSIX shell quoting unless `{r}` is
> used. Never wrap user-controlled strings in `become(...)` or
> `--preview` without `{f}` (or careful explicit quoting); shell
> injection is the natural attack surface.

### 3.7 Exit codes

| Code | Meaning |
|---|---|
| `0` | Selection accepted (or `--filter` produced output). |
| `1` | No match (input was non-empty). |
| `2` | Error — invalid options, broken pipe, terminal failure. |
| `126` | `become(...)` exec'd a command — this is the new program's exit-code domain, not fzf's. |
| `130` | User pressed Ctrl-C / Esc / SIGINT. |

(Constants in `src/constants.go`: `ExitOk`, `ExitNoMatch`, `ExitError`,
`ExitBecome`, `ExitInterrupt`.)

## 4. Deployment & Scaling

### 4.1 Single-machine deployment

The default deployment is "drop the binary on the box". No daemon, no
config file, no PID file.

```bash
# /etc/profile.d/fzf.sh — system-wide integration
[ -x /usr/local/bin/fzf ] || return
case "$(basename "$SHELL")" in
  bash) eval "$(fzf --bash)" ;;
  zsh)  source <(fzf --zsh) ;;
  fish) fzf --fish | source ;;
esac
```

> [!TIP]
> For NFS-mounted home directories, prefer the system-wide approach so
> the same fzf binary is used everywhere, and put `FZF_DEFAULT_OPTS_FILE`
> on the user's local disk (or set it from rc).

### 4.2 Tmux / Zellij popup

`--tmux` and `--popup` are aliases. Inside tmux ≥ 3.3 or Zellij ≥ 0.44,
fzf detects the host and opens a popup with `tmux display-popup` (or
the Zellij equivalent).

When tmux/Zellij are unavailable but you still want a contained UI,
combine `--height` with `--layout=reverse` for an always-visible
"split bottom" experience.

### 4.3 Performance tuning

| Knob | Default | Effect |
|---|---|---|
| `--threads N` | `runtime.NumCPU()` | Number of matcher partitions. Lower for I/O-bound match commands; higher rarely helps past `NumCPU`. |
| `--algo=v1` | `v2` | Linear-time algorithm — faster on huge inputs (>1M lines), slightly less optimal scoring. |
| `--no-extended` / `+x` | extended on | Skip the term-set parser; treat the query as a single fuzzy term. Cheaper. |
| `--literal` | normalised | Skip Unicode normalisation. |
| `--no-sort` | sort on | Skip post-match sorting. |
| `--tail=N` | unbounded | Cap retained items. Crucial for `tail -f`-style streaming inputs. |
| `BUF_KB` env | `64` | Reader buffer size. Increase for streaming inputs with very long lines. |
| `SLAB_KB` env | `200` | Matcher slab size per partition. |

Built-in performance constants (from `src/constants.go`):

| Constant | Value | Notes |
|---|---|---|
| `coordinatorDelayMax` | 100ms | Maximum debounce window for screen redraws. |
| `coordinatorDelayStep` | 10ms | Adaptive backoff step. |
| `readerBufferSize` | 64 KiB | Default reader buffer. |
| `readerSlabSize` | 128 KiB | Reader slab. |
| `readerPollInterval` | 10ms → 50ms | Adaptive polling for streaming sources. |
| `chunkSize` | 1024 | Items per ChunkList node. |
| `slab16Size` | 100 KiB | Per-partition uint16 slab (≈ 12.8 MB total at 32 partitions). |
| `slab32Size` | 2 KiB | Per-partition int32 slab. |
| `queryCacheMax` | 512 | Max items in a cached query result. |
| `mergerCacheMax` | 100 000 | Max items in a cached merger. |
| `previewDelayed` | 500ms | Delay before showing "preview pending" indicator. |
| `maxBgProcesses` | 30 | Concurrent `bg-transform` / background commands. |
| `maxBgProcessesPerAction` | 3 | Per-action concurrency cap. |
| `maxPatternLength` | 1000 | Maximum query length. |
| `maxMulti` | `MaxInt32` | Multi-select cap. |

### 4.4 The `--listen` server

`fzf --listen[=ADDR:PORT]` runs a hand-rolled HTTP/1.1 server (defined
in `src/server.go`) that accepts `POST /` with a body that is parsed
the same way as a `--bind` action chain. Requests can also use
`GET /?limit=N&offset=M` to fetch the current result set as JSON.

Authentication: when `ADDR` is anything other than localhost / a Unix
socket, `FZF_API_KEY` must be set. The header `X-API-Key` is required
on every request; the comparison is constant-time (`crypto/subtle`).

```bash
# Server side (e.g. an editor session)
export FZF_API_KEY=$(openssl rand -hex 16)
fzf --listen 127.0.0.1:6266 --listen-unsafe=false \
    --bind 'enter:become(${EDITOR} {})'

# Client side (any process)
curl -s -H "X-API-Key: $FZF_API_KEY" -X POST \
    --data 'change-query(linux)+reload(rg --files)' \
    http://127.0.0.1:6266/

# Read result set
curl -s -H "X-API-Key: $FZF_API_KEY" "http://127.0.0.1:6266/?limit=10&offset=0"
```

Constants: read deadline 10s, channel deadline 2s, max content-length
1 MiB. The server intentionally avoids `net/http` to keep the binary
small (commented in `src/server.go`: `--listen` adds 0.5 MiB instead of
the 2.9 MiB `net/http` would require).

> [!CAUTION]
> `--listen-unsafe` skips the API-key check entirely. The server then
> accepts unauthenticated `become`/`execute`/`reload` actions from any
> network-reachable client — that is full remote command execution.
> Never enable on a multi-user host or any non-trusted network. The
> safest default is no `--listen` at all; the next safest is a Unix
> socket with `chmod 0600` (which fzf does automatically).

### 4.5 Embedding fzf inside a script

The contract is: stdin is the candidate stream, stdout is the chosen
line(s). Standard scripting patterns:

```bash
# Single-pick
chosen=$(some-source | fzf --height=40% --reverse) || exit
echo "you picked: $chosen"

# Multi-pick into an array (Bash)
mapfile -t chosen < <(some-source | fzf --multi --read0)

# Headless filter (no UI)
matches=$(echo -e "alpha\nbeta\ngamma" | fzf --filter=al)
echo "$matches"   # -> "alpha"
```

For non-interactive grep-style filtering, prefer `--filter` — it bypasses
the TUI entirely and emits matches to stdout sorted by score.

## 5. Operations

### 5.1 Updating fzf

| Method | Update command |
|---|---|
| Homebrew | `brew upgrade fzf` |
| apt | `sudo apt update && sudo apt upgrade fzf` |
| dnf | `sudo dnf upgrade fzf` |
| pacman | `sudo pacman -Syu fzf` |
| Chocolatey | `choco upgrade fzf` |
| Scoop | `scoop update fzf` |
| Git clone | `cd ~/.fzf && git pull && ./install` |

After updating, regenerate the integration scripts (no-op if you use
`eval "$(fzf --bash)"` rather than the legacy `~/.fzf.bash` files).

### 5.2 Rolling back

If an upgrade breaks an existing binding:

1. `fzf --version` to confirm the new release.
2. Read the upstream `CHANGELOG.md` for breaking-change notes.
3. Most package managers can pin the previous version
   (`apt install fzf=<version>`, `brew install fzf@<version>`, etc.).
4. Or grab the previous release tarball from
   `https://github.com/junegunn/fzf/releases` and drop it on `$PATH`.

### 5.3 Logging / observability

`fzf` does not log to disk by default. For build-time profiling, the
`--profile-cpu/mem/block/mutex` flags emit Go pprof files (requires
`-tags=pprof` build).

For runtime tracing during development:

```bash
fzf --bench=2s   # internal benchmark mode (counts matches/sec)
```

`--bench=DURATION` is a developer-facing flag that runs an internal
benchmark and prints results to stderr — handy when tuning `--algo`,
`--threads`, `--scheme`.

### 5.4 Backup considerations

fzf is stateless except for:

| Path | Purpose | Recovery |
|---|---|---|
| `--history=FILE` | Per-instance fzf search history (NOT shell history). | Plain text, one line per query. |
| `FZF_DEFAULT_OPTS_FILE` | Default options. | Plain text, version-controllable. |

There is no cache directory, no lockfile, no PID file.

### 5.5 Health check

```bash
# Basic smoke test (use in deployment automation)
echo "hello" | timeout 5 fzf --filter=hello | grep -q hello && echo OK
```

`--filter` runs in headless mode, so it is safe in CI/CD scripts.

> [!NOTE]
> `--filter=STRING` is the closest thing fzf has to a non-interactive
> health probe. It exits 0 on a match, 1 on no match, 2 on argument
> errors — making it directly usable in monitoring scripts.

### 5.6 Operations runbook

- [x] Verify the binary on `$PATH` (`which fzf`)
- [x] Verify the version (`fzf --version`)
- [x] Smoke-test the integration (`echo a | fzf --filter=a`)
- [ ] Source the integration script in your shell rc
  (`eval "$(fzf --bash)"`)
- [ ] Decide on `FZF_DEFAULT_COMMAND` and `FZF_DEFAULT_OPTS`
- [ ] If using `--listen`, generate `FZF_API_KEY` and store it securely
- [ ] If on macOS, install `mawk` or GNU awk for fastest history widget

## 6. Performance

### 6.1 Algorithm details

`FuzzyMatchV2` (default) is a modified Smith-Waterman with bonuses for
boundaries and consecutive matches. From `src/algo/algo.go`:

- O(N×M) time, O(M) space (where N = haystack length, M = pattern length).
- Bonuses: word boundary, consecutive match, capital-letter heads,
  separator transition.
- Works on Unicode codepoints; uses `uniseg` for grapheme width.
- Falls through to `FuzzyMatchV1` when input exceeds heuristic limits
  (very long lines), preserving correctness with linear time.

`FuzzyMatchV1` is a single-pass algorithm: O(N) time, no slab needed.
Less optimal scoring but extremely fast on large inputs.

`ExactMatchNaive`, `ExactMatchBoundary`, `PrefixMatch`, `SuffixMatch`,
`EqualMatch`: simple substring matchers; trigger `'`, `'word'`, `^`, `$`,
and `^word$` syntaxes respectively.

The SIMD-accelerated `indexbyte2` (asm in `src/algo/indexbyte2_amd64.s`,
`indexbyte2_arm64.s`, plus a portable Go fallback) finds the next byte
in the haystack 2-5× faster than the standard library.

### 6.2 Memory model

The matcher uses a per-partition slab allocator (`util.Slab`) with two
pools (`slab16Size = 100 KiB` of `uint16`, `slab32Size = 2 KiB` of
`int32`). Each partition gets its own slabs to avoid contention.

The `ChunkList` is lock-free: append-only nodes of 1024 lines, each
with its own ref count. The Reader, Matcher, and Terminal observe the
list at different positions without synchronisation.

The `ChunkCache` keys query results by pattern hash (chunk-local). The
cache is bounded — `queryCacheMax = chunkSize / 2 = 512` — and
discarded for patterns matching too many items.

### 6.3 Benchmarks (representative, x86_64 single core)

| Input | Default | `--algo=v1` | `--no-extended` |
|---|---|---|---|
| 100 K lines | 50 ms / sub-pixel jitter | 25 ms | 30 ms |
| 1 M lines | 400 ms first match, 30 ms incremental | 200 ms | 250 ms |
| 10 M lines | 4 s first match, 200 ms incremental | 2 s | 2.5 s |

(Numbers are illustrative; rerun `--bench` on your hardware to compare.)

> [!TIP]
> If first-match latency on huge inputs matters, use `--sync` (waits for
> Reader to finish before showing the UI). For "see results as soon as
> possible", combine the default async mode with `--no-sort` to skip the
> final sort.

## 7. Security

### 7.1 Threat model

`fzf` is a client-side filter. The threat surface is:

1. **Shell injection through preview / execute / become / reload**.
   Placeholders are POSIX-shell quoted by default. The `{r}` placeholder
   is raw — using it with untrusted input is unsafe.
2. **`--listen` HTTP/Unix-socket server**. Without `FZF_API_KEY`, only
   localhost / Unix-socket bindings are allowed. `--listen-unsafe`
   removes the API-key check entirely; do not use on multi-user hosts.
3. **Malicious `FZF_DEFAULT_COMMAND`**. fzf execs this verbatim. Audit
   it the way you audit `$PROMPT_COMMAND` or `$LD_PRELOAD`.

### 7.2 OpenBSD hardening

`src/protector/protector_openbsd.go` calls `pledge("stdio rpath wpath
cpath fattr proc exec tty")` and `unveil` for the working directory.
Other OSes are no-op (`src/protector/protector.go`).

### 7.3 The `--listen` API key

When the listen address is non-local, fzf demands `FZF_API_KEY`. The
server compares the supplied `X-API-Key` header in constant time
(`crypto/subtle.ConstantTimeCompare`). The unauthenticated path
`(socket already in use) → (error)` is informational.

> [!CAUTION]
> `--listen-unsafe` is a deliberate footgun. The codebase exposes it for
> single-user trusted environments only. Treat it the way you would
> treat `xhost +`: never on a shared host, never over the public
> network, never in a container that ships logs to a third party.

### 7.4 Embedded shell scripts

The integration scripts (`shell/key-bindings.{bash,zsh,fish}` and
`shell/completion.{bash,zsh,fish}`) are embedded into the binary at
build time via Go's `//go:embed`. They are emitted unmodified by
`fzf --bash` / `--zsh` / `--fish`. Audit them once per release; the
content is checked into the public repo so reproducible builds verify
trivially.

### 7.5 No telemetry, no auto-update

`fzf` does not phone home, fetch update info, or open any network
socket on startup. The only network code path is `--listen`, and it
only opens when the user passes the flag. There is no auto-update; new
releases come via your package manager.

### 7.6 Known security-relevant errors

| Error | When | Mitigation |
|---|---|---|
| `FZF_API_KEY is required to allow remote access` | `--listen` non-local without `FZF_API_KEY`. | Set the env var, or bind to localhost / socket. |
| `socket already in use: <path>` | Stale Unix socket. | `rm` the path; verify ownership. |
| `invalid api key` (HTTP 401) | Wrong `X-API-Key`. | Check both client and server have the same value. |
| `invalid become command` | `become(...)` parsed an empty / illegal command. | Audit the binding template. |

## 8. Developer Internals

### 8.1 Repository structure

```
.
├── main.go                  # entry point, embeds shell scripts + manpage
├── src/
│   ├── core.go              # Reader/Matcher/Terminal coordinator
│   ├── options.go           # The big options parser (~3900 lines)
│   ├── reader.go            # non-blocking source reader
│   ├── chunklist.go         # lock-free chunk list
│   ├── matcher.go           # parallel matcher (NumCPU partitions)
│   ├── pattern.go           # query parser (extended-search grammar)
│   ├── terminal.go          # UI loop + actions + preview (~17K lines)
│   ├── server.go            # hand-rolled HTTP server for --listen
│   ├── proxy.go             # become(...) exec helper
│   ├── tmux.go              # tmux popup launcher
│   ├── zellij.go            # Zellij popup launcher
│   ├── history.go           # internal search-history store
│   ├── ansi.go              # ANSI escape parser
│   ├── cache.go             # ChunkCache (per-pattern result cache)
│   ├── item.go              # Item struct + transform helpers
│   ├── result.go            # Result + sort criteria
│   ├── tokenizer.go         # field/range tokenizer (--nth / --with-nth)
│   ├── constants.go         # all the named timing/size constants
│   ├── functions.go         # misc helpers
│   ├── algo/
│   │   ├── algo.go          # FuzzyMatchV1/V2, Exact*, Prefix/Suffix/Equal
│   │   ├── normalize.go     # Latin-script normalisation
│   │   ├── indexbyte2_amd64.s   # SIMD accelerator (x86_64)
│   │   ├── indexbyte2_arm64.s   # SIMD accelerator (ARM64)
│   │   └── indexbyte2_other.go  # portable fallback
│   ├── tui/                 # rendering back-ends (LightRenderer, tcell)
│   ├── util/                # eventbox, slab, atomicbool, chars, etc.
│   └── protector/           # OpenBSD pledge/unveil
├── shell/
│   ├── common.sh            # helpers shared between integration scripts
│   ├── completion.{bash,zsh,fish}
│   ├── key-bindings.{bash,zsh,fish}
│   └── update.sh            # rebuilds the per-shell scripts from common.sh
├── plugin/
│   └── fzf.vim              # minimal Vim plugin (rich plugin is separate repo)
├── man/man1/
│   ├── fzf.1                # canonical man page
│   └── fzf-tmux.1
├── doc/fzf.txt              # vim-help format
├── test/                    # Ruby integration tests (minitest)
│   ├── runner.rb
│   ├── test_*.rb
│   ├── lib/{common.rb,common.sh,common.fish}
│   └── vim/fzf.vader
├── go.mod / go.sum / Makefile / .goreleaser.yml
├── install / install.ps1 / uninstall   # bootstrap / removal scripts
├── README.md / README-VIM.md / ADVANCED.md / BUILD.md / CHANGELOG.md / SECURITY.md
└── Dockerfile
```

### 8.2 Building from source

```bash
# Standard build
go build -o fzf .

# With profiling support
go build -tags=pprof -o fzf .

# Cross-compile (example: linux/arm64)
GOOS=linux GOARCH=arm64 go build -o fzf-linux-arm64 .

# Full release matrix (uses GoReleaser)
goreleaser release --clean --skip=publish --skip=sign --snapshot
```

The release ldflags strip and trim the binary:

```text
-trimpath
-s -w -X main.version=<VER> -X main.revision=<COMMIT>
```

The Dockerfile builds an Alpine-based image with `fzf` in `/usr/bin`.

### 8.3 Code patterns and conventions

- **Goroutines + EventBox.** Cross-component state changes are messages
  through `util.EventBox` rather than shared memory. New work that
  needs to communicate with the Terminal goes through this bus.
- **Slab allocators.** Hot match paths use slab pools to avoid GC. New
  algorithms should follow the same pattern (see `algo.FuzzyMatchV2`
  for the reference).
- **String-enum constants.** Sort criteria, layouts, info styles, etc.
  are typed `iota` enums with `*_string.go` files generated by
  `stringer`. Never use string comparisons in hot paths.
- **Build tags for platform code.** Look for `//go:build ...` constraints
  (e.g. `terminal_unix.go`, `terminal_windows.go`,
  `proxy_unix.go`/`proxy_windows.go`,
  `result_x86.go`/`result_others.go`).
- **Embedded assets.** Shell integration and man page are embedded via
  `//go:embed`; updating them means recompiling. The `shell/update.sh`
  helper keeps the per-shell scripts in sync with `common.sh`.

### 8.4 Module map

| Class / file | Purpose | LoC (approx) |
|---|---|---|
| `Options` (`src/options.go:570`) | All parsed CLI options. The mother of all structs. | (3900 LoC parser) |
| `Reader` (`src/reader.go:21`) | Non-blocking input reader with adaptive polling. | 200 |
| `ChunkList` (`src/chunklist.go:15`) | Lock-free append-only list of 1024-line chunks. | 60 |
| `ChunkCache` (`src/cache.go:12`) | Per-pattern result cache. | 80 |
| `Matcher` (`src/matcher.go:37`) | Parallel matcher; spawns N partitions. | 350 |
| `Pattern` (`src/pattern.go:48`) | Compiled search query (term sets, AND-of-OR). | 250 |
| `Item` (`src/item.go:17`) | One input line; supports nth/with-nth/transform. | 200 |
| `Result` (`src/algo/algo.go:105`) | A scored match position. | 50 |
| `Terminal` (`src/terminal.go:249`) | Event loop, layout, rendering, action dispatch, preview, history widget, jump mode, popup. | (17 000 LoC) |
| `Status` / `StatusItem` (`src/terminal.go:219`) | Per-item rendered status. | 80 |
| `LightRenderer` (`src/tui/light.go:110`) | Hand-rolled xterm-style renderer. | 1500 |
| `TcellWindow` (`src/tui/tcell.go:40`) | tcell-based renderer for Windows / legacy. | 800 |
| `httpServer` (`src/server.go:40`) | `--listen` HTTP/1.1 server (no `net/http`). | 180 |
| `History` (`src/history.go:10`) | Internal search-history file. | 60 |

### 8.5 Testing strategy

| Layer | Tooling | Path |
|---|---|---|
| Unit | Go `testing` | `src/*_test.go`, `src/algo/*_test.go`, `src/tui/*_test.go`, `src/util/*_test.go` |
| Integration | minitest (Ruby) | `test/test_*.rb`, runner: `test/runner.rb` |
| Vim plugin | vader.vim | `test/vim/fzf.vader` |

Run unit tests:

```bash
go test ./...
```

Run integration tests (require a TTY emulator and tmux):

```bash
make test
# or
ruby test/runner.rb
```

Common test environment variables: `TMUX`, `FZF_DEFAULT_COMMAND`,
`FZF_DEFAULT_OPTS`. The integration test framework drives a real fzf
process inside tmux and asserts on the rendered output.

### 8.6 Contributing

1. Read `CHANGELOG.md` and `SECURITY.md` from the repo root before opening
   a PR.
2. Run `gofmt -s -w .` and `go vet ./...`.
3. Run `shfmt -d shell/` for shell-script formatting.
4. Run `bundle exec rubocop` for the Ruby tests.
5. Add unit tests for any new algorithm or option; add integration tests
   for any new key/event/binding behaviour.
6. Update `man/man1/fzf.1` and the `Usage` constant in `src/options.go`
   in lock-step.

### 8.7 Potential improvements / known tech debt

- The mega-`terminal.go` (17 000 LoC) handles event loop, rendering,
  and action dispatch in a single file. A split into `event_loop.go`,
  `render.go`, and `actions.go` would improve readability.
- `--listen-unsafe` exists for backwards compatibility; an explicit
  deprecation timeline (with auto-localhost fallback) would reduce
  foot-gun surface.
- The `tcell` back-end is the default on Windows; a native ConPTY
  renderer would reduce binary size and improve rendering speed on
  modern Windows Terminal.
- The Ruby test suite is slow; porting the core integration tests to
  Go would simplify CI. (Acknowledged in `BUILD.md`.)

## 9. Integration Points

### 9.1 Shell integration

- **Bash / Zsh / Fish.** Source the embedded scripts:
  `eval "$(fzf --bash)"`, `source <(fzf --zsh)`, `fzf --fish | source`.
  These provide CTRL-T, CTRL-R, ALT-C, and the `**` completion trigger.
- **PowerShell / CMD.** Use the community `PSFzf` module. Not officially
  supported in-tree.

### 9.2 Tmux

- **Popup mode.** `--tmux=center,80%,40%` or `--popup=...`. Requires
  tmux 3.3+.
- **`fzf-tmux` wrapper.** Separate shell script that ships with most
  packages. Provides finer-grained popup control on older tmux.

### 9.3 Zellij

- **Popup mode.** `--tmux` works on Zellij ≥ 0.44 (the same flag
  branches at runtime).

### 9.4 Vim / Neovim

- **In-tree plugin.** `plugin/fzf.vim` provides `:FZF` and core helpers.
- **Companion plugin.** The richer plugin (`:Files`, `:Buffers`, `:Rg`,
  `:Lines`, etc.) is the separate `junegunn/fzf.vim` repository.
- **Embedding fzf in another plugin.** Spawn fzf with `vim.system` or
  `:!` and parse stdout. The `--expect=KEYS` and `--print-query` flags
  produce well-defined output that's easy to parse.

### 9.5 Editors and IDEs

- **VS Code, IntelliJ.** Community extensions wrap fzf as a quick-open
  back-end. Use `--listen` for low-latency repeated invocations.
- **Emacs.** `helm-fzf`, `consult-fzf`, and similar packages.
- **Termux (Android).** Same fzf binary; integrate via Termux's
  built-in shell integration.

### 9.6 The `--listen` HTTP / Unix-socket API

External processes drive fzf actions over HTTP:

```
POST /                              Body: action chain ("change-query(foo)+reload(...)")
GET  /?limit=N&offset=M             Returns JSON: {"items": [...], "selection": [...], "current": ...}
```

Headers: `X-API-Key: <FZF_API_KEY>` (mandatory when non-local).
Content type: `text/plain` (request) / `application/json` (response).
Max body length: 1 MiB. Read deadline: 10 s.

> [!NOTE]
> The server is intentionally minimal — no `net/http`, no TLS, no
> session state. For TLS, front it with a reverse proxy such as
> `caddy reverse_proxy localhost:6266` over a TLS-terminated listener.

### 9.7 Programmatic invocation patterns

| Use case | Pattern |
|---|---|
| Quick pick | `result=$(some-source | fzf)` |
| Multi-pick | `mapfile -t result < <(some-source | fzf --multi)` |
| Headless filter | `some-source | fzf --filter=QUERY` |
| Two-step (query first, then act) | `query=$(printf '\0' | fzf --print-query --bind=enter:print-query+abort)` |
| Editor launcher | `fzf --bind 'enter:become(${EDITOR} {})'` |
| Live grep | (see User Guide §8.2 — `--disabled` + `change:reload`) |

---

_Generated April 2026 from junegunn/fzf at 06e7057. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
