Skip to content

WIP: Add the previousURLs key - #392

Draft
lsmith77 wants to merge 4 commits into
publiccodeyml:mainfrom
lsmith77:previous-urls
Draft

lsmith77 wants to merge 4 commits into
publiccodeyml:mainfrom
lsmith77:previous-urls

Conversation

@lsmith77

@lsmith77 lsmith77 commented Oct 30, 2026 •

Copy link
Copy Markdown

Work in progress, opened for review of the approach rather than for a vote. Builds on #391 (mirrors), which should be merged first, and reuses its rules for comparing and fetching URLs. Until #391 is merged this PR also shows its commits; to review only this PR's changes, see the compare view.

The motivating case is public sector code moving from GitHub or GitLab to a national forge such as code.overheid.nl, as the Dutch registry expects in #293. Today such a move breaks every reference to the old url, and catalogs keep a stale entry next to the new one. This PR lets the project list its earlier locations, so catalogs can resolve those references and merge the old entry into the current one. The key is optional; parsers that do not know it see a new project, as today.

What an entry means. An entry says the software used to live there, and only counts once the old location agrees. It does not say the code is unchanged, give the new location anything beyond what a catalog already attaches to url, or apply to forks: a fork that continues a project without its authors is a variant, with its own url and isBasedOn.

Security

previousURLs moves an old location's history onto the new one, so it is the main target for takeover. The central rule: only the old location confirms a move, through its own url (its last known file names the new location) or a redirect there. Confirmation flows forward: each old location confirms only the one move it made, the whole chain must be confirmed, and later changes at an old location do not affect a confirmed move. Most cases below follow from this.

Attack Mitigation
A project claims a live project's URL. The live file still names itself, so nothing is confirmed.
An endorsed mirror's operator claims to be the successor. Being listed under mirrors is not consent to a move.
Several projects claim the same old location, to take it or block a real move. A file has one url, so only one claim can be confirmed; others are ignored.
A project claims a location that is gone. Without a last known file or redirect pointing to it, the claim stays unconfirmed.
Someone takes over the old location later (repojacking, a compromised old organisation) to undo or redirect a move. Later changes at a confirmed old location are ignored. Only the new location can confirm a further move.
A chain hides an unconfirmable link, such as [A, B] where A moved to B but B is live elsewhere. An entry counts only if every entry after it is confirmed.

False claims change nothing and block nothing.

Edge cases

  • A move in progress can take days. Parsers keep existing entries and check again later; authors shorten it by changing both urls together.
  • A rename on the forge: parsers record the redirect, which may later disappear, and a recorded redirect can confirm the move even without previousURLs.
  • The old location stays online as a mirror, the usual path when moving to a national forge while keeping the GitHub copy: it copies the new file, so its url confirms the move.
  • The old location is archived: a last commit setting its url to the new location confirms the move.
  • Several moves (A → B → C): each entry is checked against the next, hence oldest first.
  • A variant's isBasedOn points to an upstream that has moved: previousURLs lets it resolve, without vouching for the variant's claim.
  • Moving back: confirmed by the location the project moved to, not by the old one.

Trade-offs and open concerns

These are why the PR is a draft:

  • Behavioural rules for catalogs. The rules assume crawl history and say when catalogs may merge entries. Apart from organisation/uri's out-of-band verification, the standard has not prescribed catalog behaviour before. Should these rules be normative, or a non-normative guide for catalogs?
  • Moves depend on one commit on the old side. Authors who forget it, or whose old forge disappears first, end up with a move nobody can confirm automatically. Catalogs then have to merge by hand.
  • Shutting down too early. Because the whole chain must be confirmed, a location shut down before catalogs have crawled its forward pointer breaks the confirmation of every earlier location as well. Authors have to wait for the catalogs that matter to catch up, and nothing in the file tells them when that has happened; a catalog-side status, such as showing a confirmed move on the entry, would help.
  • The rename window. A forge redirect usually confirms a rename, but only if a crawler sees it before someone reuses the old name. Until then, whoever takes the name controls the old entry. That risk exists today; the PR narrows it but does not close it.
  • Trust that cannot be checked. Where the old location has no file, the forge operator's redirect is trusted. A lapsed domain lets its new owner confirm a move to their own copy, and nothing in the file tells that apart from the real owner. Catalogs can treat a location that returns from an outage and points somewhere new as suspicious, but that is policy, not a rule.
  • Complexity. Every review round so far found new interactions between the rules. Before this is voted on, it would help to implement it in a crawler and run it against real moves. Further adversarial reading is very welcome.

Possible follow-ups: a trusted archive such as Software Heritage as the source of an old location's last known file; naming a successor in advance for forges that disappear without warning.

Rationale: Sanctioned Mirror Declarations.

A repository renamed or transferred on its code hosting platform keeps
answering at the old path through a redirect. Comparing url without
following it makes such a project look like a technical fork of itself.
Lets a project declare the repository locations it endorses, so parsers
can tell its own mirrors from technical forks. The key is read only from
the publiccode.yml at url, and a listed repository counts only if its
own publiccode.yml points back, so neither a clone nor the listing
project can claim a location on its own.

Refs publiccodeyml#293
@vercel

vercel Bot commented Oct 30, 2026

Copy link
Copy Markdown

@lsmith77 is attempting to deploy a commit to the bfabio's projects Team on Vercel.

A member of the Team first needs to authorize it.

Lets a project list the earlier values of url, so catalogs can resolve
references made against an old location and merge the old entry into
the current one. Only the old location can confirm a move, through its
own url or a redirect, and the whole chain of moves must hold. Listing a
location under mirrors is not consent to a move.

Refs publiccodeyml#293
Because the whole chain of moves must be confirmed, a location shut down
before catalogs have seen its forward pointer breaks the confirmation of
every earlier location as well.
@lsmith77
lsmith77 marked this pull request as draft October 30, 2026 19:35

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant