documenteer.toml reference#
Rubin’s Sphinx user guide configuration with documenteer.conf.guide uses a documenteer.toml file, located next to the Sphinx conf.py file to configure metadata about the project.
This page describes the schema for this documenteer.toml file.
For a step-by-step guide, see Setting up the Documenteer configuration for Rubin user guides.
[project] table#
The [project] table is where most of the project’s metadata is set.
Required
title#
Required
Name of the project, used as titles throughout the documentation site. The title can be different from the package name, if that’s the local standard.
[project]
title = "Documenteer"
base_url#
Optional Auto set by project.python
The root URL of the documentation project, used to set the canonical URL link rel, which is valuable for search engines.
[project]
base_url = "https://documenteer.lsst.io"
copyright#
Optional
The copyright statement, which should exclude the “Copyright” prefix.
[project]
copyright = "2015-2022 Association of Universities for Research in Astronomy, Inc. (AURA)"
github_url#
Optional Auto set by project.python
The URL for the project’s GitHub source repository. When set, a link to the repository is included in the site’s header.
[project]
github_url = "https://github.com/lsst-sqre/documenteer"
github_default_branch#
Optional
The default branch on GitHub.
Default is main.
Used in conjunction with the “Edit on GitHub” link, see sphinx.show_github_edit_link.
version#
Optional Auto set by project.python
The project’s version, which is set to the standard Sphinx version and release configuration variables.
[[project.citations]]#
Optional
Sites that are published with a DOI can declare their citations in the [[project.citations]] array of tables.
Documenteer uses them to make the site a proper DOI landing page: it renders a full bibliographic citation with the DOI as a resolvable https://doi.org/ link, and emits machine-readable citation metadata in the page <head>.
A declared citation is displayed in the site footer on every page, with the citation-card directive, which renders it as a card wherever a page asks for one, and with the doi role, which links its DOI inline.
That head metadata is what a DOI registration agency, Google Scholar, Zotero, and Google Dataset Search read.
Every page describes the self citation with the full set of Highwire meta tags — citation_title, a citation_author per author with its citation_author_institution and citation_author_orcid, citation_publication_date, citation_doi, citation_publisher, and citation_fulltext_html_url — plus the Dublin Core DC.identifier (the DOI as a https://doi.org/ URL), together with a schema.org JSON-LD block describing the site, following DataCite’s crosswalk from DataCite metadata to schema.org.
Those Highwire tags are what gives a reader a one-click “Save to Zotero” with the right title, creators, date, and DOI.
A field the entry does not state emits no tag, and a site that marks no entry self emits no meta tags at all — its JSON-LD block describes the site itself instead.
The other entries reach that block as relations of the site rather than as records repeated on every page of it, and which relation follows from whether the entry claims a page:
An entry that names a page inside the site is a part of the site’s work. The site-wide block names it in
hasPartby reference alone, and its full record moves to the page it claims.An entry with no page is a work the site cites. The preferred entry appears in the site-wide block in full whether or not the footer shows it, since it is the citation the site asks readers to use. Any other appears there in full when in_footer is true, and not at all when it is false.
A site that declares no citations emits none of it. See Related metadata for the whole picture.
A site that declares citations should set base_url explicitly, because every URL in that metadata is composed from it — citation_fulltext_html_url, the url of the site’s JSON-LD node, and the landing URL of each entry that claims a page.
A site that states none falls back to whatever its pyproject.toml names as its Homepage (see [project.python]), which is a field kept for a package index rather than for a DOI registration; a site with neither emits no citation_fulltext_html_url on any page and states no url on its site node.
Because it is an array of tables, the table header is written with double brackets and repeated once per citation. A site can cite more than one work — the documentation itself and the dataset it describes, for example — and the order the entries are written in is the order they appear in the site footer.
[[project.citations]]
doi = "10.71929/rubin/2570308"
label = "Dataset"
type = "dataset"
self = true
note = "Cite the DP2 dataset and this documentation."
title = "Data Preview 2"
publisher = "NSF-DOE Vera C. Rubin Observatory"
date = 2025-06-30
authors = [{ name = "NSF-DOE Vera C. Rubin Observatory" }]
Each entry carries two kinds of field.
The bibliographic fields (doi, url, type, title, authors, publisher, date, and version) describe the work being cited.
They can instead come from a CITATION.cff file (see cff and cff_preferred), and the ones a site’s works share can be stated once in [project.citation_defaults] rather than on every entry.
The presentation fields (label, self, preferred, page, in_footer, note, and bibtex_key) say how the site displays the citation, and are only ever set here.
doi#
Optional
The DOI of the work being cited.
It can be written bare (10.71929/rubin/2570308), as a https://doi.org/ URL, or with a doi: prefix; anything else fails the build.
Every entry has to be locatable, so this field or url must have a value — set here or supplied by cff. The self entry is the exception, and needs a DOI specifically: it is the claim that this site is a DOI’s landing page, which an entry with no DOI has no way to make.
type#
Optional
The kind of work being cited, which decides both the schema.org type the site publishes it under in its JSON-LD metadata and the entry type of the BibTeX a reader copies:
|
schema.org type |
BibTeX entry type |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Any other value fails the build.
The BibTeX entry types are biblatex’s, which is the vocabulary Zenodo and GitHub’s “Cite this repository” export in.
Classic BibTeX never defined @dataset or @software, and a classic style that meets an entry type it does not know typesets it as @misc — so declaring the specific type costs a reader of such a style nothing.
Set type = "dataset" on every data product the site publishes: Dataset is the type Google Dataset Search indexes, and it is the one that makes a data release discoverable as data rather than as a page about data.
A work that is none of the kinds above — an instrument or a facility, such as LSSTCam — is "other".
It is published as a generic CreativeWork and composes as @misc: this field has no narrower kind to give such a work, and a broad type that is true beats a narrow one that is not.
A citation that declares no type says nothing about what the work is: it composes as @misc, and is published as a WebSite if it is the self citation and a CreativeWork otherwise.
So an untyped entry renders much as "other" does; what it loses is the statement, since "other" says the work was typed and is none of the listed kinds where an absent field says nothing at all.
The self entry is typed like any other, so a site that is a data release’s landing page declares type = "dataset" there too.
If cff is set, the file’s own type supplies this field, and setting it here overrides the file’s value.
A file’s top-level record that declares no type is read as software, which is the default CFF itself defines for that key and which most files rely on; a preferred-citation that declares none leaves the entry untyped.
label#
Optional
A short label for this citation, such as "Dataset", "Paper", or "TAP".
It is the heading shown on the citation’s card and in the site footer, one of the three ways the citation-card directive and the doi role select an entry, and the name a warning about a citation uses.
It is a display string, and it need not be unique.
A label says what the reader needs to see at the spot the citation appears, so a site with a registered landing page per data product writes label = "TAP" on every one of those entries and selects each of them by bibtex_key or by DOI.
Identity belongs to the key, which the build already keeps distinct across the site.
A selector that two entries answer to emits a documenteer.citation_card warning naming both by key, and renders nothing, rather than picking one of them.
It is a display string in the other sense too: what a work is is declared with type.
self#
Optional
Whether this site is the registered landing page of this DOI.
Default is false, and at most one entry can set it to true.
This is a claim about where the DOI resolves, and nothing else.
It is what makes every page of the site describe the work in its Highwire and Dublin Core meta tags, and what makes it the subject of the site-wide JSON-LD block.
Set it only when doi.org really does send a reader here.
A work published somewhere else — a journal article, a Zenodo record, a dataset in another archive — has that publisher’s landing page, and marking it self tells a harvester this site is something it is not.
An entry that reads the preferred-citation of a cff file is that case by construction, and the build rejects it: such a record names the work the repository asks to be cited instead of itself, published elsewhere.
Set preferred = true in place of self to ask readers to cite that work, or cff_preferred = false to make this site the landing page of the repository’s own record.
A site that really is the preferred citation’s landing page says so by writing cff_preferred = true explicitly alongside self, which the build accepts.
self and page are mutually exclusive, and an entry that sets both fails the build.
Both name where the DOI resolves: the self entry’s landing page is the site itself, while page names a landing page inside the site, so setting both declares two landing pages for one work.
Which citation the site asks readers to use is a separate question, answered by preferred.
The two coincide for a site that publishes its own DOI, so a self entry is also the preferred citation unless another entry claims that.
A self entry’s in_footer defaults to true on its own account, not only by way of being the preferred citation, since displaying the citation is part of being a landing page.
A site that marks a different entry preferred therefore shows both in the footer, and writes in_footer = false on one if it wants only the other.
The self entry is the one entry that may omit title, since the site’s own project.title is then the title of the work.
A site that marks no entry self emits no citation meta tags on any page — not even the title and authors of the work it marks preferred, since those tags say that this page is the work’s full text — and its site-wide JSON-LD block describes the site itself — a schema.org WebSite carrying the site’s project.title and project.base_url and no identifier — with the declared works hanging off it.
preferred#
Optional
Whether this is the citation the site asks readers to use.
Default is false, and at most one entry can set it to true.
The preferred citation is the entry a citation-card renders when it is given no label, and one of the two entries whose in_footer defaults to true — the other being the self entry, which is the same entry unless this field names another.
A self entry is the preferred citation when no entry sets this field, so a site that publishes its own DOI states neither field twice.
Set preferred when the citation to use is not a work this site is the landing page of:
[[project.citations]]
cff = "../CITATION.cff"
label = "Paper"
preferred = true
note = "Cite this paper in publications that use the package."
Unlike self, preferred says nothing about where a DOI resolves, so it emits no head metadata of its own.
It is a statement about what this site asks for, which is the site’s to make about any work at all.
page#
Optional
The page inside this site that is the DOI’s registered landing page, written as a Sphinx docname — the source file’s path from the documentation root, without its file extension:
[[project.citations]]
doi = "10.71929/rubin/3382539"
page = "products/catalogs/object"
Default is unset, which means the site as a whole is the landing page.
Setting it moves the entry’s machine-readable metadata off every page and onto that one: the claimed page’s Highwire and Dublin Core meta tags describe this entry — its title, authors, date, DOI, publisher, and this page as its citation_fulltext_html_url — alongside a JSON-LD block describing the same work at the page’s own URL.
It also says what the work is to the site: a claimed entry is a part of the site’s own work rather than something the site cites, so the site-wide block names it in hasPart and the claimed page’s block points back with isPartOf.
Every other page of the site is unaffected and keeps the self citation’s metadata.
This is what a data release’s documentation needs when each of its data products has a DOI of its own that resolves to the product’s page; see Landing pages inside the site.
An entry cannot set both page and self; the two are mutually exclusive, since the self entry’s landing page is the site itself and page names a landing page inside it.
Asking readers to cite a work whose landing page is one of this site’s pages is a different claim, made with preferred, which does combine with page.
The docname may be followed by # and a fragment identifier, naming a location within the page.
Write that location as an explicit target in the page’s source, above the heading that documents the work:
.. _object-tap:
TAP
---
and claim the target:
[[project.citations]]
doi = "10.71929/rubin/3382540"
page = "products/catalogs/object#object-tap"
A heading’s own anchor is also a fragment this accepts, but it is a poor one to register a DOI against: it is generated from the heading’s text, so rewording “TAP” to “TAP service” turns #tap into #tap-service and the URL registered at DataCite stops resolving to anything on the page.
An explicit target is the author’s to name and to keep, and survives any rewording of the heading it sits above.
Several entries may claim the same page, provided each names a different fragment — two products documented in two sections of one page, for example.
Such a page describes both works in a JSON-LD @graph and emits no citation meta tags at all, because every one of them is single-valued — one title, one DOI, one date — and the page is the landing page of more than one work.
Two entries that name the same docname and the same fragment fail the build.
The claim does not change what the site displays: citation-card with no argument still renders the self entry, and a page that wants to show its own citation names it by label.
A page the build cannot resolve is a warning, not an error: the entry still appears everywhere else the site shows its citations, but no page carries its landing-page metadata.
Both halves of the claim are checked — a docname the project does not contain, and a fragment that names no anchor on the page it claims, which the warning reports alongside the explicit targets that page does record.
Those warnings carry the subtype documenteer.citation_page, so a site that claims a page, or an anchor, it has not written yet can keep them from failing a warnings-as-errors (-W) build:
# conf.py
suppress_warnings = ["documenteer.citation_page"]
The page’s URL comes from project.base_url, with the entry’s fragment appended when it names one.
A site that sets no base_url cannot know it, so the JSON-LD node falls back to the https://doi.org/ URL, as it does elsewhere, and the page emits no citation_fulltext_html_url; the rest of the meta tags are unaffected.
note#
Optional
Free text about when to use this citation, displayed alongside it.
[[project.citations]]
note = "To be used when citing the DP2 dataset and this documentation."
bibtex_key#
Optional
The key this citation’s BibTeX entry is written under — what a reader’s own \cite commands name once they have copied the entry.
[[project.citations]]
doi = "10.71929/rubin/2570308"
bibtex_key = "RTN-115"
The key is what a manuscript pins, so this field exists to let a site keep one stable no matter what else changes.
Set it and the entry is written @article{RTN-115, — or whatever entry type the citation’s type implies.
Unset, the key is defaulted, in this order:
The site’s lsst.io subdomain, for the entry describing this site’s own work. A site published at the root of a
<name>.lsst.iohost is<name>to every reader at Rubin, sohttps://dp2.lsst.iokeys its own workdp2andhttps://safir.lsst.io/keys its ownsafir. The entry that gets it is the self entry, or the preferred one for a site that publishes no DOI of its own; exactly one entry ever receives it, and a site that marks neither leaves the subdomain unused. A base_url that is not such a host —https://github.com/lsst/daf_butler,https://sub.dp2.lsst.io— or that points below the host’s root —https://pipelines.lsst.io/v/daily/, one versioned build among the many that share the subdomain — names no work, and the entry falls through to the next rule.The DOI, verbatim, for any other entry that has one:
10.71929/rubin/3382528. This is howlsst.bibkeys every DataCite record, so an entry copied from a Documenteer site collides with nothing a reader already has.Author, year, and title, for an entry with no DOI, reduced to lowercase ASCII:
jenness2022vera. A leadinga,an, ortheis skipped when the title word is chosen, so a title opening with an article is not keyed by the article every such title shares. The version is never part of the key, so a reader’s.bibfile keeps working across releases.
Every key on the site has to be distinct: a reader pastes the entries into one .bib file, which cannot hold two under one key.
Two entries resolving to the same key — two author-year-title keys that agree, or a bibtex_key equal to another entry’s default — fail the build with a message naming both entries.
A key is written in printable ASCII with no whitespace and none of the characters ,, {, }, (, ), ", #, %, ~, or \, each of which ends the key or means something else to BibTeX.
The punctuation a key does carry is -, _, ., :, and / — which is what lets a DOI be a key.
Anything else fails the build.
title#
Optional
The title of the work being cited. Required unless cff supplies one, or the entry is the self citation — which falls back to the site’s project.title.
publisher#
Optional
The organization that published the work.
date#
Optional
The work’s publication date, at the precision its source states.
A source that knows the day is written as a TOML date; one that knows only the year, or only the year and month, has no TOML type for that — a TOML date is always a full date — so it is written as an integer year or as a quoted ISO 8601 date:
date = 2025-06-30 # a full date
date = 2025 # a year
date = "2025-06" # a year and a month
date = "2025" # a year, quoted
Only the year appears in a rendered citation, so all four forms display alike.
The precision matters to the machine-readable metadata: the date is published as the schema.org datePublished of the entry’s JSON-LD block, exactly as written here.
It reaches BibTeX too, where a date stated to the month or finer writes a month field after the year — date = "2026-07" composes year = {2026}, then month = {July}, — as the month name BibTeX, biblatex, and lsst.bib all read.
A day never becomes a field of its own, because BibTeX has none to put it in.
Writing 2025-01-01 for a work whose sources say only “2025” would assert a publication day on every page of the site that nothing stands behind, which is why the reduced forms exist.
Anything else — "June 2025", a month outside 1–12, a year that is not four digits — fails the build with a message naming the accepted forms.
An entry that states no date, and whose cff file supplies none either, is cited undated: the rendered citation shows no year, and its BibTeX entry carries no year field.
That is a warning, not an error — the citation still displays — and it carries the subtype documenteer.citation_date, naming the entry and the record the date belongs in.
A software entry located by a url rather than by a DOI is the exception, and is not reported: a package released continuously has no publication event to date, and what identifies the code a reader ran is its version.
A site with no date to give suppresses it the way any other Sphinx warning is suppressed (see Undated citations):
# conf.py
suppress_warnings = ["documenteer.citation_date"]
version#
Optional
The release of the work being cited.
[[project.citations]]
url = "https://github.com/lsst-sqre/safir"
type = "software"
title = "Safir"
version = "12.3.0"
FORCE11’s software citation principles list the version among the elements a software citation has to carry, because software is the kind of work whose behavior changes between releases: a citation naming only the project says which code was run no more precisely than naming the language would.
The version qualifies the title wherever the citation is displayed — Safir (version 12.3.0). — and reaches the machine-readable metadata on the types that have somewhere to put it:
BibTeX writes
version = {12.3.0}on a@softwareor@datasetentry, the two biblatex entry types that define the field, and omits it on every other.JSON-LD writes
softwareVersionon a SoftwareSourceCode node andversionon a Dataset node, the two schema.org types that define one.The BibTeX key never carries it, so a reader’s
.bibfile keeps working across releases of the site that composed it.
If cff is set, the file supplies this field from the version of whichever record the entry cites — the top-level record’s for a repository, or a preferred-citation’s own.
Setting it here overrides the file’s value.
A blank version fails the build, the way a blank url does: it reads everywhere as a version that was stated while composing to nothing, and it would also suppress the default described next.
The default for this site’s own software
A software entry that states no version, and whose cff file supplies none either, takes the site’s own project.version when the entry describes this site’s package. An entry describes this site’s package when it is any of:
the self entry — this site is that work’s landing page;
the preferred citation — this is the work the site asks readers to cite;
an entry reading a
CITATION.cfffile’s top-level record with cff_preferred = false — the repository the site documents.
The page a reader is on is the documentation of that release, so filling the version in states what the page already means.
Every other software entry is somebody else’s package, whose releases this site knows nothing about, and is left version-less: a guide that cites the Science Pipelines alongside its own package labels only its own.
No other type ever defaults — a dataset’s or a paper’s release has nothing to do with the version of the software that builds the site — even when the entry sets self.
The default needs a version to take.
A site that declares neither project.version nor project.python resolves its version to the literal Latest, which names a documentation build rather than a release; such a site’s citations state no version at all rather than claiming one.
A site whose version comes from installed package metadata is cited at whatever that metadata reports, a development build (12.4.0.dev3+g1a2b3c4) included.
That is deliberate: the pages were built from that code, and naming it is more honest than rounding it to the last tag.
A site that would rather cite the release than the build sets version on the entry, which always wins.
There is no way to switch the default off other than stating a version: an explicit version is the whole of the API.
url#
Optional
The work’s landing page — where a reader goes to find it.
A work with a DOI is already located by it: the rendered citation ends in the https://doi.org/ link, and this field is not needed.
It is what locates a work that has no DOI, such as a package or a dataset that has never been deposited:
[[project.citations]]
url = "https://github.com/lsst/daf_butler"
type = "software"
title = "daf_butler"
label = "Software"
preferred = true
Such a citation renders exactly as one with a DOI does, ending in a link to this URL instead of to doi.org, and its BibTeX entry carries a url field and no doi.
The preferred line is what puts it on a surface at all. self is the field a site’s own work usually sets, and it needs a DOI, so an entry located by a URL claims the site’s citation this way instead; an entry that sets neither renders in no footer and answers no card that does not name it. See Software that has never been deposited for the whole configuration a package with no DOI needs.
The value has to be an absolute http or https URL; a blank one, or one written without a scheme (github.com/lsst/daf_butler), fails the build.
Both are values a reader cannot be sent to: a blank one renders as a citation with no link at all, and a scheme-less one is read as a path relative to whichever page carries the citation.
If cff is set, the file supplies this field from its url, or from its repository-code when it states no landing page — which is how a CFF file that has never carried a DOI locates the software it describes.
Setting it here overrides the file’s value.
Either field of the file has to be an absolute URL for the same reason, and a blank one in the file is read as no landing page at all rather than as one.
cff#
Optional
The path to a CITATION.cff file that supplies the entry’s bibliographic fields, relative to documenteer.toml.
Since documenteer.toml sits beside conf.py in the documentation directory and CITATION.cff sits at the repository root, this is usually "../CITATION.cff".
[[project.citations]]
cff = "../CITATION.cff"
preferred = true
label = "Paper"
note = "Cite this paper in publications that use the package."
A repository that already maintains a CITATION.cff for GitHub’s “Cite this repository” button has written the bibliographic record down once; pointing at it keeps documenteer.toml from restating it.
When the file declares a preferred-citation, that is the citation Documenteer reads, exactly as GitHub renders it — unless the entry sets cff_preferred = false, which reads the file’s top-level record instead.
A preferred-citation is by construction a work other than the repository — the paper to cite instead of the software — so its landing page belongs to whoever published it.
An entry that reads one and sets self fails the build, since self would publish every page of this site as that work’s full text.
Three fixes are available, and which is right depends on what the entry meant:
Set preferred = true instead of
self, as above: the site asks readers to cite the paper without claiming to be the paper’s landing page.Set cff_preferred = false to cite the file’s top-level record instead — the repository itself, which is a work this site can be the landing page of.
Write
cff_preferred = trueexplicitly alongsideselfif this site really is the preferred citation’s landing page, as it is for a paper whoseCITATION.cffand documentation site are one repository. Naming the record on purpose is the acknowledgement, and the build accepts it.
The file’s own type supplies the entry’s type, so a repository that describes itself as type: software, or whose preferred citation is an article or a report, is typed without restating it.
A CFF type that Documenteer has no counterpart for leaves the entry untyped.
The file’s date is likewise kept at the precision it is written in: a date-released or date-published dates the work to the day, and a reference that carries only a year, or a year and a month, dates it to the year or the month (see date).
Any bibliographic field set alongside cff overrides the file’s value, so a single field can be corrected without abandoning the file:
[[project.citations]]
cff = "../CITATION.cff"
preferred = true
title = "Data Preview 2 Documentation"
A cff path that names no file, or a file that cannot be read as a citation, fails the build with an error naming the path.
Documenteer reads only the fields a citation is composed from: title, type, authors (both people and entities such as an observatory), publisher or institution, the dates, version, number, and the work’s location — its doi, or an identifiers entry of type: doi, and its url or repository-code.
Everything else a CITATION.cff may carry — abstract, license, keywords, commit, and identifiers of any other type, such as a Software Heritage swh identifier — is not read, and a site that wants any of it states it on the page itself.
The version read is the one on whichever record the entry cites, so a preferred-citation that states its own release is cited at that release rather than at the repository’s (see version).
cff_preferred#
Optional
Which record inside the cff file the entry cites.
Default is true: a preferred-citation in the file is the record read, which is what GitHub’s “Cite this repository” button renders.
Set it to false to cite the file’s top-level record — the software or the dataset the repository itself is:
[[project.citations]]
cff = "../CITATION.cff"
cff_preferred = false
label = "Software"
That is the only way to cite a repository whose CITATION.cff prefers a paper, and a site can do both at once by declaring two entries against the same file: one for the paper and one, with cff_preferred = false, for the software.
The top-level record’s own type — which CFF restricts to software or dataset — supplies the entry’s type, and a top-level record with no DOI is located by its url or repository-code (see url).
CFF makes that key optional and defines its default as software, so a top-level record that states no type — as most files, including generated ones, do not — is read as software rather than as a work of unstated kind.
cff_preferred chooses which record of a file is read; preferred chooses which of the site’s citations is the one it asks readers to use.
The two are unrelated, and an entry that sets cff_preferred without cff fails the build, since there is then no file whose records it could be choosing between.
[project.citation_defaults]#
Optional
The bibliographic fields the site’s [[project.citations]] entries share, stated once.
A site that mints a DOI per data product declares an entry per product, and those entries agree about nearly everything: the same publisher, the same author, the same year, the same type.
Spelled out on each of forty products, those four facts are what a reader has to look past to find the one thing an entry says — and documenteer.toml stops being a file anyone reads.
This table states them once, above the entries that take them:
[project.citation_defaults]
type = "dataset"
publisher = "NSF-DOE Vera C. Rubin Observatory"
date = 2026
authors = [
{ name = "NSF-DOE Vera C. Rubin Observatory", ror = "https://ror.org/048g3cy84" },
]
[[project.citations]]
doi = "10.71929/rubin/2570308"
label = "Release"
self = true
title = "Data Preview 2"
[[project.citations]]
doi = "10.71929/rubin/3382539"
label = "Butler"
page = "products/catalogs/object#object-butler"
title = "DP2 Object catalog"
[[project.citations]]
doi = "10.71929/rubin/3382540"
label = "TAP"
page = "products/catalogs/object#object-tap"
title = "DP2 Object catalog"
Each entry is then the four fields that tell one product from the next.
The table is written with single brackets — there is one of it — and it sits beside the array of entries rather than inside one.
The fields it accepts
Five keys, each the entry field of the same name, written the same way and validated the same way:
How a field resolves
Each of those fields on each entry is taken from the first source that states it:
the entry’s own value;
the record its cff file supplies, for an entry that names one;
this table;
nothing, and the citation is composed without the field.
A default fills only what nothing else states, so a CITATION.cff file’s date-released beats a site-wide date and a paper from 2019 keeps its own year.
The entries a defaults table does not describe are exactly the works a site cites rather than publishes, and each of them states what it needs on its own.
authors is all-or-nothing: an entry that names any author names all of them, so its list replaces this one instead of extending it. An entry that names none takes the default list whole, ROR identifiers and all.
A defaulted date is a date — an entry dated only by this table is not reported by the documenteer.citation_date warning — and a defaulted version is a stated one, so it wins over the project.version that a software entry describing this site’s own package would otherwise inherit.
The fields it does not accept
Any other key fails the build with a message naming the table and the key.
Identity and presentation are per work: doi, url, title, label, page, note, bibtex_key, self, preferred, in_footer, cff, and cff_preferred each name one work, so there is no value any of them could take that every entry would want. The same check catches a misspelled key, which would otherwise be a default that silently never applied.
A [project.citation_defaults] table on a site that declares no [[project.citations]] entries is accepted and does nothing.
[project.openapi]#
Optional
Web applications that use OpenAPI can include a [project.openapi] table in documenteer.toml to embed a Redoc subsite of the API documentation (see Embedding a Redoc subsite for OpenAPI (HTTP API) documentation).
doc_path#
Optional
The docname (without extension) of the page in the Sphinx documentation tree where the Redoc HTML page is built.
Default is api.
openapi_path#
Optional
The path to the OpenAPI specification file, relative to the Sphinx configuration file, conf.py.
If [project.openapi.generator] is set, this is the path where the OpenAPI specification file is generated.
[project.openapi.generator]#
Optional
If this table is provided, the OpenAPI specification file is generated from a user-specified Python function. This is useful for FastAPI and similar applications where the OpenAPI specification is generated from the application code.
function#
Required
The Python function that generates the OpenAPI specification file. This function must return the OpenAPI specification as a JSON-serialized string.
Specify the function as <module>:<function>.
For example, if the function called create_openapi is in the main.py module of the example package, the value would be "example.main:create_openapi".
[project.openapi.generator]
function = "example.main:create_openapi"
positional_args#
Optional
Positional arguments to pass to the function, if required.
[project.openapi.generator]
function = "example.main:create_openapi"
positional_args = ["arg1", "arg2"]
keyword_args#
Optional
Keyword arguments to pass to the function, if required.
[project.openapi.generator]
function = "example.main:create_openapi"
keyword_args = {kwarg1 = "value1", kwarg2 = "value2"}
[project.python]#
Optional
Projects that use a pyproject.toml to set their build metadata can include a [project.python] table in documenteer.toml.
With this, many metadata values are automatically detected — look for Auto set by project.python badges above.
Note
If a value is directly set, such as version, that value will override will override information discovered from the Python project itself.
package#
Required
This is the Python project’s name, as set in the name field of the [project] table in pyproject.toml.
Note that the package name can be different from the Python module name.
Setting this field actives automatic metadata discovery for Python projects.
[project]
[project.python]
package = "documenteer"
documentation_url_key#
Optional
By default the base_url is detected from the Homepage field in the [project.urls] table of pyproject.toml.
If your documentation’s URL is associated with a different field label, set that with documentation_url_key.
github_url_key#
Optional
By default the github_url is detected from the Source field in the [project.urls] table of pyproject.toml.
If your GitHub repository’s URL is associated with a different field label, set that with github_url_key.
[sphinx]#
Optional
This [sphinx] table allows you to set a number of Sphinx configurations that you would normally set through the conf.py file.
exclude#
Optional
A list of file paths, relative to conf.py, to exclude from the Sphinx build.
This configuration is often used to prevent file unrelated to the documentation from being accidentally included in the site build.
documenteer.conf.guide includes common files and directories, so you may not need to modify this configuration in standard situations.
extensions#
Optional
A list of Sphinx extensions to append to the extensions included in the Documenteer configuration preset (see documenteer.conf.guide). Duplicate extensions are ignored.
Remember that additional packages may need to be added to your project’s Python dependencies (such as in a requirements.txt or pyproject.toml file).
nitpicky#
Optional
Set to true to escalate Sphinx warnings to errors, which is useful for leveraging CI to notify you of any syntax errors.
The default is false.
[sphinx]
nitpicky = true
See nitpick_ignore and nitpick_ignore_regex for ways to suppress unavoidable errors.
nitpick_ignore#
Optional
A list of Sphinx warnings to ignore. Each item is a tuple of two items:
type, often the reStructuredText role or directive creating the error/warning.target, often the argument to the reStructuredText role.
[sphinx]
nitpick_ignore = [
["py:class", "fastapi.applications.FastAPI"],
["py:class", "httpx.AsyncClient"],
["py:class", "pydantic.main.BaseModel"],
]
This configuration extends the Sphinx nitpick_ignore configuration.
nitpick_ignore_regex#
Optional
A list of Sphinx warnings to ignore, formatted as regular expressions. Each item is a tuple of two items:
type, a regular expression of the warning type.target, a regular expression of the warning target.
[sphinx]
nitpick_ignore_regex = [
['py:.*', 'fastapi.*'],
['py:.*', 'httpx.*'],
['py:.*', 'pydantic*'],
]
Tip
Use single quotes for literal strings in TOML.
This configuration extends the Sphinx nitpick_ignore_regex configuration.
rst_epilog_file#
Optional
Set this as a path to a reStructuredText file (relative to documenteer.toml and conf.py) containing substitutions and link targets that are available to all documentation pages.
This configuration sets Sphinx’s rst_epilog configuration.
If set, the file is also included in the Sphinx source ignore list to prevent it from becoming a standalone page.
[sphinx]
rst_epilog_file = "_rst_epilog.rst"
.. _Astropy Project: https://www.astropy.org
.. |required| replace:: :bdg-primary-line:`Required`
.. |optional| replace:: :bdg-secondary-line:`Optional`
See Using the rst epilog for common links and substitutions.
python_api_dir#
Optional
Set this to the directory where Python API documentation is generated, through automodapi.
The default value is api, which is a good standard for Python projects with a public API.
If the Python API is oriented towards contributors, such as in an application or service, you can change the default:
[sphinx]
python_api_dir = "dev/api/contents"
[sphinx.redirects]#
Optional
A table of paths to redirect to other paths. Use this setting to redirect old page locations to the new locations when a documentation site is reorganized.
[sphinx.redirects]
"old/path" = "new/path"
"old/path2" = "new/path2"
[sphinx.theme]#
Optional
Configurations related to the Sphinx HTML theme.
header_links_before_dropdown#
Optional
Number of links to show in the navigation head before folding extra items into a “More” dropdown. The default is 5.
If the section titles are long you may need to reduce this number.
show_github_edit_link#
Optional
Default is true, so that each page contains a link to edit its source on GitHub.
This configuration requires information about the GitHub repository from these other configurations:
See also
“Edit on GitHub” links for how the edit URL is assembled.
The in-repository path of the documentation is detected automatically from where the Sphinx source directory sits in the Git working tree, so there is nothing to configure for it.
Builds that keep conf.py outside the source directory — sphinx-build -c . docs _build/html, for instance — link to the right file.
When the documentation isn’t being built from a Git checkout (an sdist, or a Docker image built without the .git directory) the path can’t be determined, so the button is omitted from every page — noted in the build log at the informational level — and the build proceeds.
Such a build does still draw a git.subprocess_error warning from sphinx-last-updated-by-git, so if you build with -W see “Edit on GitHub” links for the warning to suppress.
To keep the button in that situation, set the path yourself with html_context["doc_path"] in conf.py; see “Edit on GitHub” links.
show_last_updated#
Optional
Default is true, so that each page shows a “Last updated on <date>.” timestamp at the bottom of each page.
See also
“Last updated” page timestamps for how the date is computed and the extension’s Sphinx configuration values.
The date is computed from the page’s Git commit history, not the filesystem modification time (which is meaningless in CI).
It is the most recent commit date across the page’s own source file and any files the page pulls in with include or literalinclude directives, so editing an included snippet updates every page that uses it.
Because the date is the last commit date, uncommitted local edits don’t change it; a page whose source has never been committed shows no timestamp.
Set this to false to hide the timestamp:
[sphinx.theme]
show_last_updated = false
Important
Because the date comes from the Git history, your CI build must check out the full commit history.
With actions/checkout, set fetch-depth: 0:
- uses: actions/checkout@v6
with:
fetch-depth: 0
A shallow clone (the default) only fetches the most recent commit, so every page would otherwise report the same, incorrect date.
To avoid publishing misleading data, Documenteer detects a shallow clone, omits the “Last updated” timestamp from every page, and emits a single build warning telling you to set fetch-depth: 0.
[sphinx.intersphinx]#
Optional
Configurations related to Intersphinx for linking to other Sphinx projects.
[sphinx.intersphinx.projects]#
Optional
A table of Sphinx projects.
The labels are targets for the external role.
The values are URLs to the root of Sphinx documentation projects.
[sphinx.intersphinx.projects]
sphinx = "https://www.sphinx-doc.org/en/master/"
documenteer = "https://documenteer.lsst.io"
python = "https://docs.python.org/3/"
See the Intersphinx documentation for details on linking to other Sphinx projects.
[sphinx.intersphinx.cache]#
Optional
Configurations for prefetching intersphinx inventories from the Ook inventory cache service (the documenteer.ext.intersphinxcache extension).
By default, Documenteer prefetches each intersphinx project’s object inventory (objects.inv) from the Ook inventory cache service and rewrites intersphinx_mapping to point at the locally-written files, so documentation builds no longer depend on third-party site availability.
Only the inventory locations are rewritten — the target URIs are left unchanged, so resolved links still point at the real upstream sites.
Prefetching requires a bearer token for the Ook API, read from the OOK_TOKEN environment variable.
When the token is unset, the extension is a complete no-op and stock Intersphinx behavior is unchanged, so builds still work for projects that haven’t configured the token (for example, fork pull requests where secrets are unavailable, or local builds).
When the service fails for an individual inventory (an unauthorized or rejected token, an unreachable service, a server error, or a timeout), that mapping entry is left untouched so Intersphinx fetches the origin directly, and the build reports the fallback at the INFO log level naming the inventory.
The fallback is logged at INFO rather than as a warning on purpose: Rubin documentation builds run with warnings-as-errors (-W), so reporting graceful service degradation as a warning would fail the build.
An Ook outage can never make a build worse than a build without the service.
To avoid re-downloading inventories on every build, Documenteer caches each prefetched objects.inv on disk and only revalidates it with Ook after a short time-to-live (see disk_cache_ttl below).
While a cached inventory is younger than the TTL, it is reused without contacting Ook at all; once the TTL has expired, Documenteer revalidates conditionally with an If-None-Match request, and a 304 Not Modified reuses the on-disk copy with no inventory body transferred.
The inventory prefetch summary#
Once the prefetch is done, Documenteer logs one summary block naming every mapping entry it considered, in [sphinx.intersphinx.projects] order — the order you see in your own configuration file:
Intersphinx inventory prefetch summary (Ook cache status):
python hit fetched 2026-08-18T17:58:24Z (26 minutes ago)
sphinx stale fetched 2026-08-18T15:24:30Z (3 hours ago)
numpy miss fetched 2026-08-18T18:24:28Z (just now)
pydantic hit fetched 2026-08-09T18:24:30Z (9 days ago) -> moved
astropy served fetch time unavailable
safir disk cache (Ook was not contacted)
requests direct fetch (Ook could not be reached)
The whole block is logged at INFO, so it never affects a warnings-as-errors (-W) build: none of what it reports is yours to fix.
Entries Documenteer doesn’t prefetch at all — a local target URI, or an inventory location that’s already a local path — get no row.
The second column is how that inventory was obtained. Three of its values come from Ook, passed through verbatim, and describe the state of Ook’s copy:
hitOok served its cached copy, which was still within its own freshness lifetime.
staleOok served its cached copy, which is past its freshness lifetime. On its own this is a normal Ook serve, not an error — see below.
missOok had no usable cached copy, so it fetched the inventory from the origin site to answer the request.
The remaining values are Documenteer’s own, and describe what the client did:
servedOok answered but sent no cache-status header, so all that’s known is that Ook served the inventory. This is what an Ook deployment older than the cache-status header looks like.
disk cacheDocumenteer’s own on-disk TTL fast path answered this entry and Ook was never contacted for it — see disk_cache_ttl.
direct fetchThe prefetch fell back to the origin: Documenteer left this mapping entry untouched, so Intersphinx fetched
objects.invfrom the upstream site itself, exactly as it would without the service. The reason is in parentheses on the same row, and the matching per-entryINFOline above the block carries the full error.
The third column is when Ook last confirmed that inventory with its origin site — not when the bytes it served to you were downloaded.
A background refresh that the origin answered with 304 Not Modified keeps Ook’s stored bytes and still advances this time.
It’s reported as the absolute UTC instant, so a row can be correlated with Ook’s own logs, followed by a humanized age for eyeballing.
Rows that explain themselves in parentheses (disk cache and direct fetch) report no fetch time, because Ook was either never asked or never served the inventory; an Ook-served row for which the service sent no usable time reads fetch time unavailable rather than showing a placeholder that would read as an age.
A -> moved flag marks a row whose configured inventory URL Ook reports as permanently moved.
The destination URL, and what to do about it, are in that entry’s own notice rather than in the table; see warn_on_permanent_redirect.
Important
A stale row on its own is not a problem, and there’s nothing to do about it.
Ook deliberately keeps serving a copy that’s past its freshness lifetime while a background job revalidates it, so that a slow or briefly unavailable origin site can’t break your build.
That availability is the entire point of the cache.
What’s worth acting on is stale paired with an old fetch time.
That combination means Ook’s refreshes for that inventory have been failing for as long as the fetch time is old, so the copy you’re building against really is drifting from the origin.
Report it in #square-docs-support on Slack.
use_service#
Optional
Whether to prefetch intersphinx inventories from the Ook inventory cache service.
Default is true.
Set this to false as an escape hatch to disable prefetching so Intersphinx fetches every inventory directly from its origin site:
[sphinx.intersphinx.cache]
use_service = false
With use_service = false the service is never contacted, even when an OOK_TOKEN is set.
service_url#
Optional
Base URL of the Ook API that hosts the intersphinx inventory cache service.
Default is https://roundtable.lsst.cloud/ook.
disk_cache_ttl#
Optional
How long, in seconds, a prefetched inventory on disk is reused before Documenteer revalidates it with the Ook service.
Default is 600 (10 minutes).
While a cached objects.inv is younger than the TTL, Documenteer reuses it as-is and makes no request to Ook, so rapid successive local rebuilds skip the round-trip entirely.
Once the TTL has expired, Documenteer revalidates the inventory conditionally: it sends the ETag it stored alongside the cached file as an If-None-Match header, and if Ook answers 304 Not Modified the on-disk copy is reused with no inventory body transferred and its TTL window restarts.
A 200 OK response replaces the cached inventory.
Set disk_cache_ttl to 0 to disable this fast path so every build revalidates with Ook:
[sphinx.intersphinx.cache]
disk_cache_ttl = 0
The TTL governs only the client-to-Ook hop; whether Ook’s own cached copy is current relative to the origin site remains Ook’s concern.
warn_on_permanent_redirect#
Optional
Whether to report a permanently-moved intersphinx inventory URL as a Sphinx warning rather than at the INFO log level.
Default is false.
When the Ook service reports that one of your configured inventory URLs now redirects permanently to a new location, Documenteer tells you so in the build log, naming the mapping key, the URL you configure, where it now lives, and the [sphinx.intersphinx.projects] entry to update.
By default that notice is logged at INFO: the move originates upstream, outside your control, and Rubin documentation builds run with warnings-as-errors (-W), so warning about it would fail your builds on a third party’s schedule.
Set this to true if you would rather your build fail than carry a stale inventory URL:
[sphinx.intersphinx.cache]
warn_on_permanent_redirect = true
The setting escalates only that one notice.
The inventory summary block stays at INFO either way, so opting in never turns a block of pure status reporting into a build failure, and prefetching is unaffected — the mapping entry is still rewritten to the locally cached inventory whether or not escalation is enabled.
The escalated notice carries the warning subtype documenteer.intersphinx_permanent_redirect, so you can silence a move you already know about — one you can’t act on yet, for instance — while keeping the warning for every other inventory:
# conf.py
suppress_warnings = ["documenteer.intersphinx_permanent_redirect"]
Note that Ook reports the redirect chain it observed at its last successful fetch of the inventory, not at your build time.
[sphinx.linkcheck]#
Optional
Configurations for the linkcheck builder, which checks the external links in the documentation.
By default, Documenteer replaces Sphinx’s built-in linkcheck builder with a builder backed by the Ook link-check service (the documenteer.ext.linkcheckservice extension).
Instead of checking every link in-process, the builder submits the project’s external links to the service and polls for the results.
The service caches results and retries failing links over time, so documentation builds no longer fail on transient third-party outages.
The service requires a bearer token for the Ook API, read from the OOK_TOKEN environment variable.
If the token is missing or rejected, the builder falls back to Sphinx’s built-in in-process linkcheck builder in every mode, so link checking still runs for projects that haven’t configured the token (for example, fork pull requests where secrets are unavailable, or CI that doesn’t forward the token).
The built-in check’s own result then decides the build’s exit status.
If instead the service is unreachable or the polling budget is exhausted, the build falls back the same way by default: the builder reports the service problem at the INFO log level and checks the links in-process.
An outage therefore costs the build time — every link is visited from the machine running the build, with none of the service’s caching or retry buffering — rather than costing it link checking, and broken links the in-process check finds fail the build as they always do.
Set strict to true to fail the build on the service problem itself instead.
Links the service reports as broken always fail the build, regardless of the strict setting.
ignore#
Optional
List of URL regular expressions patterns to ignore checking.
These are appended to the linkcheck_ignore configuration.
Ignored URLs apply to both the service-backed builder (matching URLs are never submitted to the service) and Sphinx’s built-in linkcheck builder.
use_service#
Optional
Whether to check links with the Ook link-check service instead of Sphinx’s built-in linkcheck builder.
Default is true.
Set this to false as an escape hatch to restore Sphinx’s built-in linkcheck builder, which checks each link in-process and doesn’t require an Ook API token:
[sphinx.linkcheck]
use_service = false
With use_service = false the built-in builder is selected outright and the service is never contacted, even when an OOK_TOKEN is set.
This differs from the automatic token fallback under the default use_service = true, where the builder uses the service when a token works and only falls back to the built-in in-process check when the OOK_TOKEN is missing or rejected.
service_url#
Optional
Base URL of the Ook API that hosts the link-check service.
Default is https://roundtable.lsst.cloud/ook.
poll_budget#
Optional
Maximum time, in seconds, to wait for link-check results from the service.
Default is 300.
If the budget is exhausted before the service completes the check, the build falls back to Sphinx’s built-in in-process linkcheck builder — or fails, if strict is true.
strict#
Optional
Whether genuine link-check service problems fail the build.
Default is false: when the service is unreachable or the poll_budget is exhausted, the builder reports the problem at the INFO log level and falls back to Sphinx’s built-in in-process linkcheck builder, whose own result then decides the exit status.
Nothing is skipped, so an outage doesn’t silently stop checking your links; it does mean a build during one takes as long as a full in-process link check, and broken links that check finds fail the build.
Set this to true to fail the build on the service problem itself instead, with no fallback:
[sphinx.linkcheck]
strict = true
Use it when a substitute check isn’t what you want — when the point of the build is that the service was consulted, or when you’d rather see an outage immediately than pay for the in-process check.
This setting only gates genuine service availability problems.
A missing or rejected OOK_TOKEN is not one of them: rather than failing, the builder falls back to Sphinx’s built-in in-process linkcheck builder in every mode (including under strict), so link checking still runs.
Links the service reports as broken always fail the build, regardless of this setting.
recheck_unverified#
Optional
Whether URLs the service couldn’t verify from its own vantage point are rechecked from the build’s.
Default is true.
Two of the service’s verdicts rest on evidence nobody actually obtained about the link:
Blocked URLs.
Some sites sit behind a bot-protection edge (typically Cloudflare) that answers the service’s requests with a 403 no matter how ordinary the request is.
The service can’t tell such a URL apart from one that’s genuinely refusing everyone, so it reports the URL as blocked: a caveat rather than a failure, never counted as broken.
URLs the service couldn’t reach at all.
When a request gets no response — a TLS chain the service can’t build, a connection the far end drops, a name it can’t resolve — the service reports the URL broken with no HTTP status code.
That verdict does fail the build, and it’s the one worth the most scrutiny: nothing about it is specific to the link rather than to the service’s own network.
A documentation build usually runs somewhere else entirely — a GitHub Actions runner the same site is happy to serve, with its own trust store and its own route — so the build can often settle what the service couldn’t. Documenteer rechecks exactly those URLs, and no others, from the machine running the build, sending the same request Sphinx’s built-in linkcheck builder would send. The checks are sequential with a short delay between them, so a handful of rechecks never arrives at a site as a burst.
A broken result that does carry a status code is never rechecked.
That’s a definite answer from the server itself — a 404 is a 404 from every vantage point — and a second opinion has no standing to overturn it.
What the build observes is merged into that same build’s report:
A URL the build resolves is reported
ok(orredirected, if it works only through a permanent redirect), and its bot-protection caveat, or the failure the service couldn’t reproduce, clears.A URL that answers the build with a definite failure (a
404, say) is reportedbroken, with the build’s own evidence — which fails the build, as any broken link does.A URL blocked from the build’s vantage point too keeps its
blockedstatus, its caveat, and the service’s own evidence: the recheck settled nothing, so nothing is rewritten.So does a URL that answers the build with nothing at all — a timeout, a connection reset, a DNS failure. Bot protection doesn’t always answer with a status code, and a runner’s network blip is not evidence about a link, so only a failure the server itself answered with is allowed to turn the service’s caveat into a build failure.
A URL the service couldn’t reach and the build can’t reach either stays
brokenand still fails the build. Two vantage points coming back empty-handed isn’t proof the link works; its detail line says both looked, so you can tell it apart from one nobody checked twice.
The linkcheck.json artifact reflects the merged view, and flags each result the build rechecked for itself with locally_rechecked.
The observations the build actually obtained are also contributed back to the service, so the next project to reference the URL benefits from them — see Contributing rechecked results back to the service, below.
Set this to false to skip the recheck, and the contribution along with it, and report the service’s verdict as-is:
[sphinx.linkcheck]
recheck_unverified = false
Contributing rechecked results back to the service#
A build that settles a URL the service could only report as blocked knows something the service can’t learn from its own vantage point.
Documenteer hands that knowledge back: whenever the recheck finds blocked URLs, the builder posts what it observed — successes and failures alike, since a URL that’s blocked from the runner too is evidence as well — to the check’s contributions endpoint on the Ook API.
Each contributed result carries the same evidence the recheck merged into this build’s report: the final status code, any redirect that was followed, and the error text when the request failed outright.
A URL that answered the build with nothing at all is one exception: a contribution is applied to state every other project’s build reads, so an observation the build wouldn’t apply to its own report — a timeout or a dropped connection settles nothing about a link — isn’t handed on as shared evidence either.
The other is a URL the service reported broken: the service only applies a contributed result to a URL its own stored state has as blocked, so an observation for one it reported broken would come back rejected, and Documenteer withholds it rather than sending it to be refused.
Those URLs are still rechecked, and what the build observes still informs this build’s own report.
A build whose links the service settled on its own has nothing to contribute, and doesn’t so much as mint a token — which is the overwhelmingly common case.
Contributions are attested with a GitHub Actions OIDC id token rather than a shared secret, so the service records the verified claims of the workflow run — the repository it ran in — as the provenance of every result it applies.
Documenteer mints that token with the configured service_url as its audience, which scopes it to one deployment: a token minted for a development Ook can’t be replayed against production, and there’s no separate audience setting to keep in sync.
The request also carries the same OOK_TOKEN bearer the rest of the link check uses; both are required.
Alongside the results it describes the run — the repository and run URL from the Actions environment, and the Documenteer version that made the observations — but those fields are advisory only, and the service takes the provenance it records from the token’s claims instead.
Contributing needs the id-token: write permission, because that’s what makes GitHub expose the OIDC token endpoint to the job:
jobs:
docs:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # contribute link-check results to Ook
steps:
# ...
Important
A reusable workflow can’t ask for a permission of its own: permissions come from the calling job, and a called workflow can only narrow them.
If your documentation build runs through a shared workflow, add permissions: id-token: write to the job in your own repository that calls it, and confirm with that workflow’s maintainers that its build job passes the permission through rather than narrowing it away.
Both sides have to be in place before a contribution can be attested.
A run that contributes says so in its build log, first from the recheck and then from the contribution:
Local recheck: 3 verified, 1 still blocked, 0 failing
Contributed 4 link-check results to lsst-sqre/documenteer (4 accepted, 0 rejected)
Nothing about a contribution can fail the build.
It improves somebody else’s future build, so it’s never allowed to cost this one — not even under strict, which gates service availability problems only.
Every way it can go wrong is reported at the INFO log level rather than as a warning, which is what keeps that promise for a build run with -W (warnings as errors), where a warning is a failure:
Where no id token can be minted, the local recheck still runs and still informs this build’s report, and only the contribution is skipped, with a note naming the
id-token: writepermission — because the absence looks identical whether the build is on a laptop, where there’s nothing to fix, or in a workflow that never asked for the permission, where there’s one line to add.The service applies a batch entry by entry, so an entry it declines (a URL that isn’t one of the check’s members, say, or one that isn’t
blockedbecause its own vantage point already settled it) is reported per URL with the service’s reason, and the rest of the batch still applies.A batch that can’t be delivered at all is retried for the failures the service documents as retryable — a
502while it can’t reach GitHub’s signing keys, and connection failures — up to three times after the first attempt, on a backoff that starts at half a second and doubles. If those attempts are exhausted, the builder reports it and moves on; the build’s exit status is unchanged. A response that would fail identically however often it’s sent, such as a422for a batch the service won’t accept, is reported the same way on its first response instead of being retried.
origin_base_url#
Optional
The origin base URL the links are submitted for: the full base URL of the published website (for example, https://documenteer.lsst.io).
The link-check service uses the origin to associate the submitted URLs with the website.
By default the origin is the project.base_url setting, so most guides don’t need to set this override.
The URL is normalized the way the service normalizes origins: the host is lowercased and any trailing slash is stripped.
[sphinx.linkcheck]
origin_base_url = "https://documenteer.lsst.io"