Serve a security contact at /.well-known: PGP key + security.txt - #108
Merged
Merged
Conversation
Tracked source at .well-known/, mirroring the served path; the build copies the whole directory, so a security.txt later needs no code change. It was sitting in site/, which is the gitignored build output that build() clears on every run — it would have been deleted before it ever deployed. The Worker serves /.well-known/* with max-age=86400 and no immutable flag: a key can be rotated or revoked, so a day is the ceiling, unlike an entry. .asc gets Content-Type: application/pgp-keys, which Pages would otherwise serve as a generic byte stream. Public key block only — S1Seven Security <security@s1seven.com>, RSA-4096, 25FF A4E5 240A E655 FEA3 9082 D57E 1BBD 422F DAD7, expires 2028-09-12. A test asserts the published bytes carry no private key. Closes #107
RFC 9116. Contact, Expires, Encryption pointing at the key added in #107, Preferred-Languages and Canonical. The worker serves .txt under .well-known/ as text/plain; charset=utf-8, which RFC 9116 requires, alongside the .asc rule. An expired security.txt is worse than none: tooling reads it as an unmonitored contact, and a static file rots without anyone noticing. So this is the ratchet rather than a calendar reminder — a validate check refuses the next PR when Expires is missing, unparseable, past, or more than a year out, and when Encryption names a key the repo does not ship. It is deliberately unnumbered: checks 1-6 are the entry rules and 7 is the CI two-yes gate; this one guards the repo's own contact, and it stays green on fixture trees, which carry no .well-known at all. Closes #109
This was referenced Sep 13, 2026
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.
Summary
Gives the repo a discoverable security contact, and makes it correct at the transport level.
.well-known/pgp-security.asc, mirroring the served path. It was insite/, which is the gitignored build output thatbuild()clears on every run — untracked, and deleted before it could ever deploy..well-known/directory, so a new file there needs no builder code.security.txtrode in on exactly that..well-known/security.txt(RFC 9116):Contact,Expires,Encryptionpointing at the key above,Preferred-Languages: en, de,Canonical. This is the file scanners actually look for; the key alone was only findable by guessing its name./.well-known/*getsCache-Control: public, max-age=86400— cacheable, and deliberately notimmutablelike an entry, because a key can be rotated or revoked and the contact expires..ascgetsContent-Type: application/pgp-keys,.txtgetstext/plain; charset=utf-8as RFC 9116 §3 requires; Pages would otherwise serve them as generic byte streams.Published key is the public block only:
S1Seven Security <security@s1seven.com>, RSA-4096, fingerprint25FF A4E5 240A E655 FEA3 9082 D57E 1BBD 422F DAD7, expires 2028-09-12.The expiry ratchet
An expired
security.txtis worse than none — tooling reads it as an unmonitored contact — and a static file rots silently. So renewal is a check, not a calendar reminder: validate fails whenExpiresis missing, unparseable, past, or more than a year out, or whenEncryptionnames a key the repo does not ship.It is deliberately unnumbered. Checks 1–6 are the entry rules and check 7 is the CI two-yes gate; renumbering would touch the skills,
pr-checks.ymland the plan for a check that guards the repo's own contact rather than the dictionary. It stays green on fixture trees, which carry no.well-knownat all.Renew it in place —
.well-known/is not underpublished/, so no immutability rule applies.Closes #107
Closes #109
Test plan
npm run validate— OK, includingsecurity.txt — RFC 9116 freshnessnpm test— 80/80, coverage 97.05% lineimmutableis absent, that other.well-knownresources keep the origin's type, and that.asc/.txtelsewhere are not special-casedsecurity.txtlands with itsCanonicalline intactEncryptioncases each fail with the right message; absent file is green