Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

TruthLens

A browser extension that surfaces provenance, counter-evidence, and rhetorical framing for the text and images on the page you're reading.

Status: early. It does not analyse anything yet.

This is Phase 1 of 7. The extension builds, loads, captures a real text selection from a real page, and renders three panels — and those panels currently show sample data from a fixture, not an analysis. The panel says so, in the panel, above the results.

What is real today: the result schema, the text normaliser and anchoring, the codegen and its drift checks, the permission model, and the CI. What is not: every detector, every retriever, every model. See Status.

It is open source now because the design decisions are the part worth arguing about before the code exists, not after. If you think the evidence-state rules are wrong, there is a document to disagree with.

What this tool will not tell you

  • Whether something is true. No panel produces a verdict. There is no truth score, no credibility rating, no aggregate number, and no code path that reads two panels at once.
  • Whether text was written by AI. There is no authorship classifier in this project and there will not be one. Peer-reviewed evaluation of 14 detection tools found none reaching 80% accuracy. A Stanford study found seven common detectors misclassified 61% of TOEFL essays by non-native English writers as AI-generated while performing near-perfectly on native-speaker text — unanimously, on roughly 20% of them. OpenAI withdrew its own classifier. Perplexity-based methods are structurally biased against anyone who writes in standard syntax, which includes second-language writers, many neurodivergent writers, and anyone edited by a copy desk.
  • Whether an author is biased. There is no political axis in this tool. Nothing is placed on a left–right spectrum and no ideology is attributed to anyone.
  • Whether an image is real. Provenance is signed history, not truth. A perfectly credentialed photograph can be captioned to mean the opposite of what it shows.

Policy, stated here because it belongs above the fold: any statistical text signal that exceeds a 5% false-positive rate on our non-native-English evaluation subset does not ship enabled by default. This is release-blocking, enforced in code rather than in a promise, and the measured figure is displayed next to the signal in the UI.

What it does instead

Three panels, always in this order, each loading independently and each able to fail on its own without the others noticing.

Provenance — where did this come from? C2PA Content Credentials with both the validity and the trust axis reported separately, because a manifest can be cryptographically valid and signed by a key nobody vouches for. EXIF, with GPS behind a deliberate click because the subject of a photograph did not consent to having their location read out. Perceptual hashing against your own local history, so you can see that you met this image before. Publisher domain age, ownership chain, first archive snapshot, and stealth-edit detection against the Wayback Machine. Reverse-image search as one-click deep links that upload nothing until you click.

Evidence — what do other sources say? Claims are extracted, decontextualised, and decomposed into verification questions, and then every claim is searched four ways: for the claim, against the claim, for the neutral underlying question, and against primary sources where the claim type suggests one. Naive retrieval returns documents that share the claim's framing; searching against it is the single highest-leverage decision in this panel. Results are deduplicated by URL, by near-duplicate content, and by owning entity — twelve outlets owned by one group is one source. Stance comes from an NLI model, never from a language model's opinion. Every quote is verified as a literal substring of the document we fetched.

Framing — how is this written? Hedging and boosting, loaded language named with the SemEval-2020 Task 11 taxonomy, unnamed attribution, agency deletion, vague quantifiers, and how the text describes each named entity — each with the exact spans that triggered it, and none of them producing a score for the document. Runs entirely on your device with no network and no API key.

Every component that failed or was skipped is listed in the result and rendered in the panel. Silent partial results are how tools in this genre mislead people.

Status

Phase What lands State
0 — Design Schema, architecture, threat model, ADRs Complete
1 — Skeleton Monorepo, codegen, capture, panel shell, CI Complete. You are here.
2 — Framing The eight detectors, on-device, no network Not started
3 — Evidence Retrieval, stance, quote verification Not started
4 — Provenance C2PA, EXIF, pHash, publisher records Not started
5 — Adversarial Sanitisation and the injection canary suite Not started
6 — Calibration The evaluation harness and reliability diagrams Not started
7 — Release Signed builds, store listings Not started

No confidence number appears anywhere in the interface until Phase 6. The fields exist in the schema because the pipeline needs them and exports must be auditable, but nothing is rendered as a number until it has a reliability diagram and an expected-calibration-error figure behind it. Where calibration error exceeds threshold, the interface shows a qualitative band permanently — not as a temporary state pending a fix.

Try it

There is no store listing, and there should not be one until the tool does something.

git clone https://github.com/VishnuR23/TruthLens
cd TruthLens
pnpm install     # Node 22+, pnpm 11+. No API keys needed, now or later, for Panel C.
pnpm dev

