Documentation

TruthCheck Docs

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

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
  • Empty-State Handling — an explicit "no verifiable claims found" message when extraction succeeds with zero claims
  • 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 run typecheck    # tsc --noEmit
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; max 10 claims per extraction; 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)
Prompt Injection HardeningFixed fact-checking policy sent as a system message; claim + evidence confined to a delimited, length-bounded user message
Proxy-Safe Client IPRate limiting and audit logs key on a deployment-specific trusted header (X-Real-IP behind nginx, X-Forwarded-For on Vercel) — never a spoofable client-supplied value

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 (max 10)
  2. Frontend fans out 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. Returns at most 10 claims.

// 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
-1ErrorVerification could not run (search or API failure)
0ContradictedEvidence explicitly contradicts the claim
1UnverifiedNo relevant evidence found
2–3UncertainEvidence is partial, ambiguous, or inconclusive
4–5Likely TrueMultiple sources explicitly confirm the claim