Documentation

TruthCheck Docs

Everything you need to understand, run, and extend TruthCheck v6.1.

What is TruthCheck?

TruthCheck is an AI-powered bilingual fact-checking application. It verifies claims against global evidence using DeepSeek Reasoner and real-time web search (Brave Search primary, DuckDuckGo fallback) — available in English and Spanish.

  • Bilingual UI (EN/ES) — full interface and AI output in both languages
  • AI Claim Extraction — identifies atomic, verifiable statements from any text
  • Parallel Verification — all claims verified simultaneously for minimum wait time
  • Progressive Results — cards appear incrementally as each verification completes
  • Truth Scoring — 0–5 score with four labels (Likely True, Uncertain, Unverified, Contradicted)
  • Source Evidence — up to 4 web search results per claim as clickable source cards
  • Real-time Status — progress bar, pending claim feed, elapsed timer, live domain feed
  • Copy Result — one-click clipboard export of claim, verdict, reasoning, and sources
  • Character Counter — amber/red threshold warnings at 12k/15k chars
  • Keyboard Shortcut — Ctrl+Enter / ⌘+Enter to submit
  • Dark Mode — follows system preference automatically
  • Error Monitoring — Sentry integration for server-side exceptions

Quick Start

Prerequisites

  • Node.js 20+
  • DeepSeek API key — platform.deepseek.com
  • Brave Search API key (optional — required on Vercel/cloud where DuckDuckGo is blocked)

Setup

git clone <repo-url>
cd truth-checker
npm install

# Copy and fill in environment variables
cp .env.example .env.local
# Required: DEEPSEEK_API_KEY
# Optional: BRAVE_SEARCH_API_KEY

npm run dev

App available at http://localhost:3000

Common Commands

npm run dev          # Start dev server
npm run build        # Production build
npm run start        # Start production server
npm run lint         # ESLint
npm test             # Run all tests (Vitest)
npm run test:watch   # Watch mode

Tech Stack

LayerTechnology
FrameworkNext.js 16.2.3 (App Router, standalone)
AI — ExtractionDeepSeek Chat (deepseek-chat)
AI — ReasoningDeepSeek Reasoner (deepseek-reasoner)
SearchBrave Search API (primary) / DuckDuckGo HTML scraper (fallback)
ValidationZod — LLM response schema enforcement
Error MonitoringSentry (@sentry/nextjs)
i18nCustom lib/i18n.ts + React Context
AnalyticsVercel Analytics
TestingVitest + React Testing Library
StylingTailwind CSS v4
IconsLucide React
Reverse Proxynginx 1.27 (TLS, rate limiting)
ContainerizationDocker / Docker Compose

Security

ControlDetails
Rate LimitingPer-IP sliding window: 5 req/min (/analyze), 10 req/min (/extract), 20 req/min (/verify)
Security HeadersCSP, HSTS (1 year + preload), X-Frame-Options: DENY, X-Content-Type-Options, Permissions-Policy
Input Validation15,000 char limit; claim.id format enforced; empty input rejected
SecretsHard startup failure if DEEPSEEK_API_KEY is missing — no mock/fallback
Network IsolationNext.js on internal Docker network only; nginx is the sole public entry point
TLSMozilla Intermediate config — TLS 1.2/1.3, strong ciphers, OCSP stapling
Audit LoggingStructured JSON logs on every API request (timestamp, IP, route, payload size, duration, status)

Architecture

TruthCheck is built on Next.js 16 App Router. All AI logic runs server-side in API routes. The frontend is a single client component (app/page.tsx) that orchestrates the full pipeline.

Analysis Pipeline

  1. User submits text → POST /api/extract → DeepSeek Chat extracts atomic claims
  2. Frontend fans out N parallel POST /api/verify calls — one per claim
  3. Each verify call searches Brave Search (DuckDuckGo fallback) → sends claim + evidence to DeepSeek Reasoner
  4. Results stream in one-by-one; each replaces its skeleton card as it arrives

Key Files

app/page.tsxRoot client component — UI layout and prop wiring
hooks/useAnalyze.tshandleAnalyze() — pipeline state, fetch orchestration, error handling
app/api/extract/route.tsPOST /api/extract — claim extraction via deepseek-chat
app/api/verify/route.tsPOST /api/verify — evidence search + reasoning via deepseek-reasoner
lib/analyzer.tsextractClaims() + verifyClaim() — core AI logic with Zod validation
lib/search.tssearchEvidence() — Brave Search primary, DuckDuckGo fallback
lib/i18n.tsTranslation dictionary (en/es) + dynamic string helpers
lib/i18nContext.tsxLanguageProvider + useLanguage() hook

Development Guide

Adding a Translation Key

  1. Add the English string to dict.en in lib/i18n.ts
  2. Add the Spanish string to dict.es with the same key
  3. Use it in any client component: const { t } = useLanguage(); t('my_key')

The test suite automatically enforces that dict.en and dict.es have identical keys.

Styling

Tailwind CSS v4. Custom utility classes in app/globals.css:

  • .glass — glassmorphism with backdrop blur
  • .gradient-text — indigo-to-cyan gradient text
  • .hero-pattern — radial dot grid background

Docker

docker compose up -d --build   # Build and start
docker compose down             # Stop

API Reference

POST/api/extract

Extracts atomic factual claims from a body of text using deepseek-chat.

// Request
{ "text": "...", "language": "en" }

// Response
{ "claims": [{ "id": "claim-0", "text": "...", "originalTextQuote": "..." }] }

POST/api/verify

Verifies a single claim via web search + DeepSeek Reasoner. Pass "language": "es" to receive reasoning in Spanish.

// Request
{ "claim": { "id": "claim-0", "text": "...", "originalTextQuote": "..." }, "language": "en" }

// Response
{ "result": { "claimId": "claim-0", "score": 0, "reasoning": "...", "sources": [...] } }

Score Interpretation

ScoreLabelMeaning
0ContradictedEvidence explicitly contradicts the claim
1UnverifiedNo relevant evidence found
2–3UncertainEvidence is partial, ambiguous, or inconclusive
4–5Likely TrueMultiple sources explicitly confirm the claim