Why I built a multilingual, evidence-first fact-checking tool — and how you can run it yourself.
Category: Code / Open Source / AI
Project: FACT — Public Evidence Verification Engine
GitHub: https://github.com/alesmcgregor/fact
Live project: https://dr.ales.work
It Started With a Simple Question
Have you ever watched a video online and thought, "Wait, is that actually true?"
I have. Quite a few times.
You open YouTube, scroll through X, or come across a post on social media. Someone makes a pretty confident statement. Maybe it's about politics, history, an international conflict, or something that supposedly happened yesterday.
The presentation looks convincing. There's dramatic music, carefully selected footage, a few screenshots, and someone explaining everything with absolute confidence.
And sometimes there's that familiar phrase:
"According to official sources..."
Okay. Which official sources?
Where's the original document? Who published it? When was it published? And does it actually say what the person in the video claims?
That's where things start getting interesting.
We're surrounded by information, but that doesn't necessarily mean we're surrounded by evidence.
Some content creators genuinely try to inform people. Others may unintentionally repeat inaccurate information. And some content is clearly designed around a narrative rather than an attempt to establish what actually happened.
I'm not interested in naming channels or pointing fingers. That's not why I started this project.
What bothers me is how easily an unverified claim can become an accepted fact simply because enough people repeat it.
And I kept coming back to the same thought:
Why not build something that helps us check the evidence instead of blindly trusting the presentation?
That's how FACT started.
I'm Not Trying to Build a Truth Machine
Let's get something out of the way.
I don't believe an AI model should have the final word on what's true.
And I certainly don't want software deciding whether someone is a liar, a propagandist, or a bad person.
That's a dangerous direction.
What I wanted was something simpler, but also more useful.
Imagine watching a video where someone makes five factual claims.
Instead of arguing in the comments, what if you could take the video's URL, analyze the available transcript, extract those claims, and compare them against identifiable sources?
Not just random websites that happen to agree.
Government publications, official statements, institutional records, and independent sources that can be traced back to their publishers.
And if two official sources contradict each other?
Well, that matters too.
The system shouldn't quietly pick a favorite. It should show that the evidence conflicts.
There's another important detail.
Not finding evidence doesn't automatically mean something is false.
Sometimes the source isn't publicly available. Sometimes a video has no accessible transcript. Sometimes a claim simply hasn't been independently verified yet.
I wanted FACT to acknowledge those limitations instead of pretending to know everything.
Because uncertainty is not a software bug. Sometimes it's the most accurate answer available.
So, What Exactly Is FACT?
FACT stands for Public Evidence Verification Engine.
It's a web application I developed using Laravel, PHP, OpenAI's Responses API, and a collection of services for retrieving and analyzing publicly accessible content.
The idea is straightforward.
You provide a URL. FACT attempts to retrieve the available content, identifies factual claims, researches relevant sources, evaluates the evidence, and produces a report you can inspect and share.
The project currently supports six interface languages:
English
Turkish
Azerbaijani
Russian
Spanish
Arabic
Arabic also has right-to-left interface support.
I wanted the tool to be useful beyond a single language or country. Misinformation doesn't respect borders, and neither should the tools we use to examine it.
What Can It Analyze?
FACT is designed to work with publicly accessible articles and content from platforms including YouTube, X, Instagram, Facebook, and TikTok.
For social platforms, it can use Supadata to retrieve available metadata and transcripts. YouTube transcript retrieval also has a dedicated implementation.
There's a limitation here, though.
Not every video exposes a transcript. Not every platform permits automated retrieval. Sometimes providers restrict access.
In those cases, FACT is designed to report the limitation rather than invent missing dialogue.
It can also accept a user-supplied transcript, which is useful when automated retrieval isn't possible. Naturally, an externally supplied transcript cannot be treated as independently authenticated.
And one thing FACT does not currently do is independently verify video frames, detect deepfakes, or authenticate someone's voice.
This distinction is important.
Analyzing what someone said and proving that a recording is genuine are two entirely different problems.
How the Verification Process Works
Let's walk through the basic workflow.
1. You submit a URL.
FACT first checks whether the address is valid and publicly accessible. It also applies security rules to prevent the application from accessing internal network resources.
2. It extracts the available content.
For a news article, that might be the readable text. For a supported video, it might be a transcript and associated metadata.
3. It identifies checkable claims.
Not every sentence needs verification.
An opinion such as "I think this policy is terrible" is different from a factual statement such as "This policy was approved on September 15."
The second statement can potentially be verified against records.
4. It researches the evidence.
The OpenAI-based analysis provider uses web search to investigate claims and collect source references.
FACT maintains a source registry that initially includes official institutions from Türkiye and Azerbaijan, alongside configured international domains and independent publishers.
5. It evaluates the evidence.
Here's something I deliberately wanted to keep separate from the AI's interpretation.
FACT has a deterministic scoring component.
Instead of simply asking an AI model, "How confident are you?", the application evaluates evidence relationships and source characteristics through explicit rules.
It considers things like official support, independent support, publisher diversity, dated evidence, and contradictory sources.
But a score of 85 doesn't mean there's an 85% probability that the claim is true.
It's an evidence index, not a scientifically calibrated probability.
6. It generates a report.
The resulting report contains the available claims, their classifications, source references, evidence relationships, and known limitations.
You can open it through a shareable public URL.
That's the whole idea: make the reasoning inspectable.
Not magic. Not absolute truth. Just a more structured way to examine factual claims.
Why Official Sources Matter — and Why They Aren't Enough
You might be wondering:
"Why prioritize official sources? Aren't official institutions capable of making misleading statements too?"
Of course they are.
An official source can establish that a government, ministry, or institution made a particular statement.
That doesn't automatically prove every part of the statement is true.
This is an important distinction, especially when examining political or international claims.
A government announcement might be the correct primary source for verifying whether a policy was officially adopted.
But when investigating whether that policy achieved its stated objectives, independent research and additional evidence may be necessary.
That's why FACT isn't designed to rely exclusively on one category of source.
It distinguishes between different evidence types, checks their relationships to individual claims, and can identify situations where official accounts conflict.
The current scoring method is still something I want to improve.
For example, five different websites repeating the same wire-service article aren't necessarily five independent confirmations.
Future improvements should make those relationships clearer.
A good verification system must be willing to question its own methodology.
Otherwise, we risk replacing one source of false confidence with another.
Let's Look Under the Hood
Now for the technical side.
I built FACT with a relatively familiar stack:
Backend: PHP 8.3+ and Laravel 13
Frontend: Blade, Tailwind CSS 4, Vite, and lightweight JavaScript
Database: SQLite, MySQL, or PostgreSQL
Background processing: Laravel queues and scheduler
AI research: OpenAI Responses API with web search
Social content: Optional Supadata integration
I kept the architecture modular.
There are separate components for content retrieval, AI analysis, evidence scoring, and report generation.
This matters because an AI provider shouldn't be permanently fused with the rest of the application.
The analysis provider follows a contract. That makes it possible to develop additional implementations without rebuilding the entire verification workflow.
There are also dedicated services for safe URL handling, content extraction, YouTube transcripts, and social metadata.
And because users can submit URLs, security was part of the design from the beginning.
FACT includes checks against private-network access, DNS-related request attacks, unsafe redirects, unsupported ports, and oversized responses.
No, that doesn't make the application invulnerable.
It does mean those risks were treated as engineering requirements, not something to think about after deployment.
Tutorial: Running FACT on Your Own Computer
All right, enough theory.
If you're a developer and want to explore how FACT works, here's how to get started.
The source code is publicly available here:
https://github.com/alesmcgregor/fact
Step 1 — Install the Requirements
You'll need:
PHP 8.3 or newer
Composer 2
Node.js 20.19+ or 22.12+
npm
SQLite, or another database supported by Laravel
Git
Make sure PHP has the required extensions, including curl, DOM, mbstring, OpenSSL, and an appropriate PDO database driver. The intl extension is recommended.
You can check your installed versions using:
php -v
composer --version
node -v
npm -v
git --versionStep 2 — Clone the Repository
Open your terminal and run:
git clone https://github.com/alesmcgregor/fact.git
cd factInstall the PHP dependencies:
composer installThen install the frontend dependencies:
npm ciStep 3 — Configure Laravel
Create your local environment configuration.
On Linux or macOS:
cp .env.example .envOn Windows PowerShell:
Copy-Item .env.example .envNow generate the Laravel application key:
php artisan key:generateFor a quick local setup, SQLite is the easiest option.
Create an empty database file if one doesn't exist:
php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"Then run the migrations and seed the initial data:
php artisan migrate --seedThe database seeder includes a starting collection of official sources from Türkiye and Azerbaijan.
You can expand this registry as the project develops.
Step 4 — Build the Frontend
Run:
npm run buildThis prepares the frontend assets for Laravel.
Step 5 — Start the Application
Open your first terminal:
php artisan serveLaravel will display a local URL, normally:
Now open a second terminal in the same project directory:
php artisan queue:work database --sleep=3 --tries=1 --timeout=240Why two terminals?
Because FACT performs its analysis in background jobs. The web application accepts the request, while the queue worker handles the processing.
The browser then checks the analysis status as it progresses.
Step 6 — Try the Demo First
Here's something useful.
By default, FACT uses mock providers.
Your .env configuration should include:
FACT_ANALYSIS_DRIVER=mock
FACT_CONTENT_DRIVER=mockThis allows you to explore the application's demonstration workflow without consuming paid AI API credits.
Just remember that mock mode does not perform real fact-checking.
It is a local preview, not a substitute for live research.
Step 7 — Enable Real AI Research
If you want FACT to perform actual content retrieval and research, configure the providers in .env:
FACT_ANALYSIS_DRIVER=openai
FACT_CONTENT_DRIVER=safe
OPENAI_API_KEY=your_api_key
OPENAI_MODEL=gpt-6-luna
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_TIMEOUT=90The repository currently uses gpt-6-luna as its configured model name. Make sure the selected Responses API model is available to your account; otherwise, change the setting to a supported model.
You can also configure the optional social content provider:
SUPADATA_API_KEY=your_supadata_keyKeep these credentials on the server.
Never place them inside JavaScript exposed to the browser, publish them on GitHub, or paste production secrets into an AI coding assistant.
After updating the configuration, run:
php artisan config:clear
php artisan queue:restartStart the worker again if necessary.
Now you can submit supported public URLs for real analysis.
Keep in mind that API-based research and transcript retrieval can incur charges.
Step 8 — Run the Tests
Before modifying the code, it's worth checking that the existing application behaves as expected.
Run:
php artisan testFor code-style checks:
vendor/bin/pint --testAnd verify that the frontend builds:
npm run buildThe repository contains tests covering several important areas, including URL security boundaries, content extraction, scoring, localization, queue workflows, and report rendering.
These commands verify your local environment. They don't automatically prove that your production deployment is secure.
Step 9 — Deploying FACT to a Server
For production, there's a little more work to do.
Start by setting your environment appropriately:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.example
SESSION_SECURE_COOKIE=true
DB_QUEUE_RETRY_AFTER=300Your web server should serve Laravel's public directory — not the application root.
Use HTTPS, protect the environment configuration, and store API credentials securely.
You also need a persistent queue worker managed by a service supervisor such as systemd or Supervisor.
For example, the worker command is:
php artisan queue:work database --sleep=3 --tries=1 --timeout=240And Laravel's scheduler should run every minute:
php artisan schedule:runIn production, arrange for that scheduling command to be invoked automatically by cron or another supported scheduler.
Also consider outbound network restrictions for the content-retrieval worker, application logs, backups, API spending limits, and dependency security scans.
The repository README documents additional deployment requirements.
One last thing: read the SECURITY.md file before exposing your own installation to the public internet.
What About the API?
If you're building your own frontend or planning an integration, FACT also exposes a small JSON-based interface.
The main routes are:
Method | Endpoint | Purpose |
|---|---|---|
POST |
| Submit a URL for analysis |
GET |
| Check analysis progress and results |
GET |
| Open the public report |
The submission endpoint accepts a URL, locale, and optional analysis preferences.
Browser requests are protected by Laravel's CSRF middleware, so calling the POST endpoint from another client requires handling the applicable session and CSRF requirements.
Analysis status is stored in the database, and reports use non-sequential public identifiers.
By default, completed reports have a configurable retention period of 90 days.
These decisions keep the interface relatively simple while leaving room for future integrations.
Is FACT Finished?
Not really.
And that's fine.
The current implementation provides a working foundation, but there are plenty of things worth improving.
I'd particularly like to see more sophisticated source independence analysis, stronger historical context, expanded official-source registries, and additional safeguards against AI-generated misinterpretations.
There are also interesting directions involving manipulated-media detection, although that would require a different set of tools and verification methods.
For now, the main focus is on factual claims and traceable evidence.
I would rather have a system that sometimes says "I don't have enough evidence" than one that confidently manufactures an answer.
Why I Made the Code Public
I don't think tools like this should be mysterious black boxes.
If a system claims to evaluate evidence, developers and researchers should be able to examine how it works.
That's one reason I published the FACT source code on GitHub.
Journalists, researchers, developers, and curious readers can explore the implementation, learn from it, and examine its decisions.
For anyone planning to modify or redistribute the software, there's an important licensing detail: the repository's package metadata currently identifies MIT, but a corresponding LICENSE file still needs to be added to make the reuse permissions explicit.
The long-term idea is to make the project something people can improve and adapt for different research needs.
I also think that's how tools like this become more useful: through review, criticism, testing, and better ideas.
You don't have to agree with the algorithm.
In fact, questioning it is part of the point.
A Small Shortcut if You Use an AI Coding Agent
If you work with an AI coding assistant, you don't necessarily need to perform every installation step manually.
You can give your agent this article and the GitHub repository, then ask it to follow the documentation and prepare a local development environment.
Here's a prompt you can adapt:
I want to install and explore FACT, a Laravel-based
public evidence verification project.
Repository:
https://github.com/alesmcgregor/fact
Tutorial:
[PASTE THIS ARTICLE URL HERE]
Read the article and the repository README first.
Inspect the existing code and verify the system
requirements before changing anything.
Set up a working local environment, install the
dependencies, prepare the database, build the
frontend, and start the application and queue worker.
Use mock mode for the initial setup.
Do not activate paid API providers without approval.
Preserve the existing security controls.
Never expose API keys or secrets.
Run the available tests, report any failures,
and explain what was configured or changed.
Do not rewrite working components unnecessarily.That's it.
The agent has the documentation, the repository, and a clear objective.
You still need to review what it changes, particularly before deploying to a public server.
One Last Thought
I started FACT because I got tired of seeing claims presented as facts without anyone bothering to show where the information came from.
But while building it, I realized something.
The interesting problem isn't just misinformation.
It's how we decide what deserves our trust.
A polished video isn't evidence.
A confident speaker isn't evidence.
And an AI-generated explanation isn't evidence either.
They might point us toward something worth investigating. But the investigation still matters.
That's what I wanted FACT to help with.
Not to tell people what to think.
Just to make it a little easier to ask a better question:
"Okay, but what does the evidence actually say?"
And sometimes, that question is more valuable than a quick answer.
Explore the project
GitHub: https://github.com/alesmcgregor/fact
Live application: https://dr.ales.work
The project is under active development. Check the repository for current installation instructions, supported integrations, and known limitations.
← CODE