Methodology annex · Report 01
A measurement is only worth as much as the account of how it was taken. This is the full method for Content provenance on the open web, including the two errors we made and corrected before publishing.
Tranco top-1M, a research-oriented domain ranking that aggregates several sources and is designed to resist the manipulation affecting commercial traffic lists. We use a published frame rather than a hand-picked list specifically so the sample is not ours to choose.
Four rank bands, 700 domains drawn from each:
| Band | Ranks | Drawn |
|---|---|---|
top1k | 1 – 1,000 | 700 |
top10k | 1,001 – 10,000 | 700 |
top100k | 10,001 – 100,000 | 700 |
tail | 100,001 – 1,000,000 | 700 |
Selection within a band uses a fixed stride, not a random draw
(stride = floor(pool / 700), taking every nth eligible domain). This is
deterministic: re-running against the same Tranco snapshot selects the same domains. There is
no seed to disclose because there is no randomness.
Domains matching a published pattern list are removed before sampling: DNS and CDN infrastructure, certificate and OCSP endpoints, telemetry and analytics collectors, and platform asset hosts. These rank highly in a DNS-derived list but are not websites. The exclusion is stated because it is a judgement call that affects the denominator.
https://<domain>/ in headless Chromium (Playwright), 1366×900, en-GB, Europe/London, 20 s navigation timeout.img[src], currentSrc, data-src, every candidate in srcset, every source[srcset], plus og:image.data: URIs, cap at 30 per domain.caBX chunk and the XMP packet all appear before the
image data. Fetching 128 KB instead of whole files reduces a million-image crawl from
tens of terabytes to roughly 60–120 GB, and reduces load on the sites measured. Images
are disabled in the renderer for the same reason: URLs are read from the DOM, and only the
ranged fetch downloads bytes.
Two stages. The first is cheap and deliberately permissive; the second is authoritative.
render page → collect image URLs → ranged fetch
→ stage 1: byte scan (permissive — must never miss)
→ stage 2: c2patool (authoritative — removes false hits)
Stage 1 scans the bytes for:
jumb, c2pa);caBX chunk;C2PA chunk;dcterms:provenance pointer, where the file contains no C2PA bytes at all, only a URL to where the manifest is hosted;digitalSourceType values indicating generative origin, counted separately.
Stage 2 passes every stage-1 hit to c2patool 0.27.7, the C2PA
reference implementation. Only confirmed manifests are counted. Stage 1 over-firing costs one
tool invocation; stage 1 under-firing loses a manifest permanently and invisibly, so
the threshold is set accordingly.
The instrument was not permitted to measure anything until it had been shown to detect what it was looking for. A detector that finds nothing produces the same output as a web that contains nothing, and the two are indistinguishable without a gate.
| Check | Result |
|---|---|
| Ground truth | c2patool 0.27.7 |
| Recall on known positives | 11 / 11 — 100% |
| Formats verified | JPEG, PNG, WebP |
| Delivery modes verified | embedded JUMBF and remote manifest |
| False positives at stage 1 | 2 — both removed by stage 2 |
| Confirmed positive on the live web | yes |
Every failure is recorded with a cause derived from the actual Chromium error code, not a catch-all. Causes are reported by rank band so that bias can be assessed rather than asserted. A domain we could not reach is never counted as a domain without provenance.
Categories: DNS name resolution failure · connection refused or reset · TLS or certificate error · HTTP/2 or QUIC protocol error · timeout · bot wall (403/429/503) · JavaScript challenge page · reachable but no qualifying images · other HTTP error.
Rates are reported with a Wilson score interval at 95%, which behaves correctly for very small proportions where the normal approximation does not. We report the interval, and where a single number is needed we use the upper bound rather than the point estimate.
We also publish an explicit worst-case bound: the rate that would obtain if every unmeasured domain carried provenance on every image. It is an absurd assumption by construction. It is published because it is the first objection a reviewer should raise.
Both of the following were caught before publication. They are recorded here because a methodology that lists no mistakes is not describing a real process.
An earlier draft reported "100% recall" as evidence that the finding was trustworthy. That figure describes the detector's performance on files already in hand. It says nothing about the share of the population that could be reached. The two are unrelated, and conflating them would have overstated the study's authority. The published version reports coverage and recall separately, and reports coverage by band.
An earlier version classified any navigation error that was not a timeout or a certificate
problem as "DNS failure", producing a 22.8% bucket that hid connection resets, protocol
errors and possibly silent blocking. It was replaced with classification on the actual
Chromium error code, retaining the raw string. The corrected data showed the bucket was in
fact dominated by genuine ERR_NAME_NOT_RESOLVED — but that was a finding, not an
assumption, and it was only available once the taxonomy was real.
If you are named in a study and believe something is wrong, write to corrections@netcensus.org. We re-run the measurement and publish the outcome either way, with the date. Corrections are free, and we do not require anything in return.