One index · news · forums · video

Cited evidence, not a confident paragraph.

Ask it what the internet says about something. It returns the evidence — with citations that were checked, the places sources disagree, and an explicit account of what it could not establish. Every sentence is re-derived from the rows it cites before it is allowed out. A sentence that fails is not emitted.

61,205 fragments 10,854 items 196 sources 871 outlets 0 model calls in the read path
 tesseract answer — real output, unedited
Q: what are the sources saying about Nielsen acquiring DoubleVerify matched: doubleverify nielsen Findings — every line re-derived from its citations 4 indexed fragments from 2 items, 2 outlets and 1 source types match "doubleverify nielsen". [163259 172585 163260 172586] The most recent matching item is 16.9 hours old. [163259 172585 …] 2 outlets published accounts of one event ("acquire billion doubleverify nielsen ad-tech") within 2.5 hours of each other. [163259 172585] The sources disagree on "doubleverify": deadline.com says $2,500,000,000; variety.com says $2,150,000,000. [163259 172585] deadline.com: "Nielsen Set To Acquire Ad-Tech Firm DoubleVerify For $2.5 Billion" [163259] variety.com: "Nielsen to Acquire DoubleVerify for $2.15 Billion in Bid to Augment Digital Measurement" [172585] deadline.com: "Nielsen is paying $2.15 billion to acquire DoubleVerify… The all-cash acquisition will deliver shareholders $13.60 per share." [163260] Could not establish TikTok: could not be asked — no topic-search surface exists; signature forgery is refused on purpose Reddit: not configured — zero results here means it was never queried 12 registered sources are robots-refused and were never fetched 48 fragments are in scripts this tokenizer cannot segment (CJK) — indexed and unreachable discourse: 377 fragments indexed, 0 matched — this one is a fact about the data

Deadline's headline says $2.5 billion. Deadline's own body, two lines later in the same answer, says $2.15 billion. A summariser reconciling forty accounts would have produced $2.3 billion — a figure nobody published and no reader could trace.

Three things a chatbot cannot do.

Not because it is worse at language. Because it has no row to point at.

Refuse to say it

Each sentence carries the fragment ids it was computed from, and the assembler re-derives the claim from exactly those rows. Strip the citations and it emits nothing. Staple a fake id to a true sentence and it is refused. The gate is in the function, not in a prompt.

Report the disagreement

Where two outlets attach different figures to the same thing, that is the finding — quoted verbatim, both sides, with their outlets. Averaging them invents a number. A figure that is merely qualified — "up to", "per share" — is never reported as a conflict.

Say what it could not reach

"Nobody discusses this" and "we were never able to ask" are different answers. Every response names which sources are structurally unaskable, which robots.txt forbids, which are stale, and which are genuinely silent.

The economics are the architecture.

A wrapper buys a model call for every answer, so its gross margin falls as usage rises — the one cost curve you cannot outrun. No model runs inside this. It assembles evidence; your Claude writes the prose, over MCP, on your subscription. An acceptance test greps the entire read path and finds zero model calls.

$0

Platform inference spend. Verified by a test, not asserted in a README.

$0

Perception cost. ffmpeg and OCR run on the customer's machine.

0 bytes

Media retained. Enforced on every exit path — an index, never an archive.

Measured, not asserted.

Fourteen assumptions were written down as settled and then contradicted by measurement. These are the ones that changed the design.

What was believedWhat was measured
SimHash finds "the same story in forty outlets"Same-event and unrelated pairs have the same distance distribution — median 31 vs 32. Outlets don't republish a body; they each write one.
A published feed is a permission12 of 157 feeds are robots-Disallowed. arXiv's export host blocks every crawler.
Fetching an article costs ~2 s11.2 s per stored article — the cost is the failures (0.66 s success, 13.4 s timeout, more failures than successes).
Storage is 481 bytes/fragmentThat was synthetic. Real text is 1,559 — so 100M fragments is ~156 GB, not 48.
The group-by is a wall at 10M rows4,820 ms → 0.01 ms with a maintained rollup. Ranked queries halved, 2,444 → 1,139 ms.
The rate limiter was fixedA 16-second event-loop stall fired every overdue timer at once: two requests 1 ms apart on a host limited to one per two seconds.

26 acceptance tests, a 10M-fragment scale run, and a 30-minute continuous run that is SIGKILLed at the halfway point and has to resume. They do real network work on purpose: the failure mode this has is not "a function returned the wrong value", it is "acquisition quietly returned less and nothing threw", and a mock cannot catch that.

Runs from a clone.

Node 22+. No npm dependencies — not for the model calls, not for the MCP server. SQLite via node:sqlite.

# seed the registry and start polling
node cli.mjs registry --seed --sync
node cli.mjs poll --forever

# ask it something
node cli.mjs answer "what is happening with Iran and munitions"

# or hand it to Claude over MCP — 18 tools, no SDK
{ "mcpServers": { "tesseract": { "command": "node", "args": ["mcp.mjs"] } } }

Logged-out only. Public surfaces only. robots.txt parsed and honoured with correct longest-match precedence. Content-Signal: ai-input=no withholds a body while keeping the citation and the link. No paywall circumvention, no signature forgery, no screen capture — absent by choice, not by effort.