fafi
fafi
Web content indexing and search tool.
- Easily index your browser bookmarks: importing list of files, a live Firefox profile, and individual URLs
- Store the readability content of each bookmark
- Full text search matching the title and contents of the collection
- Detect non-text bookmarks (PDFs, images, video, audio) — saved with a category icon, skipped for article extraction.
In the latest incarnation:
- Aim of the rewrite in GO is to have a single binary that starts a webserver which can be started via systemd and queried via firefox.
- On launch it will index any missing bookmarks (in a background process)
This is how it looks:
Searching
The search box accepts plain words plus a few extras:
- Phrase queries — wrap words in double quotes:
"react hooks". - Prefix queries — the last unquoted token is auto-prefixed, so
lidaralso matcheslidarr. Add an explicit*(e.g.desig*) to control where prefixing applies. - Domain queries — paste a URL or host (e.g.
lidarr.audio); punctuation is split into tokens so it still matches. - Porter stemming —
designmatchesdesigns,designed,designing. Powered by the FTS5porter unicode61tokenizer. - Highlighting — matched terms are highlighted in titles, URLs, and snippets. Click a snippet to expand the full extracted text.
- Results are filtered by HTTP status with the chips above the list (2xx / 3xx / 4xx / 5xx / none).
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:
- Reindex — re-fetches the page and refreshes the stored title/text.
- Delete — soft-deletes the bookmark. Soft-deleted entries are hidden and blocked from re-import (e.g. on the next Firefox sync), so accidental re-adds don't bring them back.
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