{
"id": "net.anisota.graph.scopedMute",
"defs": {
"main": {
"key": "literal:self",
"type": "record",
"record": {
"type": "object",
"required": [
"subjects",
"createdAt"
],
"properties": {
"appView": {
"type": "string",
"description": "The AppView these mutes were last verified against (e.g. 'bluesky', 'blacksky', 'custom'). Native mutes are held per-AppView, so an index written while reading one AppView says nothing about another; without this, verifying the index against a different AppView reads 'that AppView does not hold this mute' as 'the reader lifted this mute' and deletes it."
},
"subjects": {
"type": "array",
"items": {
"ref": "#scopedMuteEntry",
"type": "ref"
},
"maxLength": 1000,
"description": "The accounts currently carrying a native scoped mute."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "When this index was first written"
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "When this index was last reconciled or edited"
}
}
},
"description": "An index of the accounts whose reposts or quote posts the viewer has muted through Bluesky's native scoped mute (app.bsky.graph.muteActor with onlyReposts / onlyQuoteposts). Bluesky stores the mute itself, but app.bsky.graph.getMutes returns only fully muted accounts, so a scoped mute is otherwise discoverable only by loading each profile. This record lets Anisota list and undo them, and enforce them client-side on unauthenticated reads where no viewer state comes back. It is a cache, not the source of truth: entries are reconciled against viewerState.mutedOnlyReposts / mutedOnlyQuoteposts and dropped when the server disagrees."
},
"scopedMuteEntry": {
"type": "object",
"required": [
"subject"
],
"properties": {
"subject": {
"type": "string",
"format": "did",
"description": "DID of the scope-muted account"
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "When this scoped mute was set from Anisota"
},
"onlyReposts": {
"type": "boolean",
"default": false,
"description": "Their reposts are muted"
},
"onlyQuoteposts": {
"type": "boolean",
"default": false,
"description": "Their quote posts are muted"
}
},
"description": "One account's native scoped mute. At least one scope flag is true — an entry with neither is a full mute or no mute at all, and does not belong here."
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}