Consumer-conversion census

Generated by labelwatch

Consumer-conversion census

Question this answers: Are any production Bluesky clients converting third-party labelers into default visibility behavior without explicit user adoption?

Short answer (as of 2026-06-08, sampled corpus): No. Every sampled client either hardcodes only moderation.bsky.app (did:plc:ar7c4by46qjdydhdevvrndac) or inherits the appview's defaults transparently. None hardcodes any third-party labeler DID as a default subscription.

Therefore: the Bundle G opt-in-consumer-adoption machinery is fire code for a building not yet built — defensible, not urgent. The wildfire-perimeter framing in F-001/F-004 was inflated. The discipline still holds; the urgency does not.

Method

For each client, search the public source for:

pattern what it would mean
BSKY_LABELER_DID / moderation.bsky.app / did:plc:ar7c4by46qjdydhdevvrndac the official default labeler is referenced/hardcoded
other did:plc: string in a labeler-config context a third-party labeler is hardcoded as default
labelersPref / app.bsky.actor.defs#labelersPref / readLabelers user-chosen labelers are read from app.bsky.actor.getPreferences
appLabelers / BskyAgent.configure({appLabelers}) the SDK is configured with a specific labeler set
queryLabels / com.atproto.label.queryLabels labels are pulled from labeler endpoints
app.bsky.labeler.service the client reads labeler service records

For each match, distinguish: hardcoded default vs user-chosen vs test-only.

Sampled via gh api search/code (where available; rate-limited) and WebFetch of raw files (where direct paths were guessable). Seven clients/SDKs sampled. The Bluesky showcase lists ~47 client-shaped projects; this is a focused-sample audit, not exhaustive.

Findings table

client source hardcoded official labeler hardcoded third-party labeler reads user labelersPref applies hide/warn/badge evidence path
bluesky-social/social-app (official iOS/Android/web) public YES — imports BSKY_LABELER_DID from @atproto/api; passes via BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]}) NO YES — readLabelers(account.did) from persistent storage; entries appended to appLabelers (excludes default to avoid double-subscribe) YES — full moderation pipeline src/lib/constants.ts, src/state/session/moderation.ts, src/lib/moderation/useModerationCauseDescription.ts
bluesky-social/atproto@atproto/api (SDK) public YES — export const BSKY_LABELER_DID = 'did:plc:ar7c4by46qjdydhdevvrndac' is the ONLY content of packages/api/src/const.ts NO N/A (SDK; provides primitives, not a client) YES — provides decideLabelModeration, getModerationUI etc. via packages/api/src/moderation/ packages/api/src/const.ts, packages/api/src/moderation/const/labels.ts (8-entry global LABELS map: !hide, !warn, !no-unauthenticated, porn, sexual, nudity, graphic-media, gore)
mimonelu/klearsky (web, Vue.js, popular) public YES — OFFICIAL_LABELER_DID = "did:plc:ar7c4by46qjdydhdevvrndac" in src/consts/consts.json; UI prevents un-subscribing from it; prepended if missing from labelersPref NO YES — reads app.bsky.actor.defs#labelersPref from currentPreferences YES — subscribe/unsubscribe UI, full labeler settings popup src/consts/consts.json, src/composables/main-state/my-labeler.ts
mozzius/graysky (iOS/Android, popular) public NO explicit hardcoding NO not visibly — no labelersPref or appLabelers references found in apps/expo/src/lib/agent.tsx; uses new AtpAgent({service: "https://public.api.bsky.app"}) with no labeler configuration inherits whatever the appview applies apps/expo/src/lib/agent.tsx (sparse; no moderation directory under src/lib/)
pdelfan/ouranos (Next.js web) public no obvious config NO not visibly — no moderation directory or labeler config surfaced in src/ inherits SDK defaults src/ (no moderation/labeler subdirectory found)
mary-ext/langit → Skeetdeck (deck-style web) public NO — app/api/moderation/ contains its own moderation primitives + a GLOBAL_LABELS map; labelers are passed in by callers NO not surfaced in service.ts (callers responsible) YES — own moderation primitives (preference/blur/severity enums, decideLabelModeration, getModerationUI) app/api/moderation/index.ts, app/api/moderation/service.ts
ioriayane/Hagoromo (Qt/C++ desktop) public delegated to ConfigurableLabels (not directly hardcoded in labelerprovider.cpp) NO retrieves via m_labels.labelerDids() — caller-configured YES — app/qtquick/moderation/labelerlistmodel.cpp plus labeler list UI lib/tools/labelerprovider.cpp, lib/tools/configurablelabels.h, app/qtquick/moderation/

What the table actually says

What this falsifies

What this does NOT falsify

Sampling gaps and honest caveats

Next moves the census enables

  1. De-emphasize the wildfire framing in F-001/F-004. Update those findings' status to note the census result: the opt-in machinery is correct but its real-world urgency is low.
  2. Quietly retain Bundle G machinery. Don't tear it out; it correctly models a possibility the protocol allows, and the Driftwatch opt-in remains a real specimen demonstrating the distinction. But stop motivating new work with "what if a client does X" until a real client does X.
  3. Re-probe periodically. If a client release adds hardcoded third-party labelers (or if the appview starts shipping additional defaults), the census needs a refresh. Cheap to re-run; ~1 hour for the sampled clients.
  4. Consider behavioral probes for closed-source clients. Subscribe a test account, load the client, observe whether labels from a known third-party labeler render. Outside the goblin-math scope but the only way to extend the census to closed clients.

Provenance