Then load apps/extension/dist as an unpacked extension at chrome://extensions with Developer mode on. Select text on any page and press Cmd/Ctrl+Shift+K, or right-click and choose Analyze selection.

The selection you make is captured, normalised, hashed, and anchored for real. Everything below it in the panel is fixture data and is labelled as such.

To run everything CI runs:

pnpm verify                                  # codegen drift, lint, format, types, tests, build, budgets
cd apps/api && uv sync --all-groups && uv run pytest
docker compose up                            # the optional self-hosted proxy

How it is put together

apps/extension     MV3 — TypeScript, React, Vite, Tailwind
apps/api           Optional self-hosted proxy. Never required, never hosted by us.
packages/core      The analysis pipeline. No DOM, no Node, no fetch.
packages/shared    The JSON Schema, and the Zod and Pydantic generated from it
docs/              Architecture, threat model, evidence-state rules

Two deployment modes. Mode A is the default and what a store build would be: everything runs on your device, and any language-model call goes directly from your browser to a provider you chose with a key you supplied, or to a local Ollama. Mode B is an optional proxy you host yourself, for a team that wants a shared cache and provider keys off individual machines. We do not run a hosted instance of it and the compose file collects nothing.

packages/core does not know which mode it is in. Every dependency it receives is nullable, because a user with no key, no models, and no network is a supported configuration rather than an error path.

The extension requests no host permissions. It reads a page only after you ask it to, through activeTab, and the capture script is injected at that moment rather than declared against every site you visit.

Design commitments

These are not preferences. Violating one is a P0 bug, and several are enforced structurally rather than by discipline.

  • No aggregate score. Not a truth score, credibility score, trust rating, or verdict badge. The three panels are never combined.
  • No AI-detection percentage for text. See above. The stylometric module describes measurable properties of prose and makes no authorship claim in any string it produces.
  • No fabricated citations. Every source was fetched during that request, has a resolvable URL, and carries a quote verified as an exact substring of the fetched document. Enforced by a runtime assertion, not by asking a model nicely. A source whose quote fails verification is dropped and the drop is reported.
  • No face search, ever. Not PimEyes, FaceCheck.ID, Clearview, or anything like them, and we will not build one. A tool that identifies strangers from photographs is a stalking instrument regardless of intent. This is a permanent non-goal, not a backlog item.
  • No author identification either. Stylometric deanonymisation is the same instrument aimed at writers. Also permanent.
  • No paywall circumvention, no robots.txt violation, no scraping past rate limits. Paywalled sources are disclosed and linked.
  • No political-axis labelling.
  • No telemetry. No analytics, no error reporting, no anonymous usage statistics. Absent, not opt-out.
  • No silent network calls. Nothing leaves your device without an action you took, and the options page enumerates which provider receives which data before the first request.

Reading order for reviewers

Document What it settles
analysis.schema.json The shape of every result, and why each field exists
docs/EVIDENCE-STATES.md How a claim's state is decided. Seven rules, no model. Disagree here.
docs/THREAT-MODEL.md Four adversaries, starting with the person using this tool
docs/ARCHITECTURE.md Packages, processes, budgets, provider boundary
LIMITATIONS.md What it gets wrong, and how
DECISIONS.md Every significant choice, including the ones with a real argument against
DEVIATIONS.md Where the implementation departs from its own specification, and why

Licensing

The code is Apache-2.0. Two things that licence does not cover, stated here so it does not imply more than it grants:

  • Evaluation datasets are fetched by script, never vendored. AVeriTeC is CC-BY-NC-4.0. A commercial fork of TruthLens cannot redistribute our evaluation data, though it can fetch it under the dataset's own terms.
  • The reference corpus ships as derived statistics, not text. Its sources (Wikinews, Wikipedia) are copyleft, so we publish the percentile tables, the derivation script, and a document-ID manifest rather than the corpus itself.

Lexicons are audited before use and each carries a PROVENANCE.md; CI fails without one. MPQA (GPL), NRC EmoLex and VAD (non-commercial), and LIWC (proprietary) are never bundled.

Contributing

Read CONTRIBUTING.md first, particularly the permanent non-goals. Disagreement about the design is the most useful contribution at this stage — an argument against a rule in docs/EVIDENCE-STATES.md is worth more right now than a pull request.

Security issues: SECURITY.md. Please do not open a public issue for a vulnerability.

About

Extension that validates readings and images for AI generated content, scores bias, provides sources.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages