The sitemap names the profiles and galleries a search engine may show
ClosedThis adds social.grain.server.getSitemap with pages of galleries and profiles. It filters on author shown, no grain label on the gallery or its author, no self-label, and at least one photo. I found nothing that must change. The filters reuse the existing shown and unlabeled helpers. Bad kind and page values are refused with 400. The offset arithmetic saturates, and the page count is correct. The test covers the cases the description lists, and the plan check covers the new queries.
One small point, not blocking. A profile's lastmod is max(g.created_at) over the raw column, while a gallery's lastmod is coalesce(updated_at, created_at). max compares strings, so a createdAt written with a UTC offset can pick the wrong "latest" gallery. Using sort_at, or max over the normalized value, would make the two consistent.
Carried forward: the change this pull makes at 852e9b181d0de1cdc137510a7f2ac93f20b98602 is the one reviewed at ab3fa56b50cb7d8511b9ffdf72f5899e8968c244, so that review stands. What is new comes from its base.
This adds social.grain.server.getSitemap, with pages of galleries and profiles. A gallery is listed only if its author is shown, no grain label stands on it or its author, it has no self-label, and it has a photo. A profile also needs an unlabelled profile record.
My earlier point is fixed. A profile's lastmod is now max over strftime(..., coalesce(updated_at, created_at)), the same UTC-normalized value a gallery uses, so an offset in createdAt or updatedAt can't pick the wrong latest gallery. The test checks this: alice's +02:00 updatedAt comes out as 07:00:00.000Z in both the gallery entry and the profile entry.
I found nothing else that must change. Bad kind and page values are refused with 400, the offset saturates, and pageCount rounds up correctly. The test covers each case the description lists, and the plan check includes both page queries.
Profile lastmod: fixed at 66ad04c. It is now max over each gallery's own lastmod normalized to UTC (strftime of coalesce(updated_at, created_at)), so it agrees with the galleries' sitemap and an offset cannot pick the wrong one.
Landed on feature/rust at fc5f686 through the batch pull Seven reviewed pulls land on feature/rust together, checked once, checked there as a whole. This pull's head is in feature/rust's history.
What changes
social.grain.server.getSitemapanswers a page of the paths a search engine may index, for the web's/sitemap.xmland/sitemap/<kind>-<page>.xml(read/sitemap.rs):kindisgalleries(the latest first, by when each was made or first indexed, whichever is earlier, as the feeds order them) orprofiles(by DID),pagefrom 0, 10,000 entries a page, andpageCount. Each entry is a path named by the account's handle where it has a valid one, and its DID where not, withlastmod, in UTC: a gallery'supdatedAt, or itscreatedAt; a profile's latest listed gallery.A search engine can neither blur nor warn, so a gallery is listed only where its author is shown, no label of grain's stands on it or on its author (
unlabeled, whatever the label says), its author gave it no self-label, and it has a photo. A profile is listed where it has such a gallery and no label stands on its profile record. What the index holds is what the network serves, so nothing in a space is listed.One of the pulls closing what the clients call and grain-serve did not serve; the table of them all is in
gaps/graph's body (Accounts mute and unmute, and list whom they block and mute).Why
The web's sitemap routes call it on the server, and grain-serve answered 501. knotless has no sitemap (its pages are found by their links, and
robots.txtkeeps crawlers to them), so this follows grain's reads and moderation filters.Differences from the old server
sort_at(the earlier of made and first indexed), which an index serves, rather thancreatedAtalone, so a clock set ahead does not move one up.kindother than the two, and apagethat is not a whole number from 0, are refused (400), where the old server read them asgalleriesand 0.lastmodis written by SQLite in UTC to the millisecond; the old server wrote JavaScript'stoISOString, the same text.How it was checked
crates/grain-serve/tests/sitemap.rs: galleries with a photo listed in order with theirlastmod(anupdatedAtwith an offset written in UTC), one without a photo, one labelled, one self-labelled, an author labelled and one taken down left out, an account with no handle named by DID; profiles listed and one whose record is labelled left out; past the last page empty; a bad kind or page refused. The plan check covers the pages' queries. Run here once (it needs no PDS); the rest is the runner's.Shortcomings
🤖 Generated with Claude Code