Verity is a Chrome Extension (Manifest V3) for article-level credibility analysis. It extracts article text in the browser, sends that text to a serverless proxy, evaluates verifiable claims and rhetorical tone with a selected model, and returns a trust score, letter grade, and claim-by-claim report.
The current system is model-selectable, cache-aware, and optimized to avoid wasteful repeat calls.
┌──────────────────────────────────────────────────────────┐
│ Chrome Extension (MV3) │
│ ┌──────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ Content │ │ Service │ │ Popup (React + │ │
│ │ Script │◄─┤ Worker │◄─┤ Tailwind) │ │
│ │ Shadow DOM│ │ (background) │ │ Score + report UI │ │
│ └──────────┘ └──────┬───────┘ └────────────────────┘ │
└─────────────────────────┼────────────────────────────────┘
│ HTTPS
▼
┌───────────────────────┐
│ Vercel Serverless │
│ Proxy (api/check) │
│ ┌─────────────────┐ │
│ │ Analysis Flow │ │
│ │ 1. Decompose │ │
│ │ 2. Evaluate │──┼──► Gemini / OpenAI / OpenRouter
│ │ 3. Tone Detect │ │
│ │ 4. Score │ │
│ └─────────────────┘ │
└───────────────────────┘
-
Article extraction The content script detects article pages and extracts title, body text, site metadata, and simple media counts.
-
Model-specific analysis The popup lets the user choose a configured model. The selected model is the only model used for that run. There is no automatic fallback to another provider.
-
Claim decomposition The proxy asks the model to extract up to
5atomic, verifiable claims from the article text. -
Claim evaluation Those claims are evaluated in a single batched LLM call. When the chosen model supports search grounding, that evaluation call can use live web search. Each claim receives:
- a status:
verified,disputed,misleading, orunverified - a confidence score
- a short rationale
- optional fallacy labels
- optional supporting source links
- Tone analysis If claims were extracted, Verity runs a separate tone scan for:
- emotional manipulation
- bias / one-sided framing
- misleading or manipulative wording
- Score generation
The proxy turns claims and tone alerts into a trust score from
0-100, then maps that score to a grade fromA-F.
Verity starts from 100 and subtracts penalties:
disputedclaim:25 * confidenceWeightmisleadingclaim:15 * confidenceWeightunverifiedclaim:3- tone alert with
highseverity:10 - tone alert with
mediumseverity:5 - tone alert with
lowseverity:2
confidenceWeight is claim.confidence / 100.
Final score is clamped to 0-100 and converted to a grade:
A:80-100B:60-79C:40-59D:20-39F:0-19
Special case:
- If no verifiable claims are extracted, Verity returns a neutral baseline score of
50and skips further claim/tone model calls for efficiency.
The proxy can expose multiple model options, depending on which API keys are configured:
gemini-groundedgemini-fastopenai-gpt-4.1-miniopenrouter-free-routeropenrouter-llama-3.2-3b-free
Availability is detected at runtime from proxy environment variables.
- Node.js 18+
- A Gemini API key for Gemini models
- Optional: an OpenAI API key for alternative model selection
- Optional: an OpenRouter API key for free-model routing (
openrouter/free,:freevariants) - A Vercel Hobby account (free)
cd proxy
cp .env.example .env # Add your API keys
npm install
vercel dev # Local dev server on :3000To deploy:
vercel --prodThen set environment variable(s) in the Vercel dashboard:
GEMINI_API_KEY(for Gemini models)OPENAI_API_KEY(optional, for OpenAI model)OPENROUTER_API_KEY(optional, for OpenRouter free models)
cd extension
cp .env.example .env # Set VITE_PROXY_URL to your proxy URL
npm install
npm run build- Open
chrome://extensions - Enable Developer mode
- Click Load unpacked
- Select the
extension/distfolder
Run the proxy locally:
cd proxy && vercel devBuild the extension in watch mode (rebuild on changes):
cd extension && npm run buildAfter rebuilding, click the refresh icon on chrome://extensions to reload.
| Variable | Location | Description |
|---|---|---|
GEMINI_API_KEY |
proxy/.env |
Enables gemini-grounded and gemini-fast |
OPENAI_API_KEY |
proxy/.env |
Enables openai-gpt-4.1-mini |
OPENROUTER_API_KEY |
proxy/.env |
Enables openrouter-free-router and free variant models |
VITE_PROXY_URL |
extension/.env |
URL of the deployed proxy |
Verity caches completed analysis results in the extension service worker using:
article URLselected model
Cache behavior:
- Cached results live for
1 hour - Reopening the popup reuses cached results when available
- Previous scores are shown per analyzed model for the same article URL
Re-analyzeclears the cache for the currentURL + modelpair and runs fresh analysis
In-flight protection:
- The background worker deduplicates concurrent requests for the same
URL + model - The popup includes a
Cancel Analysisaction - Cancel aborts the extension-side fetch and clears the in-flight tracking entry
The current implementation is intentionally conservative about token spend:
- One model is used per analysis run; there is no cross-model fallback
- Claim evaluation is batched into one LLM call instead of one call per claim
- Claim extraction is capped to
5claims - Article text is truncated before sending to the model
- Empty-claim runs do not retry decomposition
- Empty-claim runs skip tone detection entirely
- Provider
5xxretries are limited to1retry
Typical request pattern per successful run:
1claim decomposition call1claim evaluation call0or1tone analysis call
When providers return quota or rate-limit errors:
- The proxy returns short user-facing messages instead of raw provider dumps
- The popup shows attempt diagnostics
- Error and limit screens include a safe way back to the detected article view
- The popup auto-scrolls to the top when an error/limit state is entered so the message is immediately visible
- Page images and videos are detected for visibility only
- The current pipeline sends extracted article text only
- No image bytes, video frames, or transcripts are sent to the model yet
Request optimization:
- The pipeline uses batched evaluation rather than one model call per claim.
- Cached results are reused by
URL + model. - Empty-claim runs stop early instead of continuing into extra model calls.
- The popup health check does not consume model requests.
- Provider retries are intentionally limited to reduce accidental token spikes.
Provider diagnostics:
- The proxy logs provider limit responses with model id, stage, HTTP status, retry-after, request id, and provider error text so you can distinguish quota exhaustion from rate limiting or an ambiguous
429. - The popup mirrors those attempt diagnostics when an analysis is blocked.
verity/
├── extension/ # Chrome Extension (MV3)
│ ├── manifest.json
│ ├── popup.html
│ ├── src/
│ │ ├── popup/ # React popup UI
│ │ │ ├── App.tsx
│ │ │ └── components/
│ │ │ ├── NutritionLabel.tsx
│ │ │ ├── TrustBadge.tsx
│ │ │ ├── ClaimCard.tsx
│ │ │ ├── ToneAlerts.tsx
│ │ │ └── AnalysisProgress.tsx
│ │ ├── content/ # Content scripts (Shadow DOM)
│ │ │ ├── extractor.ts
│ │ │ └── overlay.ts
│ │ ├── background/ # Service worker
│ │ │ └── service-worker.ts
│ │ └── lib/ # Shared types & utilities
│ └── dist/ # Build output → load in Chrome
└── proxy/ # Vercel serverless proxy
└── api/
├── check.ts # SAFE pipeline endpoint
└── factcheck.ts # Standalone claim search endpoint
The single GEMINI_API_KEY never touches the client-side bundle. All Gemini and search-grounded calls are proxied through the Vercel serverless layer, which reads the key from process.env at runtime.
MIT
