A catalog you can re-derive, or it isn’t a catalog.
The index is a public, forkable list of verified Lean packages. Each entry names a package repository at a pinned commit, says how far its evidence goes (its trust level), and carries verbatim copies of the receipts that justify that level. Anyone can re-derive an entry from the package’s bytes at the pin with one command. Everything is a file; git is the database.
Layout
index/
README.md generated by `lean-pkg index render`; never hand-edited
<name>/
entry.yaml schema index-entry/2
receipts/<c12>-<job>.json verbatim copy of the lower-tier receipt
receipts/<c12>-comparator.json verbatim copy of the comparator receipt
Logs stay in the package repository (under .lean-receipts/); index check
fetches them at the pin. The index root is --index PATH, else the
environment variable LEAN_PKG_INDEX, else index/ beside the registry when
it exists, else ./index, so the index can move to its own repository later
without code changes. index check, render and migrate refuse (exit 1) a
root that does not exist.
Trust levels
| Trust | Evidence that must validate at the pin | Proves |
|---|---|---|
lower-tier |
a lower-tier receipt (gate’s validator) |
the package builds with no sorry and only the standard axioms at that commit |
comparator |
a comparator receipt (comparator --check’s validator) |
the named theorems match the trusted statements; the table shows the mechanism, and legacy is not predictive of Palomar acceptance |
palomar |
a Palomar id, verified against data.palomar-registry.org when the network is reachable |
the result is registered in Palomar |
What each level does not prove, and how to re-derive an entry without a registry, is in the Trust model.
Trust is computed, never typed: index add derives it from the evidence
present, and index check recomputes it and refuses an entry whose stated
trust differs from its evidence.
comparator trust also requires coverage: every headline theorem must name,
in judged_as, a theorem that one of the comparator receipt’s configurations
judged (a Challenge restates a theorem under its own name). A comparator
receipt that judged other statements, such as a smoke configuration, does not
grade the headline: the entry is lower-tier if a lower-tier receipt exists,
and refused otherwise.
Entry schema (index-entry/2; index-entry/1 still validates)
Keys appear in exactly this order; an unknown key is refused. Every key is
required except built_on, which index-entry/2 accepts as an optional key
(proposal 0007); an entry without
it stays valid. index add
writes index-entry/2, which adds claims after headline; an
index-entry/1 entry has no claims key and stays valid;
lean-pkg index migrate rewrites it as index-entry/2 by deriving claims
from its headline (no other key changes), so its headlines become searchable.
| Key | Value |
|---|---|
schema |
index-entry/2 (or index-entry/1) |
name |
the directory name |
repo |
public https URL |
pin |
commit (40 hex, the receipts’ commit) and tag (or null; at the pin or an evidence-only descendant, or a docs-only one when the registry record opts in; see below) |
lean_toolchain |
equals lean-toolchain at the pin |
mathlib_rev |
equals the Mathlib rev in lake-manifest.json at the pin, or null |
license_spdx |
SPDX id; a license file must exist at the pin |
trust |
lower-tier, comparator or palomar (computed) |
receipts |
lower and comparator: repository paths under .lean-receipts/, or null |
headline |
list of name (fully qualified), file (repository-relative .lean), summary (one sentence), and optionally judged_as (the Challenge theorem name under which a comparator configuration judged it) |
claims |
index-entry/2 only: one claim per headline item, derived at index add and never typed: id (<name>/<slug of the declaration>), decl, kind (theorem), summary, judged_as, trust (the entry’s); lean-pkg search finds them (Prior-art search) |
palomar |
list of id and version |
doi |
null, or concept and versions (each doi, tag, commit) |
reservoir |
null, or the package’s Reservoir page |
formalization_yaml |
null, or a repository-relative path |
built_on |
optional, index-entry/2 only: credits for third-party Lean code the package builds on (see Credits); omitted when there is none |
fields |
lowercase tags such as probability |
consent |
recorded (true or false); the pointers to the consent records stay private |
added |
at (ISO-8601) and by (a public handle) |
notes |
free text |
No value may look like a local path, and local_path is never written.
Credits (built_on)
A package that is a thin layer over someone else’s library should say so
where readers look: index render shows each credit in the Built on column
of index/README.md (the library’s name linked to its repository, and its
authors) and again in a Built on section with the relation, license, pin
and note. built_on is a non-empty list; each credit is a mapping with these
keys, in this order, and no others:
| Key | Value |
|---|---|
name |
required: the library’s name, one line |
url |
required: its public https repository URL |
authors |
required: a non-empty list of names, as the library itself states its authorship (a file header, its README or CITATION.cff) |
license |
optional: its SPDX expression |
pin |
optional: the 40-hex commit the package uses |
relation |
optional: depends-on (a Lake dependency in lake-manifest.json) or adapted-from (source copied into the package and modified) |
note |
optional: one line, at most 300 characters, such as what the package takes from it |
An optional key is omitted, never null. Credits are not evidence: they change
neither trust nor the checks at the pin, and index check --clone only
warns when a depends-on (or unlabelled) credit’s pin differs from the
rev of the package with that URL in lake-manifest.json at the entry’s
pin, or when no package there has that URL. An adapted-from credit is
vendored source, so its pin is not compared.
What to list: every third-party Lean library the package’s Lean code imports
or copies, from its lake-manifest.json (its direct requires and the
libraries they pull in) and from any vendored source it carries. Mathlib and the packages Mathlib itself pulls in
(Batteries, Aesop, Qq, ProofWidgets, Plausible, ImportGraph,
LeanSearchClient, Cli), and code copied from Mathlib, are implicit and not
listed; Lean core is not listed. Tools that are not Lean code the package
builds on (scripts, CI helpers) are out of scope.
A missing credit for a Lake dependency is caught: index check --clone (or
--repo-dir) reads lake-manifest.json at the entry’s pin and prints one
note, “warning: Lake dependency NAME (URL) in lake-manifest.json at the pin
has no built_on credit”, for each package in packages[] that is not
implicit and that no credit names; --json lists them in the entry’s
uncredited_dependencies (name and url; [] in a static check). A
credit names a package when its url equals the package’s URL (ignoring
case and a trailing .git) or its name equals the package’s name
(ignoring case), whatever its relation. The warning never fails the check:
the exit status and ok are unchanged. A package is implicit when
- its name or URL is Mathlib or one of Mathlib’s own dependencies:
mathlib(leanprover-community/mathlib4),batteries,std(leanprover/std4),aesop,Qq(quote4),proofwidgets(ProofWidgets4),importGraph(import-graph),LeanSearchClient,plausibleandCli(leanprover/lean4-cli); - it is Lean core (
lean4,Lean,Init,Lake, orleanprover/lean4); or - the manifest marks it
"inherited": trueand its repository is undergithub.com/leanprover-community/orgithub.com/leanprover/, the organizations that host Mathlib and its dependencies, so a dependency Mathlib adds later is implicit before the list above names it.
inherited alone does not make a package implicit: a library pulled in by a
credited library is not Mathlib’s and warns until it is credited. A direct
require from those two organizations that is not in the list (a tactic
library, say) warns as well.
built_on:
- name: MarkovProcess
url: https://github.com/scottnarmstrong/MarkovProcess
authors:
- Scott Armstrong
license: Apache-2.0
pin: 666cda029098a1106914fc9fa7abd1727573284a
relation: depends-on
note: Supplies the Brownian motion, exit times and Dynkin's formula.
Adding an entry
From your registry row (repository, receipts, license, Palomar ids, DOI and Reservoir listing are taken from it):
lean-pkg index add my-lean --from-registry --by your-handle \
--headline 'MyLean.main_theorem :: MyLean/Main.lean :: One sentence saying what it proves.'
Without a registry, pass the facts as flags:
lean-pkg index add my-lean --repo https://github.com/you/my-lean --license Apache-2.0 \
--lower .lean-receipts/0123456789ab-slurm-4242.json \
--comparator .lean-receipts/0123456789ab-comparator.json \
--by your-handle --headline 'MyLean.main_theorem :: MyLean/Main.lean :: One sentence.'
lean-pkg index render
Credits are given with --built-on-file FILE, a YAML file holding the
built_on list (or a mapping whose only key is built_on, so the block can
be copied from an entry); [] means none.
index add clones the package (from --repo-dir or the URL) into a
temporary directory, finds the receipts as committed at the pin or in an
evidence-only descendant of it (untracked files do not count), checks out the
pin, and runs the same validators as gate and comparator. It refuses a
second entry for the same repository and commit.
An entry that already exists is refused unless --replace is given: that is
how a package is re-pinned at a new gated commit. --replace writes the new
entry over the old one, replaces its receipt copies, and carries the old
entry’s built_on over unchanged unless --built-on-file is given, so a
re-pin never drops the credits. The headlines, field tags and notes come from
the flags as usual. The pull request shows the diff.
By default index add also fetches the public repository’s branches and
refuses a pin or an evidence commit that no public branch contains, so an
entry never points at history that only exists on one machine.
--allow-unpushed skips that check for a provisional entry (the output says
so); index check --clone refuses such an entry until the commits are
pushed. Receipt copies are public: a receipt whose strings look like a local
path is refused, and the comparator runner records
COMPARATOR_RECEIPT_HOST_LABEL instead of the host name when that variable
is set.
A Palomar registration grades an entry only when its commit equals the
pin: index add keeps only those rows, and index check fetches each
Palomar entry and compares its source.commit and source.repository_url
with the entry.
Checking entries
lean-pkg index check # static: schema, copies, trust, duplicates
lean-pkg index check --clone # every entry, re-derived from bytes at the pin
lean-pkg index check --clone my-lean --json
With --clone (or --repo-dir for one entry) the check re-derives the
receipts and logs at the pin, compares the index copies byte for byte,
checks the toolchain, lake-manifest.json, the license, the tag and the
headline files, and recomputes trust. Palomar ids and the Reservoir listing
are verified by API when the network is reachable; otherwise they are
reported as skipped, never as failed or passed. Exit 0 when every entry
passes, 1 on any failure, 2 on a usage error.
Docs-only descendants
The tag, and the commits the receipts are committed in, are bound by the
same rule as gate, publish and comparator: an evidence-only descendant
of the pin, or, when the package’s registry record opts in
(docs_descendants: true, set with
lean-pkg update NAME --accept-docs-descendants --reason R), a docs-only
descendant: one where no build input changed (see
Receipts). index add and index check --clone /
--repo-dir read the opt-in from the registry they resolve (--registry, $LEAN_PKG_REGISTRY or the user default), and only
from the record with the entry’s name and repository, never from a file in
the package repository. Without a registry, or without the opt-in, the
evidence-only rule binds; a tag the docs-only rule would admit is then
refused with a clause that names the opt-in.
A repository whose HEAD has moved past the pin still passes: the pin is the claim. The rendered table shows the pin, not HEAD.
The static check refuses only a stated trust above the evidence; a stated
trust below it is caught by --clone or --repo-dir, which recompute trust
from bytes and require equality.
An unfinished project as an entry
A project does not have to be finished to be listed; it has to be honest. The index lists what is proved at a pin, nothing more, and the tool enforces that: the headline theorems must exist in the files named, their receipts must validate at the pin, and comparator trust requires every headline to be judged. An unfinished project is listed like this:
- Headlines are the results that are proved today, in the project’s own
words; the goal it is working toward goes in
notes, together with what is not proved.index addrefuses a headline whose declaration is not in the file at the pin, so the entry cannot run ahead of the code. - Trust comes from the receipts, not from ambition. A sorry-free library
with a lower-tier receipt is
lower-tier; a comparator receipt that judges the headline lifts it tocomparator; the Palomar level needs a registered result. A project below Palomar’s toolchain floor can still reach comparator trust through the legacy mechanism. - Partial and conditional results are claims, not headlines. The
package’s
claims.yamlmarks reusable resultsreusableand conditional or provisional onesexperimental(with the hypothesis in the summary), solean-pkg searchfinds them with their status. Conditional certificates are a planned schema extension; until then the summary carries the condition. - Progress is a new pin. Each milestone is a receipt at a new commit and a pull request that replaces the entry; the index history is the project’s verified timeline.
The lab’s own example is total-coloring-lean: a 700-theorem, sorry-free
library whose README states that it proves an auxiliary edge-coloring theorem
and a Δ+3 total assignment, and does not prove Vizing’s theorem or the
conjecture. Its entry says exactly that. Its second pin added comparator
evidence for both headline theorems, with Mathlib-only Challenge statements
in the package’s Audit/ directory.
Pull requests
A contributor adds index/<name>/ and the rendered index/README.md in one
pull request; the maintainer runs lean-pkg index check --clone <name>
before merging and pastes the output into the review. Hosted CI runs the same
check once the repository is public.
Next: Receipts and trust.