server: A login with no Bluesky profile makes a bare grain profile
MergedThis makes a login with neither a grain profile nor a Bluesky profile create a bare grain profile. The bare profile holds $type and createdAt, built with from_bluesky(&json!({}), None). The other cases keep their behavior: a named profile returns Named, an unnamed profile with no Bluesky profile returns NoBluesky, and a Bluesky profile seeds as before. The test covers the new case, including that tracks becomes true and that a second seeding is a no-op. I did not read the CI results. One nit: the module doc comment now has an overlong line, grain profile and no Bluesky profile is left as it is. Both are read from…, which is worth rewrapping.
The pull makes a login with neither a grain profile nor a Bluesky profile create a bare grain profile. The only change since my last review is a formatting commit on the test. The logic is as I read it before: (None, Some(_)) returns NoBluesky, (None, None) makes a bare profile, and a Bluesky profile seeds as before. The test covers the new case, including that the account becomes tracked and that seeding again changes nothing. The CI checks for this commit are still queued, so I haven't seen results. The doc-comment line I flagged last round, "grain profile and no Bluesky profile … Both are read from the account's PDS, which has", is still overlong at crates/grain-serve/src/login/profile.rs:14. Rewrap it if you want, but it shouldn't block the merge.
- server: A login with no Bluesky profile makes a bare grain profile
- main

What changes
A web login always leaves the account with a grain profile. When an account has neither a grain profile nor a Bluesky profile, the login's seeding (
crates/grain-serve/src/login/profile.rs) now makes a baresocial.grain.actor.profileatself, holding only itscreatedAt. Before, it made nothing.The other cases are unchanged:
Seeded::NoBluesky).Why
grain-serve starts tracking an account only once it writes a grain profile or a group declaration, and drops the rest of an untracked account's records. The login seeding copied old grain's
on-loginhook, which writes nothing when there's noapp.bsky.actor.profile. So an account with no Bluesky profile logs in, gets no grain profile, and everything it posts passes by.That's hellsangel81.selfhosted.social (
did:plc:5eswzrytpngfgviqyyfixtex). Its repo holds one story and nothing else. grain.social shows the story, because hatk indexes any grain record. next.grain.social has no row for the account at all.The other fix considered was to track an account on any
social.grain.*write. We kept the profile as what makes an account tracked, and made sure every login writes one. The iOS and Android apps sign in at the account's PDS, so grain-serve never sees their logins; they get the same rule in pulls of their own.How it was checked
a_login_seeds_a_grain_profile_from_blueskynow covers the case where an account has neither profile. The login makes a bare profile, holding only$typeand a validcreatedAt. The account is then tracked, and seeding again returnsNoBlueskyand leaves the profile as it is. Built and tested on CI.Shortcomings
An account that already slipped through, like hellsangel81, is fixed only by its next web login, or by the app once the app pulls land. Until then next doesn't show it, while grain.social does.
🤖 Generated with Claude Code