import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AlertTriangle, BadgeCheck, BrainCircuit, CheckCircle2, Cpu, Database, Download, Eye, EyeOff, FileJson, FileText, Gauge, ImageUp, Layers3, Link, Loader2, Lock, KeyRound, RefreshCw, ShieldCheck, ShieldAlert, Trash2, Workflow, } from 'lucide-react'; const API_BASE = import.meta.env.VITE_API_BASE_URL || (window.location.port === '5173' || window.location.port === '4173' ? 'http://127.0.0.1:8000' : window.location.origin); const ACCESS_TOKEN_ENABLED = import.meta.env.VITE_REQUIRE_ACCESS_TOKEN === 'true'; const verdictCopy = { likely_real: 'Likely Real', likely_ai_generated: 'Likely AI Generated', likely_manipulated_or_deepfake: 'Likely Manipulated', inconclusive: 'Inconclusive', }; const architectureStages = [ { title: 'Private Intake', detail: 'Upload or public URL, consent gate, MIME validation, SSRF protection, and encrypted temporary media.', checks: ['15 MB and pixel limits', 'Public URL restrictions', '15-minute media TTL'], icon: Lock, }, { title: 'Provenance First', detail: 'EXIF/XMP, C2PA content credentials, software markers, source context, and cryptographic hashes.', checks: ['Missing metadata stays neutral', 'C2PA claims are verified', 'GPS is redacted in reports'], icon: Database, }, { title: 'Calibrated MoE', detail: 'Community Forensics runs across five benign transforms, then two independent models provide counter-opinions.', checks: ['Model-specific thresholds', 'Five-view stability check', 'Raw logits never equal truth'], icon: BrainCircuit, }, { title: 'Safety Arbiter', detail: 'Calibrated stances are combined with provenance and forensic counter-evidence before any verdict is emitted.', checks: ['Primary-anchored consensus', 'Real-vote false-positive guard', 'Abstention when evidence conflicts'], icon: ShieldAlert, }, { title: 'Evidence Report', detail: 'A victim-friendly summary and technical PDF/JSON ledger preserve the reasoning, caveats, and reproducibility data.', checks: ['Decision and counter-evidence', 'Model and layer ledgers', 'Early deletion endpoint'], icon: FileText, }, ]; const expertPanels = [ { title: 'Broad Primary', detail: 'Community Forensics checks original, crop, JPEG, mirrored, and social-resize views before its stance is trusted.', signals: ['Five-view median', 'MAD/IQR stability', 'Broad generator coverage'], guardrail: 'A stable primary score still cannot decide the verdict alone.', }, { title: 'Counter-Models', detail: 'Two independently trained classifiers challenge the primary with different data and decision boundaries.', signals: ['Hard AI vote', 'Real/human vote', 'Abstention-band vote'], guardrail: 'A real vote or strong disagreement blocks an overconfident accusation.', }, { title: 'Forensic Residuals', detail: 'Image layers are decomposed into compression, noise, edge, frequency, and tile-level evidence.', signals: ['ELA score', 'Noise inconsistency', 'Regional anomaly severity'], guardrail: 'Forensics are weak supporting signals, never final proof alone.', }, { title: 'Provenance', detail: 'Metadata and content credentials are checked before pixel-based conclusions are trusted.', signals: ['EXIF/XMP fields', 'C2PA status', 'SHA/perceptual hashes'], guardrail: 'Missing metadata is treated as neutral, not proof of AI generation.', }, ]; const moeExperts = [ { title: 'Community Forensics', detail: 'Broad ViT primary trained on a highly diverse synthetic-image corpus and evaluated across five benign transforms.', output: 'Primary stance + stability', icon: Cpu, }, { title: 'Ateeqq Counter-Model', detail: 'Independent visual classifier with a strict AI threshold and reduced reliability weight.', output: 'Hard AI / real / abstain', icon: BrainCircuit, }, { title: 'Distilled Counter-Model', detail: 'A lightweight detector with a different boundary that widens model diversity.', output: 'Independent counter-opinion', icon: Gauge, }, { title: 'Forensic Expert', detail: 'Noise, ELA, frequency, edge, and regional-map evidence that supports or challenges model scores.', output: 'Non-model evidence', icon: Layers3, }, { title: 'Provenance Expert', detail: 'EXIF/XMP, C2PA status, generative markers, public URL context, and hashes.', output: 'Source context', icon: Database, }, ]; const moeRules = [ 'The broad primary must remain stable across original, crop, JPEG, mirror, and social-resize views.', 'Each raw score enters a model-specific AI, real, or abstention band.', 'Primary-anchored model-only consensus requires every counter-expert to lean AI.', 'A real/human vote, poor input quality, or disagreement lowers the evidence score.', 'The safety arbiter preserves inconclusive when independent evidence does not agree.', ]; const deploymentControls = [ { title: 'Ephemeral Storage', detail: 'Encrypted temp media, short TTLs, early delete endpoint, no raw media returned.', checks: ['15-minute raw-media TTL', '24-hour job/report metadata', 'User-triggered deletion'], }, { title: 'Public Safety', detail: 'No face search, no doxxing, no login scraping, no private identity inference.', checks: ['Public links only', 'No identity attribution', 'Sensitive-preview blur'], }, { title: 'Runtime Guardrails', detail: 'Rate limits, privacy-safe audit identifiers, readiness checks, security headers, and background execution.', checks: ['Health and readiness probes', 'Per-client rate limiting', 'No raw media in logs'], }, { title: 'Scale Profile', detail: 'The same API supports PostgreSQL and Redis/RQ when deployed across multiple workers and instances.', checks: ['Server database validation', 'Queue-backed workers', 'Deployment-time safety checks'], }, ]; const reportFeatures = [ { title: 'Victim Summary', detail: 'Plain-language verdict, confidence band, strongest evidence, and practical next steps.', includes: ['Verdict and confidence', 'Strongest evidence', 'Limitations and next steps'], icon: FileText, }, { title: 'Evidence Ledger', detail: 'Eleven layers expose provenance, model robustness, forensic residuals, regional differences, and uncertainty.', includes: ['Decision role and reliability', 'Direction and influence', 'Counterfactual and limitations'], icon: Layers3, }, { title: 'Model Arbitration', detail: 'Primary and counter-model scores, five-view stability, calibrated stances, attribution, and arbiter policy.', includes: ['Raw versus calibrated scores', 'Five-view MAD/IQR stability', 'Abstention rationale'], icon: BrainCircuit, }, { title: 'Technical Export', detail: 'JSON and PDF downloads with hashes, detector scores, regional map, and reproducibility notes.', includes: ['SHA/perceptual hashes', 'Detector scores', 'Reproducibility notes'], icon: Download, }, ]; const heroHighlights = [ ['Calibrated', 'Model-specific gates turn raw logits into AI, real, or abstain stances.'], ['Victim-safe', 'The arbiter preserves uncertainty instead of turning weak signals into accusations.'], ['Private', 'Raw media expires after 15 minutes and is never used for face or identity search.'], ]; const methodFacts = [ ['5 views', 'Primary stability check', Cpu], ['3 models', 'One primary, two counter-experts', BrainCircuit], ['11 layers', 'Provenance, transforms, residuals, regions', Layers3], ['15 min', 'Raw-media deletion window', Lock], ['PDF + JSON', 'Human and machine-readable evidence', FileText], ]; function App() { const [mode, setMode] = useState('upload'); const [file, setFile] = useState(null); const [url, setUrl] = useState(''); const [consent, setConsent] = useState(false); const [analysis, setAnalysis] = useState(null); const [error, setError] = useState(''); const [submitting, setSubmitting] = useState(false); const [accessToken, setAccessToken] = useState(() => window.localStorage.getItem('aida_access_token') || ''); const [previewBlurred, setPreviewBlurred] = useState(true); const previewUrl = useMemo(() => (file ? URL.createObjectURL(file) : ''), [file]); const pollRef = useRef(null); const authHeaders = useCallback(() => ( ACCESS_TOKEN_ENABLED && accessToken.trim() ? { 'X-AIDA-Access-Token': accessToken.trim() } : {} ), [accessToken]); const refreshAnalysis = useCallback(async (id, options = {}) => { try { const response = await fetch(`${API_BASE}/analyses/${id}`, { headers: authHeaders() }); const payload = await parseResponse(response); setAnalysis(payload); } catch (err) { if (!options.quiet) setError(err.message); } }, [authHeaders]); useEffect(() => () => previewUrl && URL.revokeObjectURL(previewUrl), [previewUrl]); useEffect(() => { if (!ACCESS_TOKEN_ENABLED) return; if (accessToken.trim()) { window.localStorage.setItem('aida_access_token', accessToken.trim()); } else { window.localStorage.removeItem('aida_access_token'); } }, [accessToken]); useEffect(() => { if (!analysis?.id || !['pending', 'running'].includes(analysis.status)) return undefined; pollRef.current = window.setInterval(() => { refreshAnalysis(analysis.id, { quiet: true }); }, 1600); return () => window.clearInterval(pollRef.current); }, [analysis?.id, analysis?.status, refreshAnalysis]); async function submitAnalysis(event) { event.preventDefault(); setError(''); setSubmitting(true); setAnalysis(null); try { const form = new FormData(); form.append('consent_confirmed', consent ? 'true' : 'false'); if (mode === 'upload') { if (!file) throw new Error('Choose an image file.'); form.append('file', file); } else { if (!url.trim()) throw new Error('Enter a public URL.'); form.append('url', url.trim()); } const response = await fetch(`${API_BASE}/analyses`, { method: 'POST', body: form, headers: authHeaders() }); const payload = await parseResponse(response); setAnalysis(payload); await refreshAnalysis(payload.id, { quiet: true }); } catch (err) { setError(err.message); } finally { setSubmitting(false); } } async function downloadReport(format) { if (!analysis?.id) return; try { const response = await fetch(`${API_BASE}/analyses/${analysis.id}/report?format=${format}`, { headers: authHeaders() }); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload.detail || `Download failed with HTTP ${response.status}`); } const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = `analysis-${analysis.id}.${format}`; document.body.appendChild(anchor); anchor.click(); anchor.remove(); window.URL.revokeObjectURL(url); } catch (err) { setError(err.message); } } async function deleteAnalysis() { if (!analysis?.id) return; try { const response = await fetch(`${API_BASE}/analyses/${analysis.id}`, { method: 'DELETE', headers: authHeaders() }); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload.detail || `Delete failed with HTTP ${response.status}`); } setAnalysis(null); setError(''); } catch (err) { setError(err.message); } } const result = analysis?.result; const verdict = result?.verdict; return (

Privacy-first authenticity analysis

AI Deepfake Analyzer

Ephemeral media
{ACCESS_TOKEN_ENABLED && ( )} {mode === 'upload' ? ( ) : ( )} {error && (
{error}
)}
{!analysis && } {analysis && !result && (

{analysis.status === 'failed' ? 'Analysis failed' : 'Analysis running'}

{analysis.error || 'Preparing the evidence layers.'}

)} {result && (

{verdictCopy[verdict.label] || verdict.label}

{Math.round(verdict.ai_probability * 100)}% AI evidence score

{verdict.confidence} confidence

This is calibrated evidence strength, not the probability that a person or image is fake.

)}
); } function LandingPage() { return ( <>
Public analyzer deployed Evidence score, not certainty

Public evidence triage for synthetic media abuse

AI Deepfake Analyzer

A privacy-first authenticity platform that combines a multi-view visual ensemble, provenance checks, pixel forensics, and a false-positive-aware safety arbiter. Every result shows what supported it, what contradicted it, and why the system may still be wrong.

Start Analysis View Architecture
{heroHighlights.map(([title, detail]) => (
{title} {detail}
))}
{methodFacts.map(([value, label, icon]) => { const FactIcon = icon; return (
{value} {label}
); })}

System architecture

One image enters. Independent evidence lanes return to a calibrated safety arbiter.

Provenance, visual models, and low-level forensics are deliberately separated so one noisy family of signals cannot silently dominate the conclusion.

{architectureStages.map((stage, index) => { const Icon = stage.icon; return (

{stage.title}

{stage.detail}

    {stage.checks.map((check) => (
  • {check}
  • ))}
); })}

Calibrated mixture of experts

Raw model outputs are converted into stances before they can influence a person.

The primary detector checks five transformed views. Two counter-models challenge it, while provenance and forensic experts contribute independent evidence. The arbiter can still abstain.

{expertPanels.map((panel) => (

{panel.title}

{panel.detail}

{panel.signals.map((signal) => ( {signal} ))}
{panel.guardrail}
))}

Downloadable reports

Every result becomes a decision brief and a reproducible technical evidence package.

The six-section PDF begins with a victim-friendly summary, then exposes calibration, expert votes, layer evidence, the regional map, file facts, limitations, and responsible next steps.

{reportFeatures.map((feature) => { const FeatureIcon = feature.icon; return (

{feature.title}

{feature.detail}

    {feature.includes.map((item) => (
  • {item}
  • ))}
); })}
Generate a report

Public deployment posture

Designed for sensitive evidence handling, with a clear path from public beta to multi-worker scale.

The current public runtime enforces privacy, limits, health checks, and deletion. PostgreSQL and Redis/RQ are supported for a scaled deployment, but the interface never claims that pixels alone prove authenticity.

{deploymentControls.map((control) => (

{control.title}

{control.detail}

    {control.checks.map((check) => (
  • {check}
  • ))}
))}
); } function MoEFlowDiagram() { return (
01

Preprocess

Validate, normalize color, measure quality risk, compute hashes, and prepare independent evidence inputs.

02

Multi-view Gate

Create original, 92% center-crop, JPEG-85, mirrored, and social-resize views for the broad primary stability check.

03

Expert Panel

One broad primary, two counter-models, and independent forensic and provenance experts.

{moeExperts.map((expert, index) => { const ExpertIcon = expert.icon; return (

{expert.title}

{expert.detail}

{expert.output}
); })}
04

Stance Calibrator

Apply per-model AI/real thresholds. Scores in between become abstentions, then reliability weights are applied.

05

Safety Arbiter

Require provenance, independent support, unanimous strong votes, or primary-anchored alignment before a strong AI claim.

likely real likely AI generated likely manipulated inconclusive
{moeRules.map((rule) => (
{rule}
))}
); } function ArchitectureFlowchart() { const laneGroups = [ ['Provenance lane', [ ['Metadata', 'EXIF/XMP, software markers, GPS redaction', Database], ['C2PA', 'Content credentials and signed generation claims', BadgeCheck], ]], ['Visual-model lane', [ ['Broad Primary', 'Community Forensics across five stable transforms', Cpu], ['Counter-Models', 'Ateeqq plus an independent distilled classifier', BrainCircuit], ]], ['Forensic lane', [ ['Pixel Residuals', 'ELA, noise, luminance, chroma, edge, frequency', Layers3], ['Regional Map', 'A 4x4 anomaly grid with explicit limitations', Gauge], ]], ]; const flowSteps = [ ['01', 'Private Intake', 'Consent, type and pixel limits, SSRF defense, encryption, and short media TTL.', Lock], ['02', 'Evidence Bus', 'Fork the validated packet into provenance, model, and forensic lanes.', Workflow], ['03', 'Expert Routing', 'Run only the evidence methods that are available and appropriate for this file.', BrainCircuit], ['04', 'Safety Arbiter', 'Calibrate stances, record disagreement, cap scores, or abstain.', ShieldAlert], ['05', 'Evidence Report', 'Return a plain-language verdict plus PDF and JSON technical ledgers.', FileText], ]; return (
Evidence packet journey

Validation, parallel expert analysis, calibrated convergence, report

{flowSteps.map(([index, title, detail, icon], stepIndex) => { const StepIcon = icon; return (
{index}

{title}

{detail}

{stepIndex === 0 ? 'validated' : stepIndex === 1 ? 'forked' : stepIndex === 2 ? 'analyzed' : stepIndex === 3 ? 'calibrated' : 'documented'}
); })}
Parallel evidence fan-out Independent signals reconverge at the arbiter
{laneGroups.map(([lane, items], laneIndex) => (
{lane} independent
{items.map(([title, detail, icon], itemIndex) => { const LayerIcon = icon; return (
{title} {detail}
); })}
))}
Safe outcome set likely real likely AI generated likely manipulated inconclusive
); } function ReportPreviewScene() { return ( ); } function ArchitectureScene() { const nodes = [ ['Primary', '5-view stability', Cpu], ['Counters', '2 independent votes', BrainCircuit], ['Provenance', 'EXIF/XMP + C2PA', Database], ['Forensics', '11 evidence layers', Layers3], ['Arbiter', 'Abstention gate', Gauge], ['Report', 'PDF + JSON ledger', FileText], ]; return (