···11+/**
22+ * GENERATED CODE - DO NOT MODIFY
33+ */
44+import { type ValidationResult, BlobRef } from "npm:@atproto/lexicon"
55+import { CID } from "npm:multiformats/cid"
66+import { validate as _validate } from '../../../../lexicons.ts'
77+import {
88+ type $Typed,
99+ is$typed as _is$typed,
1010+ type OmitKey,
1111+} from '../../../../util.ts'
1212+1313+const is$typed = _is$typed,
1414+ validate = _validate
1515+const id = 'com.atproto.moderation.defs'
1616+/** Rude, harassing, explicit, or otherwise unwelcoming behavior */
1717+export const REASONRUDE = `${id}#reasonRude`
1818+/** Spam: frequent unwanted promotion, replies, mentions */
1919+export const REASONSPAM = `${id}#reasonSpam`
2020+2121+export type ReasonType =
2222+ | 'com.atproto.moderation.defs#reasonSpam'
2323+ | 'com.atproto.moderation.defs#reasonViolation'
2424+ | 'com.atproto.moderation.defs#reasonMisleading'
2525+ | 'com.atproto.moderation.defs#reasonSexual'
2626+ | 'com.atproto.moderation.defs#reasonRude'
2727+ | 'com.atproto.moderation.defs#reasonOther'
2828+ | 'com.atproto.moderation.defs#reasonAppeal'
2929+ | (string & {})
3030+3131+/** Other: reports not falling under another report category */
3232+export const REASONOTHER = `${id}#reasonOther`
3333+3434+/** Tag describing a type of subject that might be reported. */
3535+export type SubjectType = 'account' | 'record' | 'chat' | (string & {})
3636+3737+/** Appeal: appeal a previously taken moderation action */
3838+export const REASONAPPEAL = `${id}#reasonAppeal`
3939+/** Unwanted or mislabeled sexual content */
4040+export const REASONSEXUAL = `${id}#reasonSexual`
4141+/** Direct violation of server rules, laws, terms of service */
4242+export const REASONVIOLATION = `${id}#reasonViolation`
4343+/** Misleading identity, affiliation, or content */
4444+export const REASONMISLEADING = `${id}#reasonMisleading`
+2
__generated__/types/social/grain/actor/defs.ts
···99 is$typed as _is$typed,
1010 type OmitKey,
1111} from '../../../../util.ts'
1212+import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.ts'
12131314const is$typed = _is$typed,
1415 validate = _validate
···2021 handle: string
2122 displayName?: string
2223 description?: string
2424+ labels?: ComAtprotoLabelDefs.Label[]
2325 avatar?: string
2426 createdAt?: string
2527}
+2
__generated__/types/social/grain/gallery.ts
···55import { CID } from "npm:multiformats/cid"
66import { validate as _validate } from '../../../lexicons.ts'
77import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util.ts'
88+import type * as ComAtprotoLabelDefs from '../../com/atproto/label/defs.ts'
89910const is$typed = _is$typed,
1011 validate = _validate
···1415 $type: 'social.grain.gallery'
1516 title: string
1617 description?: string
1818+ labels?: $Typed<ComAtprotoLabelDefs.SelfLabels> | { $type: string }
1719 createdAt: string
1820 [k: string]: unknown
1921}
+2
__generated__/types/social/grain/gallery/defs.ts
···1111} from '../../../../util.ts'
1212import type * as SocialGrainActorDefs from '../actor/defs.ts'
1313import type * as SocialGrainPhotoDefs from '../photo/defs.ts'
1414+import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.ts'
14151516const is$typed = _is$typed,
1617 validate = _validate
···2324 creator: SocialGrainActorDefs.ProfileView
2425 record: { [_ in string]: unknown }
2526 items?: ($Typed<SocialGrainPhotoDefs.PhotoView> | { $type: string })[]
2727+ labels?: ComAtprotoLabelDefs.Label[]
2628 indexedAt: string
2729}
2830
+101
__generated__/types/social/grain/labeler/defs.ts
···11+/**
22+ * GENERATED CODE - DO NOT MODIFY
33+ */
44+import { type ValidationResult, BlobRef } from "npm:@atproto/lexicon"
55+import { CID } from "npm:multiformats/cid"
66+import { validate as _validate } from '../../../../lexicons.ts'
77+import {
88+ type $Typed,
99+ is$typed as _is$typed,
1010+ type OmitKey,
1111+} from '../../../../util.ts'
1212+import type * as SocialGrainActorDefs from '../actor/defs.ts'
1313+import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.ts'
1414+import type * as AppBskyActorDefs from '../../../app/bsky/actor/defs.ts'
1515+import type * as ComAtprotoModerationDefs from '../../../com/atproto/moderation/defs.ts'
1616+1717+const is$typed = _is$typed,
1818+ validate = _validate
1919+const id = 'social.grain.labeler.defs'
2020+2121+export interface LabelerView {
2222+ $type?: 'social.grain.labeler.defs#labelerView'
2323+ uri: string
2424+ cid: string
2525+ creator: SocialGrainActorDefs.ProfileView
2626+ favoriteCount?: number
2727+ viewer?: LabelerViewerState
2828+ indexedAt: string
2929+ labels?: ComAtprotoLabelDefs.Label[]
3030+}
3131+3232+const hashLabelerView = 'labelerView'
3333+3434+export function isLabelerView<V>(v: V) {
3535+ return is$typed(v, id, hashLabelerView)
3636+}
3737+3838+export function validateLabelerView<V>(v: V) {
3939+ return validate<LabelerView & V>(v, id, hashLabelerView)
4040+}
4141+4242+export interface LabelerViewDetailed {
4343+ $type?: 'social.grain.labeler.defs#labelerViewDetailed'
4444+ uri: string
4545+ cid: string
4646+ creator: AppBskyActorDefs.ProfileView
4747+ policies: SocialGrainActorDefs.LabelerPolicies
4848+ favoriteCount?: number
4949+ viewer?: LabelerViewerState
5050+ indexedAt: string
5151+ labels?: ComAtprotoLabelDefs.Label[]
5252+ /** The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed. */
5353+ reasonTypes?: ComAtprotoModerationDefs.ReasonType[]
5454+ /** The set of subject types (account, record, etc) this service accepts reports on. */
5555+ subjectTypes?: ComAtprotoModerationDefs.SubjectType[]
5656+ /** Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type. */
5757+ subjectCollections?: string[]
5858+}
5959+6060+const hashLabelerViewDetailed = 'labelerViewDetailed'
6161+6262+export function isLabelerViewDetailed<V>(v: V) {
6363+ return is$typed(v, id, hashLabelerViewDetailed)
6464+}
6565+6666+export function validateLabelerViewDetailed<V>(v: V) {
6767+ return validate<LabelerViewDetailed & V>(v, id, hashLabelerViewDetailed)
6868+}
6969+7070+export interface LabelerViewerState {
7171+ $type?: 'social.grain.labeler.defs#labelerViewerState'
7272+ like?: string
7373+}
7474+7575+const hashLabelerViewerState = 'labelerViewerState'
7676+7777+export function isLabelerViewerState<V>(v: V) {
7878+ return is$typed(v, id, hashLabelerViewerState)
7979+}
8080+8181+export function validateLabelerViewerState<V>(v: V) {
8282+ return validate<LabelerViewerState & V>(v, id, hashLabelerViewerState)
8383+}
8484+8585+export interface LabelerPolicies {
8686+ $type?: 'social.grain.labeler.defs#labelerPolicies'
8787+ /** The label values which this labeler publishes. May include global or custom labels. */
8888+ labelValues: ComAtprotoLabelDefs.LabelValue[]
8989+ /** Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler. */
9090+ labelValueDefinitions?: ComAtprotoLabelDefs.LabelValueDefinition[]
9191+}
9292+9393+const hashLabelerPolicies = 'labelerPolicies'
9494+9595+export function isLabelerPolicies<V>(v: V) {
9696+ return is$typed(v, id, hashLabelerPolicies)
9797+}
9898+9999+export function validateLabelerPolicies<V>(v: V) {
100100+ return validate<LabelerPolicies & V>(v, id, hashLabelerPolicies)
101101+}
···11+/**
22+ * GENERATED CODE - DO NOT MODIFY
33+ */
44+import { type ValidationResult, BlobRef } from "npm:@atproto/lexicon"
55+import { CID } from "npm:multiformats/cid"
66+import { validate as _validate } from '../../../../lexicons.ts'
77+import {
88+ type $Typed,
99+ is$typed as _is$typed,
1010+ type OmitKey,
1111+} from '../../../../util.ts'
1212+import type * as AppBskyLabelerDefs from '../../../app/bsky/labeler/defs.ts'
1313+import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.ts'
1414+import type * as ComAtprotoModerationDefs from '../../../com/atproto/moderation/defs.ts'
1515+1616+const is$typed = _is$typed,
1717+ validate = _validate
1818+const id = 'social.grain.labeler.service'
1919+2020+export interface Record {
2121+ $type: 'social.grain.labeler.service'
2222+ policies: AppBskyLabelerDefs.LabelerPolicies
2323+ labels?: $Typed<ComAtprotoLabelDefs.SelfLabels> | { $type: string }
2424+ createdAt: string
2525+ /** The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed. */
2626+ reasonTypes?: ComAtprotoModerationDefs.ReasonType[]
2727+ /** The set of subject types (account, record, etc) this service accepts reports on. */
2828+ subjectTypes?: ComAtprotoModerationDefs.SubjectType[]
2929+ /** Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type. */
3030+ subjectCollections?: string[]
3131+ [k: string]: unknown
3232+}
3333+3434+const hashRecord = 'main'
3535+3636+export function isRecord<V>(v: V) {
3737+ return is$typed(v, id, hashRecord)
3838+}
3939+4040+export function validateRecord<V>(v: V) {
4141+ return validate<Record & V>(v, id, hashRecord, true)
4242+}
···11+{
22+ "lexicon": 1,
33+ "id": "social.grain.labeler.defs",
44+ "defs": {
55+ "labelerView": {
66+ "type": "object",
77+ "required": ["uri", "cid", "creator", "indexedAt"],
88+ "properties": {
99+ "uri": { "type": "string", "format": "at-uri" },
1010+ "cid": { "type": "string", "format": "cid" },
1111+ "creator": {
1212+ "type": "ref",
1313+ "ref": "social.grain.actor.defs#profileView"
1414+ },
1515+ "favoriteCount": { "type": "integer", "minimum": 0 },
1616+ "viewer": { "type": "ref", "ref": "#labelerViewerState" },
1717+ "indexedAt": { "type": "string", "format": "datetime" },
1818+ "labels": {
1919+ "type": "array",
2020+ "items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
2121+ }
2222+ }
2323+ },
2424+ "labelerViewDetailed": {
2525+ "type": "object",
2626+ "required": ["uri", "cid", "creator", "policies", "indexedAt"],
2727+ "properties": {
2828+ "uri": { "type": "string", "format": "at-uri" },
2929+ "cid": { "type": "string", "format": "cid" },
3030+ "creator": { "type": "ref", "ref": "app.bsky.actor.defs#profileView" },
3131+ "policies": {
3232+ "type": "ref",
3333+ "ref": "social.grain.actor.defs#labelerPolicies"
3434+ },
3535+ "favoriteCount": { "type": "integer", "minimum": 0 },
3636+ "viewer": { "type": "ref", "ref": "#labelerViewerState" },
3737+ "indexedAt": { "type": "string", "format": "datetime" },
3838+ "labels": {
3939+ "type": "array",
4040+ "items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
4141+ },
4242+ "reasonTypes": {
4343+ "description": "The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed.",
4444+ "type": "array",
4545+ "items": {
4646+ "type": "ref",
4747+ "ref": "com.atproto.moderation.defs#reasonType"
4848+ }
4949+ },
5050+ "subjectTypes": {
5151+ "description": "The set of subject types (account, record, etc) this service accepts reports on.",
5252+ "type": "array",
5353+ "items": {
5454+ "type": "ref",
5555+ "ref": "com.atproto.moderation.defs#subjectType"
5656+ }
5757+ },
5858+ "subjectCollections": {
5959+ "type": "array",
6060+ "description": "Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type.",
6161+ "items": { "type": "string", "format": "nsid" }
6262+ }
6363+ }
6464+ },
6565+ "labelerViewerState": {
6666+ "type": "object",
6767+ "properties": {
6868+ "like": { "type": "string", "format": "at-uri" }
6969+ }
7070+ },
7171+ "labelerPolicies": {
7272+ "type": "object",
7373+ "required": ["labelValues"],
7474+ "properties": {
7575+ "labelValues": {
7676+ "type": "array",
7777+ "description": "The label values which this labeler publishes. May include global or custom labels.",
7878+ "items": {
7979+ "type": "ref",
8080+ "ref": "com.atproto.label.defs#labelValue"
8181+ }
8282+ },
8383+ "labelValueDefinitions": {
8484+ "type": "array",
8585+ "description": "Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler.",
8686+ "items": {
8787+ "type": "ref",
8888+ "ref": "com.atproto.label.defs#labelValueDefinition"
8989+ }
9090+ }
9191+ }
9292+ }
9393+ }
9494+}
+47
lexicons/social/grain/labelers/service.json
···11+{
22+ "lexicon": 1,
33+ "id": "social.grain.labeler.service",
44+ "defs": {
55+ "main": {
66+ "type": "record",
77+ "description": "A declaration of the existence of labeler service.",
88+ "key": "literal:self",
99+ "record": {
1010+ "type": "object",
1111+ "required": ["policies", "createdAt"],
1212+ "properties": {
1313+ "policies": {
1414+ "type": "ref",
1515+ "ref": "app.bsky.labeler.defs#labelerPolicies"
1616+ },
1717+ "labels": {
1818+ "type": "union",
1919+ "refs": ["com.atproto.label.defs#selfLabels"]
2020+ },
2121+ "createdAt": { "type": "string", "format": "datetime" },
2222+ "reasonTypes": {
2323+ "description": "The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed.",
2424+ "type": "array",
2525+ "items": {
2626+ "type": "ref",
2727+ "ref": "com.atproto.moderation.defs#reasonType"
2828+ }
2929+ },
3030+ "subjectTypes": {
3131+ "description": "The set of subject types (account, record, etc) this service accepts reports on.",
3232+ "type": "array",
3333+ "items": {
3434+ "type": "ref",
3535+ "ref": "com.atproto.moderation.defs#subjectType"
3636+ }
3737+ },
3838+ "subjectCollections": {
3939+ "type": "array",
4040+ "description": "Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type.",
4141+ "items": { "type": "string", "format": "nsid" }
4242+ }
4343+ }
4444+ }
4545+ }
4646+ }
4747+}
···11+/**
22+ * GENERATED CODE - DO NOT MODIFY
33+ */
44+import { type ValidationResult, BlobRef } from "npm:@atproto/lexicon"
55+import { CID } from "npm:multiformats/cid"
66+import { validate as _validate } from '../../../../lexicons.ts'
77+import {
88+ type $Typed,
99+ is$typed as _is$typed,
1010+ type OmitKey,
1111+} from '../../../../util.ts'
1212+1313+const is$typed = _is$typed,
1414+ validate = _validate
1515+const id = 'com.atproto.label.defs'
1616+1717+/** Metadata tag on an atproto resource (eg, repo or record). */
1818+export interface Label {
1919+ $type?: 'com.atproto.label.defs#label'
2020+ /** Optionally, CID specifying the specific version of 'uri' resource this label applies to. */
2121+ cid?: string
2222+ /** Timestamp when this label was created. */
2323+ cts: string
2424+ /** Timestamp at which this label expires (no longer applies). */
2525+ exp?: string
2626+ /** If true, this is a negation label, overwriting a previous label. */
2727+ neg?: boolean
2828+ /** Signature of dag-cbor encoded label. */
2929+ sig?: Uint8Array
3030+ /** DID of the actor who created this label. */
3131+ src: string
3232+ /** AT URI of the record, repository (account), or other resource that this label applies to. */
3333+ uri: string
3434+ /** The short string name of the value or type of this label. */
3535+ val: string
3636+ /** The AT Protocol version of the label object. */
3737+ ver?: number
3838+}
3939+4040+const hashLabel = 'label'
4141+4242+export function isLabel<V>(v: V) {
4343+ return is$typed(v, id, hashLabel)
4444+}
4545+4646+export function validateLabel<V>(v: V) {
4747+ return validate<Label & V>(v, id, hashLabel)
4848+}
4949+5050+/** Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel. */
5151+export interface SelfLabel {
5252+ $type?: 'com.atproto.label.defs#selfLabel'
5353+ /** The short string name of the value or type of this label. */
5454+ val: string
5555+}
5656+5757+const hashSelfLabel = 'selfLabel'
5858+5959+export function isSelfLabel<V>(v: V) {
6060+ return is$typed(v, id, hashSelfLabel)
6161+}
6262+6363+export function validateSelfLabel<V>(v: V) {
6464+ return validate<SelfLabel & V>(v, id, hashSelfLabel)
6565+}
6666+6767+export type LabelValue =
6868+ | '!hide'
6969+ | '!no-promote'
7070+ | '!warn'
7171+ | '!no-unauthenticated'
7272+ | 'dmca-violation'
7373+ | 'doxxing'
7474+ | 'porn'
7575+ | 'sexual'
7676+ | 'nudity'
7777+ | 'nsfl'
7878+ | 'gore'
7979+ | (string & {})
8080+8181+/** Metadata tags on an atproto record, published by the author within the record. */
8282+export interface SelfLabels {
8383+ $type?: 'com.atproto.label.defs#selfLabels'
8484+ values: SelfLabel[]
8585+}
8686+8787+const hashSelfLabels = 'selfLabels'
8888+8989+export function isSelfLabels<V>(v: V) {
9090+ return is$typed(v, id, hashSelfLabels)
9191+}
9292+9393+export function validateSelfLabels<V>(v: V) {
9494+ return validate<SelfLabels & V>(v, id, hashSelfLabels)
9595+}
9696+9797+/** Declares a label value and its expected interpretations and behaviors. */
9898+export interface LabelValueDefinition {
9999+ $type?: 'com.atproto.label.defs#labelValueDefinition'
100100+ /** What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. */
101101+ blurs: 'content' | 'media' | 'none' | (string & {})
102102+ locales: LabelValueDefinitionStrings[]
103103+ /** How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. */
104104+ severity: 'inform' | 'alert' | 'none' | (string & {})
105105+ /** Does the user need to have adult content enabled in order to configure this label? */
106106+ adultOnly?: boolean
107107+ /** The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). */
108108+ identifier: string
109109+ /** The default setting for this label. */
110110+ defaultSetting: 'ignore' | 'warn' | 'hide' | (string & {})
111111+}
112112+113113+const hashLabelValueDefinition = 'labelValueDefinition'
114114+115115+export function isLabelValueDefinition<V>(v: V) {
116116+ return is$typed(v, id, hashLabelValueDefinition)
117117+}
118118+119119+export function validateLabelValueDefinition<V>(v: V) {
120120+ return validate<LabelValueDefinition & V>(v, id, hashLabelValueDefinition)
121121+}
122122+123123+/** Strings which describe the label in the UI, localized into a specific language. */
124124+export interface LabelValueDefinitionStrings {
125125+ $type?: 'com.atproto.label.defs#labelValueDefinitionStrings'
126126+ /** The code of the language these strings are written in. */
127127+ lang: string
128128+ /** A short human-readable name for the label. */
129129+ name: string
130130+ /** A longer description of what the label means and why it might be applied. */
131131+ description: string
132132+}
133133+134134+const hashLabelValueDefinitionStrings = 'labelValueDefinitionStrings'
135135+136136+export function isLabelValueDefinitionStrings<V>(v: V) {
137137+ return is$typed(v, id, hashLabelValueDefinitionStrings)
138138+}
139139+140140+export function validateLabelValueDefinitionStrings<V>(v: V) {
141141+ return validate<LabelValueDefinitionStrings & V>(
142142+ v,
143143+ id,
144144+ hashLabelValueDefinitionStrings,
145145+ )
146146+}
···11+{
22+ "lexicon": 1,
33+ "id": "com.atproto.label.defs",
44+ "defs": {
55+ "label": {
66+ "type": "object",
77+ "required": [
88+ "src",
99+ "uri",
1010+ "val",
1111+ "cts"
1212+ ],
1313+ "properties": {
1414+ "cid": {
1515+ "type": "string",
1616+ "format": "cid",
1717+ "description": "Optionally, CID specifying the specific version of 'uri' resource this label applies to."
1818+ },
1919+ "cts": {
2020+ "type": "string",
2121+ "format": "datetime",
2222+ "description": "Timestamp when this label was created."
2323+ },
2424+ "exp": {
2525+ "type": "string",
2626+ "format": "datetime",
2727+ "description": "Timestamp at which this label expires (no longer applies)."
2828+ },
2929+ "neg": {
3030+ "type": "boolean",
3131+ "description": "If true, this is a negation label, overwriting a previous label."
3232+ },
3333+ "sig": {
3434+ "type": "bytes",
3535+ "description": "Signature of dag-cbor encoded label."
3636+ },
3737+ "src": {
3838+ "type": "string",
3939+ "format": "did",
4040+ "description": "DID of the actor who created this label."
4141+ },
4242+ "uri": {
4343+ "type": "string",
4444+ "format": "uri",
4545+ "description": "AT URI of the record, repository (account), or other resource that this label applies to."
4646+ },
4747+ "val": {
4848+ "type": "string",
4949+ "maxLength": 128,
5050+ "description": "The short string name of the value or type of this label."
5151+ },
5252+ "ver": {
5353+ "type": "integer",
5454+ "description": "The AT Protocol version of the label object."
5555+ }
5656+ },
5757+ "description": "Metadata tag on an atproto resource (eg, repo or record)."
5858+ },
5959+ "selfLabel": {
6060+ "type": "object",
6161+ "required": [
6262+ "val"
6363+ ],
6464+ "properties": {
6565+ "val": {
6666+ "type": "string",
6767+ "maxLength": 128,
6868+ "description": "The short string name of the value or type of this label."
6969+ }
7070+ },
7171+ "description": "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel."
7272+ },
7373+ "labelValue": {
7474+ "type": "string",
7575+ "knownValues": [
7676+ "!hide",
7777+ "!no-promote",
7878+ "!warn",
7979+ "!no-unauthenticated",
8080+ "dmca-violation",
8181+ "doxxing",
8282+ "porn",
8383+ "sexual",
8484+ "nudity",
8585+ "nsfl",
8686+ "gore"
8787+ ]
8888+ },
8989+ "selfLabels": {
9090+ "type": "object",
9191+ "required": [
9292+ "values"
9393+ ],
9494+ "properties": {
9595+ "values": {
9696+ "type": "array",
9797+ "items": {
9898+ "ref": "#selfLabel",
9999+ "type": "ref"
100100+ },
101101+ "maxLength": 10
102102+ }
103103+ },
104104+ "description": "Metadata tags on an atproto record, published by the author within the record."
105105+ },
106106+ "labelValueDefinition": {
107107+ "type": "object",
108108+ "required": [
109109+ "identifier",
110110+ "severity",
111111+ "blurs",
112112+ "locales"
113113+ ],
114114+ "properties": {
115115+ "blurs": {
116116+ "type": "string",
117117+ "description": "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.",
118118+ "knownValues": [
119119+ "content",
120120+ "media",
121121+ "none"
122122+ ]
123123+ },
124124+ "locales": {
125125+ "type": "array",
126126+ "items": {
127127+ "ref": "#labelValueDefinitionStrings",
128128+ "type": "ref"
129129+ }
130130+ },
131131+ "severity": {
132132+ "type": "string",
133133+ "description": "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.",
134134+ "knownValues": [
135135+ "inform",
136136+ "alert",
137137+ "none"
138138+ ]
139139+ },
140140+ "adultOnly": {
141141+ "type": "boolean",
142142+ "description": "Does the user need to have adult content enabled in order to configure this label?"
143143+ },
144144+ "identifier": {
145145+ "type": "string",
146146+ "maxLength": 100,
147147+ "description": "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).",
148148+ "maxGraphemes": 100
149149+ },
150150+ "defaultSetting": {
151151+ "type": "string",
152152+ "default": "warn",
153153+ "description": "The default setting for this label.",
154154+ "knownValues": [
155155+ "ignore",
156156+ "warn",
157157+ "hide"
158158+ ]
159159+ }
160160+ },
161161+ "description": "Declares a label value and its expected interpretations and behaviors."
162162+ },
163163+ "labelValueDefinitionStrings": {
164164+ "type": "object",
165165+ "required": [
166166+ "lang",
167167+ "name",
168168+ "description"
169169+ ],
170170+ "properties": {
171171+ "lang": {
172172+ "type": "string",
173173+ "format": "language",
174174+ "description": "The code of the language these strings are written in."
175175+ },
176176+ "name": {
177177+ "type": "string",
178178+ "maxLength": 640,
179179+ "description": "A short human-readable name for the label.",
180180+ "maxGraphemes": 64
181181+ },
182182+ "description": {
183183+ "type": "string",
184184+ "maxLength": 100000,
185185+ "description": "A longer description of what the label means and why it might be applied.",
186186+ "maxGraphemes": 10000
187187+ }
188188+ },
189189+ "description": "Strings which describe the label in the UI, localized into a specific language."
190190+ }
191191+ }
192192+}
···55import { PDS_HOST_URL } from "./env.ts";
66import { onError } from "./lib/errors.ts";
77import * as actionHandlers from "./routes/actions.tsx";
88+import { handler as communityGuidelinesHandler } from "./routes/community_guidelines.tsx";
89import * as dialogHandlers from "./routes/dialogs.tsx";
910import { handler as exploreHandler } from "./routes/explore.tsx";
1011import { handler as followersHandler } from "./routes/followers.tsx";
···25262627bff({
2728 appName: "Grain Social",
2929+ appLabelers: ["did:plc:nd45zozo34cr4pvxqu4rtr7e"],
3030+ appLabelerCollection: "social.grain.labeler.service",
2831 collections: [
2932 "social.grain.gallery",
3033 "social.grain.actor.profile",
···7073 route("/support/privacy", legalHandlers.privacyHandler),
7174 route("/support/terms", legalHandlers.termsHandler),
7275 route("/support/copyright", legalHandlers.copyrightHandler),
7676+ route("/support/community-guidelines", communityGuidelinesHandler),
7377 route("/dialogs/create-account", dialogHandlers.createAccount),
7478 route("/dialogs/gallery/new", dialogHandlers.createGallery),
7579 route("/dialogs/gallery/:rkey", dialogHandlers.editGallery),
7680 route("/dialogs/gallery/:rkey/sort", dialogHandlers.sortGallery),
8181+ route("/dialogs/label/:src/:val", dialogHandlers.labelValueDefinition),
7782 route("/dialogs/profile", dialogHandlers.editProfile),
7883 route("/dialogs/avatar/:handle", dialogHandlers.avatar),
7984 route("/dialogs/image", dialogHandlers.image),
+123
src/routes/community_guidelines.tsx
···11+import { BffContext, RouteHandler } from "@bigmoves/bff";
22+import { ComponentChildren } from "preact";
33+import { Breadcrumb } from "../components/Breadcrumb.tsx";
44+import { State } from "../state.ts";
55+66+export const handler: RouteHandler = (
77+ _req,
88+ _params,
99+ ctx: BffContext<State>,
1010+) => {
1111+ ctx.state.meta = [
1212+ { title: "Community Guidelines — Grain" },
1313+ ];
1414+ return ctx.render(
1515+ <div className="px-4 py-4">
1616+ <Breadcrumb
1717+ items={[{ label: "support", href: "/support" }, {
1818+ label: "community guidelines",
1919+ }]}
2020+ />
2121+ <h1 className="text-3xl font-bold mb-6 text-zinc-900 dark:text-white">
2222+ Community Guidelines
2323+ </h1>
2424+ <Section title="About Grain Social">
2525+ <p>
2626+ Grain Social is a photo-sharing service built on the AT Protocol.
2727+ These guidelines apply specifically to Grain Social. While the
2828+ protocol is decentralized and supports many independent services, our
2929+ focus is on fostering a respectful, creative, and safe experience
3030+ within our app.
3131+ </p>
3232+ </Section>
3333+3434+ <Section title="Our Principles">
3535+ <ul className="list-disc pl-5 space-y-1">
3636+ <li>
3737+ <strong>User choice</strong>: We are committed to empowering users
3838+ with control over where their data is stored, how their content is
3939+ moderated, and which algorithms power their feeds (hopefully more
4040+ options soon!).
4141+ </li>
4242+ <li>
4343+ <strong>Welcoming space</strong>: We aim to build a friendly,
4444+ inclusive environment where people enjoy sharing and discovering
4545+ photos.
4646+ </li>
4747+ <li>
4848+ <strong>Evolving standards</strong>: Our policies will adapt over
4949+ time based on your feedback and the needs of the community.
5050+ </li>
5151+ </ul>
5252+ </Section>
5353+5454+ <Section title="What’s Not Allowed">
5555+ <p>
5656+ Don't use Grain Social to break the law, harm others, or disrupt the
5757+ network. Specifically, do not:
5858+ </p>
5959+ <ul className="list-disc pl-5 space-y-1">
6060+ <li>Promote hate groups or terrorism</li>
6161+ <li>
6262+ Share child sexual abuse material or any sexual content involving
6363+ minors
6464+ </li>
6565+ <li>Engage in trafficking, exploitation, or predatory behavior</li>
6666+ <li>Trade illegal goods or substances</li>
6767+ <li>Share private personal info without consent</li>
6868+ <li>Hack, phish, scam, or impersonate others</li>
6969+ <li>Spam, abuse automation, or manipulate engagement</li>
7070+ <li>Violate copyrights or trademarks</li>
7171+ <li>Spread false or misleading election info</li>
7272+ <li>
7373+ Evade moderation actions (e.g., ban evasion) by creating new
7474+ accounts
7575+ </li>
7676+ </ul>
7777+ </Section>
7878+7979+ <Section title="Respect Others">
8080+ <p>We expect respectful conduct. This includes avoiding:</p>
8181+ <ul className="list-disc pl-5 space-y-1">
8282+ <li>Harassment, bullying, or targeted abuse</li>
8383+ <li>Hate speech or extremist content</li>
8484+ <li>Threats of violence or glorification of harm</li>
8585+ <li>Promotion of self-harm or suicide</li>
8686+ <li>Graphic violence or non-consensual sexual content</li>
8787+ <li>Misleading impersonation of individuals or organizations</li>
8888+ </ul>
8989+ </Section>
9090+9191+ <Section title="Reporting Violations">
9292+ <p>
9393+ Help us keep the community safe. You can report photos, galleries, or
9494+ accounts directly through the app (soon!) or by contacting us at{" "}
9595+ <a
9696+ href="mailto:support@grain.social"
9797+ className="text-sky-500 underline hover:underline"
9898+ >
9999+ support@grain.social
100100+ </a>
101101+ . Our moderation team will review and take action where needed.
102102+ Reports may consider off-platform context when relevant.
103103+ </p>
104104+ </Section>
105105+ </div>,
106106+ );
107107+};
108108+109109+type SectionProps = {
110110+ title: string;
111111+ children: ComponentChildren;
112112+};
113113+114114+const Section = ({ title, children }: SectionProps) => (
115115+ <section className="mb-8">
116116+ <h2 className="text-xl font-bold mb-2 text-zinc-800 dark:text-zinc-100">
117117+ {title}
118118+ </h2>
119119+ <div className="space-y-2 text-zinc-700 dark:text-zinc-300">
120120+ {children}
121121+ </div>
122122+ </section>
123123+);
+30
src/routes/dialogs.tsx
···99import { CreateAccountDialog } from "../components/CreateAccountDialog.tsx";
1010import { GalleryCreateEditDialog } from "../components/GalleryCreateEditDialog.tsx";
1111import { GallerySortDialog } from "../components/GallerySortDialog.tsx";
1212+import { LabelDefinitionDialog } from "../components/LabelDefinitionDialog.tsx";
1213import { PhotoAltDialog } from "../components/PhotoAltDialog.tsx";
1314import { PhotoDialog } from "../components/PhotoDialog.tsx";
1415import { PhotoSelectDialog } from "../components/PhotoSelectDialog.tsx";
1516import { ProfileDialog } from "../components/ProfileDialog.tsx";
1617import { getActorPhotos, getActorProfile } from "../lib/actor.ts";
1718import { getGallery, getGalleryItemsAndPhotos } from "../lib/gallery.ts";
1919+import { atprotoLabelValueDefinitions } from "../lib/moderation.ts";
1820import { photoToView } from "../lib/photo.ts";
1921import type { State } from "../state.ts";
2022···162164) => {
163165 return ctx.html(<CreateAccountDialog />);
164166};
167167+168168+export const labelValueDefinition: RouteHandler = async (
169169+ _req,
170170+ params,
171171+ ctx: BffContext<State>,
172172+) => {
173173+ const src = params.src;
174174+ const val = params.val;
175175+ const labelerDeinitionsMap = await ctx.getLabelerDefinitions();
176176+ if (!labelerDeinitionsMap) return ctx.next();
177177+ const labelValueDefinitions = labelerDeinitionsMap[src]
178178+ ?.labelValueDefinitions;
179179+ if (!labelValueDefinitions) return ctx.next();
180180+181181+ let valDef = labelValueDefinitions.find((def) => def.identifier === val);
182182+ if (!valDef && typeof val === "string") {
183183+ valDef = atprotoLabelValueDefinitions[val];
184184+ }
185185+186186+ if (!valDef) return ctx.next();
187187+ const labelerAtpData = await ctx.didResolver.resolveAtprotoData(src);
188188+ return ctx.html(
189189+ <LabelDefinitionDialog
190190+ labelByHandle={labelerAtpData?.handle}
191191+ labelValueDefinition={valDef}
192192+ />,
193193+ );
194194+};