Methodology

How ce.rodeo scores an image, ranks a publisher, dates a build, and decides what to index. The grades page has the per-signal rubric; this page is the governance surface: what we commit to, how we change it, and the dated history of every change.

Image dating

Freshness ("rebuilt in the last 90 days") used to read config.Created from the image manifest, which reproducible-build publishers set to their source commit date. That penalised reproducibility. From 2026-10-21, freshness reads first_seen_at_cerodeo — the first time our scanner observed the image digest. That date is monotonic-forward: nothing a publisher does can push it later. For images that pre-date the column, we back-filled with coalesce(last_pushed_at, created_at).

Severity source

Grype's native severity varies by data source — Ubuntu Security Tracker for Ubuntu packages, NVD for much of Debian's unfixed tail, GHSA for language ecosystems. That drift was a defensible choice at launch; it isn't anymore. For CVE severity, ce.rodeo now rebuckets every match by its CVSS v3 base score — Critical ≥ 9.0, High ≥ 7.0, Medium ≥ 4.0, Low < 4.0 — using NVD's own thresholds. When a match has no CVSS v3 record, we fall back to grype's per-source rating; treating "no NVD rating" as a filter would hide real exposure. The downloadable grype JSON on every image page carries the full raw matches so you can rebucket differently.

Publisher rank: v2 (current) and v3 (preview)

Rank v2 grades each (publisher, project) product by the best 48h-grace scoring_grade across every variant in the 60-day push window. Rank = (3·A + B − 0.5·F) ÷ products_60d × ln(1 + products_60d). Shipped 2026-10-19.

Rank v3 (preview, switches 2026-11-04) grades each product by the newest-pushed variant in the 60-day window — one image, no best-of-window hiding. Ordering key is per-product quality with a Wilson-style small-sample correction (lower bound of a 95% confidence interval), so a publisher with 1 clean product can't tie one with 50. Breadth becomes a separate column: "products_60d", not a multiplier.

Both rankings are live in the API during the dual-run. Pass ?variant=v3 to /v1/publishers or open /purveyors?variant=v3 to preview the v3 leaderboard.

Scan cadence

The hourly rematch covers freshest images first with namespace round-robin inside each week bucket. Before 2026-10-21, older images could go 6–11 days without a rescan while fresh CVE matches queued up in the DB. From that date, every tracked image is rematched against each Grype DB pull (every 4h) via a companion systemd service. The SBOM/content dedupe keeps the extra load cheap — most hourly cycles rematch rather than rescan.

Measurement bugs fixed on 2026-10-21

Changelog

From 2026-10-21 onward, every ranking or rubric change is announced here before it ships, dual-runs for ~2 weeks, and links to its effect on the top 10 publishers.

  1. 2026-11-04 rank-v3 cerodeo-v1

    Publisher rank v3: newest release wins, quality × Wilson small-sample correction

    Dual-run with v2 from 2026-10-21 through 2026-11-04. v3 grades each (publisher, project) by the single newest-pushed image's 48h-grace scoring_grade (not the best of any variant in the window). Primary ordering key is per_product_quality with a Wilson lower-bound small-sample correction; products_60d (breadth) becomes a separate column rather than a multiplier.

  2. 2026-10-21 rank-v2.1 cerodeo-v1
    DUAL-RUN until 2026-11-04

    Governance overhaul: unified ranks, operator-unranked, methodology dual-run

    Three disagreeing rank fields (leaderboard v2, publisher dossier v1, API /v1/publishers/:name alternate) reduced to one source of truth. ReadyStack listed unranked, scored like every other publisher, left out of the leaderboard ordering. Hard-coded project and publisher lists replaced with a public policy table. Four measurement bugs fixed in the scanner: unreachable HEALTHCHECK bonus, missed BuildKit in-index attestations, cosign presence-not-verified, upstream-release suffix compare. Severity normalized to CVSS v3 base score where available, falling back to grype's per-source rating. Freshness dated from first-sighting on ce.rodeo, not config.Created. Scanner now rescans every tracked image on each Grype DB update (not just the hourly subset).

  3. 2026-10-19 rank-v2 cerodeo-v1

    Publisher rank v2: product-level, 60-day scope, log-damped breadth

    Replaced the all-time per-image trust_score with per_product_quality × ln(1+products_60d) over products pushed in the last 60 days. Change announced the same day the switch happened; this did not follow the dual-run principle introduced on 2026-10-21.

  4. 2026-10-06 rank-v1 cerodeo-v1

    Initial rubric and publisher rank

    First published rubric (cerodeo-v1) and the all-time, per-image publisher trust_score. Shipped alongside the first scored snapshot in early October.

Think a principle should be stronger? Open an issue: github.com/c0inz/cerodeo/issues.