Publish the reporting route at the site, not only in the tree [#86] - #147
Merged
Conversation
SECURITY.md is read by somebody who already found the source. The reports this plan most wants are from people who found a problem in the published pages, and for them the site published nothing, so the obvious route was a public issue on the tracker, which is the one place a report of this kind must not go. The build now writes .well-known/security.txt, at the one path a person looking for it already knows. It names the route the security policy writes down, the policy itself, and the date after which the file should not be trusted. The expiry is the part that goes wrong by sitting still, and an expiry that has passed is worse than no file, because it tells a finder the route was abandoned. So it is not typed. It is a duration added to the day the route was last confirmed, which is data in the tree, and the day the sum falls into the past a row over the produced output refuses it. A build that pushed the expiry forward on every run would keep the file valid forever without anybody ever looking at the route again, which is the failure the field exists against. Nothing in the build reads the clock. What it writes is derived from the tree, so two builds of one commit are the same bytes; whether a date has passed is a question about the world and is asked by the check that reads the output. That is the one row in the table that reads a clock and it says so. The file is produced rather than committed, because a committed file describing generated output is a second copy that drifts from it, and this one carries a date that would drift first. It carries no preferred language. Which language this site publishes in is open on the tracker, and a field answering it here would settle it in the one place nobody would look for the answer. It repeats nothing about the state of the route either: the policy is where that is written, with the command that reads it, so the two cannot disagree. Signed-off-by: Nils Lehnen <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
The site published no route for a security report:
Run 2026-08-11.
SECURITY.mdis read by somebody who already found the source.A person who found a problem in the pages had one obvious route, a public issue
on the tracker, and that is the one place a report of this kind must not go.
What this does
The build writes
.well-known/security.txtinto the output. It names the routethe security policy writes down, the policy itself, and the date after which the
file should not be trusted:
The expiry is the part of this file that goes wrong by sitting still, and one
that has passed is worse than no file, because it tells a finder the route was
abandoned. So it is not typed. It is a duration added to the day the route was
last confirmed, which is data in
data/security-contact.json, and a producedfile whose expiry has passed is refused by a row over the output. A build that
pushed the expiry forward on every run would keep the file valid forever without
anybody ever looking at the route again, which is the failure the field exists
against. Moving the day forward is a change somebody makes, and it is the act
this arrangement is built to force.
Nothing in the build reads the clock. What it writes is derived from the tree,
so two builds of one commit are the same bytes:
Whether a date has passed is a question about the world rather than about the
tree, so it is asked by the row that reads the output. That is the one row in
the table that reads a clock, and it says so where it is written.
The file repeats nothing about the state of the route.
SECURITY.mdsays whatthe route is and that the form does not answer today, with the command that
reads that, and this file points at it rather than carrying a second copy that
could disagree in either direction.
It carries no preferred language. Which language this site publishes in is entry
2 of #7 and is open, and a field answering it here would settle it in the one
place nobody would look for the answer.
Closes
Closes #86.
Every clause of the done-when, in order. The build writes the path. The file
names the route from #59, the policy and an expiry. The expiry is a duration
added to a date the build is given, shown by a case that moves that date and
sees only that field move. A produced file whose expiry has passed reds the gate
and the failure names the date. And the path answers on a served build, asked
for over loopback from a server holding nothing but what the build wrote, with a
neighbour asking for a path nothing wrote so the case is about the path rather
than about a server handing out one file for everything.
What was run
Two produced files where there was one, and seventeen decided rows where there
were sixteen. The one still owed is
image-dimensions-match-the-file, whichwaits on the first image the build writes and belongs to #69.
All run 2026-08-11.
The guards, deleted and watched go red
Each one on its own, with the rest of the tree left alone:
The near miss the expiry row has to get right is the file that is not its
subject. It reads the produced output, where most files carry no expiry at all,
so a row that refused everything it could not parse would refuse every page. The
three answers are told apart in the suite: no expiry, an expiry ahead, and an
expiry that has passed, plus a fourth for an expiry that is not a moment.
No test was skipped. The one case that serves the output binds loopback through
the toolchain's own test server and asks for the path over it; nothing here
needs a display, a browser, elevation or the public network.
The means
Go and the packages that already exist, plus one small JSON file in the tree for
the two addresses and the date. JSON because it is read by a program and edited
rarely, which is the reason the roster schema gives for the same choice, and
because the alternative is three values parsed out of prose.
The file format is RFC 9116, which is not a choice this repository makes: the
value of the file is that it sits at a fixed path in a shape a finder's tooling
already reads, so inventing anything here would defeat what it is for.
The rule about the expiry is a row in the existing invariant table, decided by
the same verb as every other row and reported under the same check name, which
is what makes it provable by the suite and runnable on the machine where the
mistake was made.
Who read it
Nobody else has read this. The ruleset requires no approving review, so this is
merged by whoever opened it, and the evidence above stands in place of a second
reader.