← all projects

fafi

Go CLI and web UI that indexes browser bookmarks and full-text searches their content
repo bookmarks cli firefox full-text-search go indexer search-engine tui

fafi

Web content indexing and search tool.

  1. Easily index your browser bookmarks: importing list of files, a live Firefox profile, and individual URLs
  2. Store the readability content of each bookmark
  3. Full text search matching the title and contents of the collection
  4. Detect non-text bookmarks (PDFs, images, video, audio) — saved with a category icon, skipped for article extraction.

In the latest incarnation:

This is how it looks:

Screenshot 2026-07-22 at 22-34-02 Fafi

Searching

The search box accepts plain words plus a few extras:

Keyboard shortcuts

Press ? in the UI for the full list. Currently:

| Key | Action | | --- | --- | | / | Focus the search box | | Esc | Clear search, then blur | | j / ↓ | Next result | | k / ↑ | Previous result | | Enter | Open the selected result | | Ctrl/⌘+Enter | Open in new tab | | ? | Toggle the shortcut help |

Managing bookmarks

Each row has inline actions:

Environment variables

If the working directory contains a .env file, then the following configuration can be declared:

# Defaults are below:

# Port number for the webserver.
FAFI_PORT=8080
# Set to non-empty value to skip populating the database with sample records.
FAFI_SKIP_RECORDS=0
# Set to 0 to disable indexing on startup
FAFI_ENABLE_INDEXING=1
# Set to 1 to clear the indexed state on every bookmark before indexing
# (forces a full re-index). Also migrates legacy databases to the latest
# schema version. Unset after one run.
FAFI_RESET_INDEX=0
# Set to 1 to clear every stored HTTP status and re-probe all bookmarks
# (destructive). Unset after one run.
FAFI_RESET_STATUS=0
# Set to 1 to probe only bookmarks that have no stored HTTP status, leaving
# known-good entries alone. Idempotent — safe to leave on but typically set
# once after a schema upgrade then unset. Also re-probes rows whose previous
# probe failed at the network layer (DNS, timeout, refused), and runs the
# auto-soft-delete pass for stale unreachable bookmarks (>=3 failures, first
# failure >14 days ago, no scraped text).
FAFI_BACKFILL_STATUS=0
# Set to 1 to soft-delete every bookmark whose probe currently fails AND has
# no scraped text. Manual escape hatch — bypasses the time/strike rule.
FAFI_PURGE_UNREACHABLE=0
# Default database path:
FAFI_DB_FILEPATH=/home/user/fafi.sqlite3

# Enable importing bookmarks from Firefox profile db:
FAFI_FIREFOX=/home/san.../32kuswpy.default-release/places.sqlite

# Privacy-preserving malware blocklist. On by default. Downloads the
# URLhaus "online URLs" feed to FAFI_BLOCKLIST_DIR on the configured
# interval; all per-bookmark checks happen locally against the snapshot.
# Set FAFI_BLOCKLIST_ENABLED=0 to disable the feature entirely.
FAFI_BLOCKLIST_ENABLED=1
FAFI_BLOCKLIST_DIR=blocklists
FAFI_BLOCKLIST_REFRESH=6h

Command-line arguments

Each of the environment variables are available as a longform command-line argument by discarding FAFI_ and lower-casing the result, replacing underscores with dashes. For example FAFI_ENABLE_INDEXING=0 and --enable-indexing=0 are equivalent.

Build and run

To skip building, download fafi2 for Linux (amd64, arm64) or macOS (arm64) from the releases page.

The project uses devbox to pin the Go toolchain. With devbox installed:

make build              # build tmp/fafi2 (--tags fts5)
make test               # go test -race ./...
make run                # build and run locally
make restart            # systemctl --user restart fafi.service
make migrate            # build + restart (schema migrations run on startup)

Schema migrations run automatically on every startup — scraped content is preserved, so upgrading the binary and restarting the service is all that's needed. Use FAFI_RESET_INDEX=1 only when you want to force a full re-scrape.

To build without make:

$ devbox run -- go build --tags fts5 -o tmp/fafi2 fafi2
$ tmp/fafi2 --firefox=/path/to/firefox/profile/places.sqlite