~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

The sitemap names the profiles and galleries a search engine may show

Closed
Closed chadtmiller.com asked to merge into feature/rust from grain.social/grain:gaps/sitemap

What changes

social.grain.server.getSitemap answers a page of the paths a search engine may index, for the web's /sitemap.xml and /sitemap/<kind>-<page>.xml (read/sitemap.rs): kind is galleries (the latest first, by when each was made or first indexed, whichever is earlier, as the feeds order them) or profiles (by DID), page from 0, 10,000 entries a page, and pageCount. Each entry is a path named by the account's handle where it has a valid one, and its DID where not, with lastmod, in UTC: a gallery's updatedAt, or its createdAt; 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.txt keeps crawlers to them), so this follows grain's reads and moderation filters.

Differences from the old server

  • Galleries run by sort_at (the earlier of made and first indexed), which an index serves, rather than createdAt alone, so a clock set ahead does not move one up.
  • A page is not kept an hour: each call asks the index. The pages' queries read by index; the counts read every gallery, as a sitemap names them all.
  • kind other than the two, and a page that is not a whole number from 0, are refused (400), where the old server read them as galleries and 0.
  • lastmod is written by SQLite in UTC to the millisecond; the old server wrote JavaScript's toISOString, the same text.

How it was checked

crates/grain-serve/tests/sitemap.rs: galleries with a photo listed in order with their lastmod (an updatedAt with 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

  • No answer is cached. A crawler reading every page counts every gallery each time; at grain's size that is milliseconds, and a cache can come when it is not.

🤖 Generated with Claude Code

Approved an earlier version

This 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.

Approved an earlier version

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.

Approved

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.