0
2
Anyone reads what grain's labels mean, as a Bluesky labeler says it
Closed Approved
This adds a public social.grain.moderation.describeLabels query. It returns policies (labelValues with !takedown first, then each entry of labels::DEFINITIONS, plus labelValueDefinitions) and did when the server has one. I found nothing that has to change.
- Output shape:
Definition::json()already emitscom.atproto.label.defs#labelValueDefinition(identifier, severity, blurs, defaultSetting, locales), and the new lexicon refsapp.bsky.labeler.defs#labelerPolicies, which is present inlexicons/. - Caching: the handler sets no headers of its own. It relies on the shared viewer layer, which marks a request with no asker as cacheable by anyone. The test checks this.
- Exposure: the handler touches no reports and no admin data. The method is public as intended.
- Tests and docs: the new test goes through
common, so the lexicon check covers the answer. It also checks that the answer equalslistLabelDefinitions, and thatdidis left out when the server has none. The README mapping is updated. - Naming:
describeLabelsundersocial.grain.moderationis fine. It keeps the old hatk name, which makes the migration table easy to read. - Not run: I did not read CI results, so the coverage gate and clippy are unchecked from my side.
Landed on feature/rust in batch/three (…/3mzbdo7hmjf2b, 236806f), with its head 4de8a68 unchanged.
What changes
social.grain.moderation.describeLabels, a public query with no auth, answers the labels grain-serve gives out and what each one means, in the shape a Bluesky labeler'sapp.bsky.labeler.servicerecord uses andapp.bsky.labeler.getServicesreturns:policiesis anapp.bsky.labeler.defs#labelerPolicies. ItslabelValuesare!takedownand every identifier ingrain::labels::DEFINITIONS, and itslabelValueDefinitionsare those definitions ascom.atproto.label.defs#labelValueDefinition. Beside it isdid, the DID the labels name assrc, which is absent on a server with no DID. Any cache may keep the answer, since it is the same for everyone who asks.social.grain.admin.listLabelDefinitionsstays admin-only and is unchanged.lexicons/README.mdnow mapsdev.hatk.describeLabelsto the new method and says how its shape differs:labelValueDefinitionsreplacesdefinitions.Why
The apps need label definitions to show a label: its name, and whether it blurs, warns or hides. Today only admins can read them. The old server answered them publicly through
dev.hatk.describeLabels.Every definition is public. Each one is a label that can stand on something anyone reads with
com.atproto.label.queryLabels,csaeincluded, so a client needs all of them. None is admin-internal. Only the reports behindcsaeare kept from view and from models, and this method touches no reports.Knotless: knotless has no counterpart to mirror. Its labeler (
crates/knotless-serve/src/moderation.rs) gives out only atproto's own!takedown, throughqueryLabelsandsubscribeLabels, and so defines no values of its own to describe. Knotless'sdefine_labelis for a repository's issue labels, not moderation. This pull follows Bluesky's labeler convention instead, as the brief asked, and sits beside thequeryLabelsgrain already mirrors from knotless.How it was checked
anyone_reads_what_each_label_meansincrates/grain-serve/tests/moderation.rs. It calls the method throughtests/common, soharness/answers.mjschecks the answer against the lexicon. It covers:!takedownfirst, then the definitions in order;listLabelDefinitionsgives an admin;didout.node harness/lexicons.mjs origin/feature/rustandgoat lex lintpass on the new schema.rustfmt --checkpasses on the changed files.Shortcomings
labelValueslists!takedownand grain's own definitions only.applyLabellets an admin give out any value, atproto's global ones such as!hideorpornincluded. A value outside the list goes out undeclared, and clients fall back to atproto's global definitions for it.web/src/lib/labels.ts), and nothing calls the new method yet.Review
A quick look. The handler is
describe_labelsincrates/grain-serve/src/moderation.rs. Issocial.grain.moderation.describeLabelsthe right name, or would you rather have one closer toapp.bsky.labeler.getServices?🤖 Generated with Claude Code