Fact Checker is a Chrome extension for real-time fact-checking of images and text, built by Aleksei Bukhalov. It uses Google Vision, TinEye, GDELT, and LLM-based verdict synthesis via a FastAPI backend.
- Image fact-checking: Right-click any image → verdict with sources, confidence score, red flags
- Text fact-checking: Select text → right-click → verdict with source citations
- Free checks without signup: 3 free checks per device per day (no account needed)
- Onboarding page: Opens automatically on first install with a 3-step guide
- Dark mode: Automatically follows system preference
- Check history: Last 50 checks stored locally with verdict chips and relative timestamps
- Keyboard shortcut:
Ctrl+Shift+F(orCmd+Shift+Fon Mac) to fact-check selected text - Extension badge: Shows verdict symbol (✓/✗/!/?) after each check, auto-clears after 2 min
- Share results: Copy verdict summary to clipboard with one click
- Shareable permalinks: Public verdict pages at
/v/{check_id}with OpenGraph tags - Error recovery: Categorized errors (auth/credits/network/server) with contextual actions
- Shared cache: Viral content checked once, free for all users (no credit deduction)
- AGENTS.md — Guide for AI agents (read before contributing)
- docs/ARCHITECTURE.md — System design, pipelines, caching
- Extension (Chrome MV3): Context menu + side panel, multi-strategy image extraction, onboarding page, Google OAuth + magic link
- Backend (Python/FastAPI): API orchestration, shared Redis cache, PostgreSQL, LLM verdict synthesis, Celery background rechecks
- Data sources: Google Cloud Vision, TinEye, Google Fact Check, GDELT, Brave Search, LLM (Gemini Flash via LiteLLM)
- Free checks: Device-scoped quota (3/day) without signup, tracked via X-Device-Id header
- Rate limiting: Per-user token bucket + per-content-hash limit (5 checks/hash/hour) to prevent abuse
- Source seed list: 20 curated credible sources (Snopes, PolitiFact, AP, Reuters, BBC, etc.) at
GET /checks/sources - Caching: Redis with bucketed phash index for near-duplicate image matching, normalized text cache keys, image verdicts cached 7 days, text verdicts use confidence-based TTL (3 days for high-confidence, 24h otherwise)
- Performance: Shared HTTP client pool (httpx), Google Vision client reuse, parallel OAuth calls, composite DB indexes, Redis pipelining for analytics
- Analytics: Redis counters + HyperLogLog for real-time metrics, Prometheus for time-series, 5 admin endpoints for dashboard data
- Public permalinks: Verdict pages at
/v/{check_id}with OpenGraph tags for social sharing (SEO flywheel)
- Google OAuth — one click for Chrome users
- Magic link — enter email, get 6-digit code, no password needed
- Email/password — traditional fallback
Facebook and GitHub OAuth have been removed (backend returns 410 Gone).
No subscriptions. Buy credits, use them as needed.
| Pack | Credits | Price |
|---|---|---|
| Starter | 100 checks | $5 |
| Pro | 500 checks | $20 |
| Bulk | 1500 checks | $50 |
- New users: 10 free credits (welcome bonus)
- Anonymous users: 3 free checks per device per day (no signup required)
- Cached results: Free (no credit deduction) — viral content shared across users
- Credits never expire
- Pro tier: Unlocked on Pro pack purchase, persists even after credits are spent. Unlocks TinEye + Brave Search.
- Python 3.11+
- Node.js 20+
- Docker (for Redis + PostgreSQL)
cd backend
cp .env.example .env # fill in API keys
pip install -e ".[dev]"
docker compose -f ../infra/docker-compose.yml up -d redis postgres
alembic upgrade head
uvicorn app.main:app --reload --port 8000cd extension
npm install
npm run dev # builds to dist/
# Load dist/ as unpacked extension in chrome://extensionscp .env.example .env
docker compose -f infra/docker-compose.yml up -d# Backend tests
cd backend && pytest
# Extension tests
cd extension && npm test
# Type checking
cd extension && npm run typecheckPOST /auth/register— Register with email/password, returns JWT + 10 free creditsPOST /auth/login— Login with email/passwordPOST /auth/oauth— Social login (google only; facebook/github return 410 Gone)POST /auth/refresh— Refresh access tokenGET /auth/me— Get current user infoPOST /auth/magic-link/request— Send 6-digit code to emailPOST /auth/magic-link/verify— Verify code and login
POST /checks/image— Check image (multipart upload, auth required)POST /checks/image/base64— Check image (base64 JSON, auth or X-Device-Id)POST /checks/image/stream— Check image with SSE progressPOST /checks/text— Check text claim (auth or X-Device-Id for free checks)POST /checks/text/stream— Check text with SSE progressGET /checks/sources— List curated credible source seed list (20 sources)
POST /reports/{check_id}— Report a check as inaccurate (invalidates cache + re-runs)
GET /v/{check_id}— Public verdict page with OpenGraph tags for social sharing
GET /billing/packs— List available credit packsPOST /billing/checkout— Create Stripe checkout sessionPOST /billing/webhook— Stripe webhook handler (idempotent)GET /billing/credits— Get current credit balanceGET /billing/transactions— Get transaction history
GET /analytics/summary— Total checks, cache hit rate, DAUGET /analytics/latency— Latency percentiles (p50/p95/p99) by typeGET /analytics/cache— Cache hits/misses/hit_rate by typeGET /analytics/adapters— Adapter call counts, error ratesGET /analytics/credits— Credits purchased/consumed, purchase count
All analytics endpoints require X-Admin-API-Key header. Set ADMIN_API_KEY in .env.
GET /health— Basic health checkGET /health/detailed— Detailed health with service status
GET /metrics— Prometheus metrics endpoint (auto-instrumented viaprometheus-fastapi-instrumentator). Scrape with Prometheus, visualize in Grafana.
To enable Google social login, fill in the client ID in extension/src/shared/config.ts:
- Google: Create OAuth 2.0 Client ID at Google Cloud Console
The redirect URI is: https://<extension-id>.chromiumapp.org/
Use chrome.identity.getRedirectURL() to get it programmatically.
Facebook and GitHub OAuth are no longer supported.
Aleksei Bukhalov — SDET.
- Website: https://bukhalov.com
- GitHub: https://github.com/liqlos
- LinkedIn: https://www.linkedin.com/in/abukhalov/
MIT