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

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

Anyone reads what grain's labels mean, as a Bluesky labeler says it

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

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's app.bsky.labeler.service record uses and app.bsky.labeler.getServices returns: policies is an app.bsky.labeler.defs#labelerPolicies. Its labelValues are !takedown and every identifier in grain::labels::DEFINITIONS, and its labelValueDefinitions are those definitions as com.atproto.label.defs#labelValueDefinition. Beside it is did, the DID the labels name as src, 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.listLabelDefinitions stays admin-only and is unchanged.

lexicons/README.md now maps dev.hatk.describeLabels to the new method and says how its shape differs: labelValueDefinitions replaces definitions.

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, csae included, so a client needs all of them. None is admin-internal. Only the reports behind csae are 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, through queryLabels and subscribeLabels, and so defines no values of its own to describe. Knotless's define_label is for a repository's issue labels, not moderation. This pull follows Bluesky's labeler convention instead, as the brief asked, and sits beside the queryLabels grain already mirrors from knotless.

How it was checked

  • Added anyone_reads_what_each_label_means in crates/grain-serve/tests/moderation.rs. It calls the method through tests/common, so harness/answers.mjs checks the answer against the lexicon. It covers:
    • the answer with no token and no cache-control;
    • !takedown first, then the definitions in order;
    • the definitions equal to what listLabelDefinitions gives an admin;
    • a server with no DID leaving did out.
  • node harness/lexicons.mjs origin/feature/rust and goat lex lint pass on the new schema.
  • rustfmt --check passes on the changed files.
  • Building, the tests, clippy and coverage are left to CI.

Shortcomings

  • labelValues lists !takedown and grain's own definitions only. applyLabel lets an admin give out any value, atproto's global ones such as !hide or porn included. A value outside the list goes out undeclared, and clients fall back to atproto's global definitions for it.
  • The web still reads its own copy of the definitions (web/src/lib/labels.ts), and nothing calls the new method yet.

Review

A quick look. The handler is describe_labels in crates/grain-serve/src/moderation.rs. Is social.grain.moderation.describeLabels the right name, or would you rather have one closer to app.bsky.labeler.getServices?

🤖 Generated with Claude Code

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 emits com.atproto.label.defs#labelValueDefinition (identifier, severity, blurs, defaultSetting, locales), and the new lexicon refs app.bsky.labeler.defs#labelerPolicies, which is present in lexicons/.
  • 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 equals listLabelDefinitions, and that did is left out when the server has none. The README mapping is updated.
  • Naming: describeLabels under social.grain.moderation is 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.