← all projects

fafi

Web content indexing and search tool
repo bookmarks cli favourites firefox indexer python search-engine tui webui

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:

image

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

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