The web's Bluesky cross-post goes through grain-serve, and a test says so
ClosedThis adds crates/grain-serve/tests/cross_post.rs. It covers the web's Bluesky cross-post through social.grain.repo.*: gallery create, uploadBlob, an app.bsky.feed.post with facets, tags and an images embed, a PDS readback, getGallery's crossPost.url, a putRecord edit, and a 3001-character post refused with InvalidRecord. It changes no server code. The check, rust, test and harness workflows all passed at this head, which covers fmt, clippy, the coverage gate and the answers check. I found nothing that must change. The deleteRecord gap is stated in the body and is minor.
Carried forward: the change this pull makes at 42065d02bf4c21aed1810b84ab7ab6f3b7daa463 is the one reviewed at eea1c93b56741d723818a141a86b0b35b656ead2, so that review stands. What is new comes from its base.
Carried forward: the change this pull makes at 521183a02db91002f3f22393ac5a72b2ee076bee is the one reviewed at 42065d02bf4c21aed1810b84ab7ab6f3b7daa463, so that review stands. What is new comes from its base.
Carried forward: the change this pull makes at a196eff0bed177c7d852c50dbb6eb43994f76466 is the one reviewed at 521183a02db91002f3f22393ac5a72b2ee076bee, so that review stands. What is new comes from its base.
Landed on feature/rust at 7d031dc through the batch pull The web proxy, deletes and the cross-post test land together, checked once, checked there as a whole. This pull's head is in feature/rust's history.
What changes
Nothing in grain-serve: a test,
crates/grain-serve/tests/cross_post.rs, that the Bluesky cross-post works through it as the web makes one. A browser logged in through the site writes a gallery, uploads a picture withsocial.grain.repo.uploadBlob, and writes anapp.bsky.feed.postwithsocial.grain.repo.createRecord: text with tag and link facets,tags, and anapp.bsky.embed.imagesembed of the blob. The post is at the account's PDS, andgetGallerynames it straight after,crossPost.urlbeinghttps://bsky.app/profile/<did>/post/<rkey>.putRecordat the same key edits it, as the web's edit flow does, and a post of 3001 characters is refusedInvalidRecord.Why
The web pull said grain-serve refused the cross-post. It does not:
social.grain.repo.*writes any collection the index takes, checked against its lexicon, and the index takesapp.bsky.feed.post(thebsky_posttable, which is how a gallery knows its cross-post). The test holds that in place.What the old server allowed, for the record: its
dev.hatk.createRecord,putRecordandapplyWritestook any collection it had a lexicon for, limited by the session's OAuth scope, and the only Bluesky collection in grain's scope isapp.bsky.feed.post, for create and update. grain-serve takesapp.bsky.feed.postand no other Bluesky collection, writes it at the account's own PDS with its session, checks the request comes from a page of the site (login::same_site) and thatrepois the session's own, as for grain's collections.How it was checked
The test, on the runner at this head with the others (
check,rust,test,harnesspassed). knotless writes nothing of Bluesky's, so there is nothing of its to follow here.Shortcomings
deleteRecordof the post is not in the test; it goes through the same path as any other collection's.dev.hatk.createRecord, which grain-serve does not serve by design: they write the post at their own PDS and callsocial.grain.sync.indexRecords.🤖 Generated with Claude Code