A community based topic aggregation platform built on atproto

refactor(lexicon): apply atProto best practices to actor and community schemas

Actor changes:
- Remove handle from actor.profile record (resolved from DID, not stored)
- Remove geoLocation from actor.profile (not implemented)
- Remove verification fields from profile (AppView concern, not record data)
- Remove federation fields from profile (AppView concern, not record data)
- Remove moderation fields from profile (AppView concern, not record data)
- Update actor.getProfile to return profileViewDetailed from defs
- Update actor.updateProfile to remove geoLocation reference

Community changes:
- Remove handle from community.profile record (resolved from DID, not stored)
- Remove memberCount, subscriberCount from record (AppView cached stats)
- Remove federatedFrom, federatedId from record (AppView metadata)
- Remove federation and contentRules from record (not implemented)
- Update community.get to return communityViewDetailed from defs
- Update community.list to return communityView array from defs

Key principle: Records contain only user-controlled data. Computed stats,
cached values, and viewer state live in AppView views (defs.json), not records.

Following atProto best practices per:
https://github.com/bluesky-social/atproto/discussions/4245

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+9 -469
internal
atproto
+3 -76
internal/atproto/lexicon/social/coves/actor/getProfile.json
···
"output": {
"encoding": "application/json",
"schema": {
-
"type": "object",
-
"required": ["did", "profile"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"profile": {
-
"type": "ref",
-
"ref": "social.coves.actor.profile"
-
},
-
"stats": {
-
"type": "ref",
-
"ref": "#profileStats"
-
},
-
"viewer": {
-
"type": "ref",
-
"ref": "#viewerState",
-
"description": "Viewer's relationship to this profile"
-
}
-
}
-
}
-
}
-
},
-
"profileStats": {
-
"type": "object",
-
"description": "Aggregated statistics for a user profile",
-
"required": ["postCount", "commentCount", "communityCount", "savedCount", "reputation"],
-
"properties": {
-
"postCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Total number of posts created"
-
},
-
"commentCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Total number of comments made"
-
},
-
"communityCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of communities subscribed to"
-
},
-
"savedCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of saved items"
-
},
-
"reputation": {
-
"type": "integer",
-
"description": "Global reputation score"
-
},
-
"membershipCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of communities with membership status"
-
}
-
}
-
},
-
"viewerState": {
-
"type": "object",
-
"description": "The viewing user's relationship to this profile",
-
"properties": {
-
"blocked": {
-
"type": "boolean",
-
"description": "Whether the viewer has blocked this user"
-
},
-
"blockedBy": {
-
"type": "boolean",
-
"description": "Whether the viewer is blocked by this user"
-
},
-
"blockUri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the block record if viewer blocked this user"
+
"type": "ref",
+
"ref": "social.coves.actor.defs#profileViewDetailed",
+
"description": "Detailed profile view with stats and viewer state"
}
}
}
+1 -172
internal/atproto/lexicon/social/coves/actor/profile.json
···
"key": "literal:self",
"record": {
"type": "object",
-
"required": ["handle", "createdAt"],
+
"required": ["createdAt"],
"properties": {
-
"handle": {
-
"type": "string",
-
"format": "handle",
-
"maxLength": 253,
-
"description": "User's handle"
-
},
"displayName": {
"type": "string",
"maxGraphemes": 64,
···
"accept": ["image/png", "image/jpeg", "image/webp"],
"maxSize": 2000000
},
-
"verified": {
-
"type": "boolean",
-
"default": false,
-
"description": "Whether the user has completed phone verification"
-
},
-
"verifiedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When the user was verified"
-
},
-
"verificationExpiresAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When verification expires"
-
},
-
"federatedFrom": {
-
"type": "string",
-
"knownValues": ["bluesky", "lemmy", "mastodon", "coves"],
-
"description": "Platform user federated from"
-
},
-
"federatedIdentity": {
-
"type": "ref",
-
"ref": "#federatedIdentity",
-
"description": "Identity information from federated platform"
-
},
-
"location": {
-
"type": "ref",
-
"ref": "#geoLocation"
-
},
"createdAt": {
"type": "string",
"format": "datetime"
-
},
-
"moderatedCommunities": {
-
"type": "array",
-
"description": "Communities the user currently moderates",
-
"items": {
-
"type": "string",
-
"format": "did"
-
}
-
},
-
"moderationHistory": {
-
"type": "array",
-
"description": "Historical record of all moderation roles",
-
"items": {
-
"type": "ref",
-
"ref": "#moderationRole"
-
}
-
},
-
"violations": {
-
"type": "array",
-
"description": "Record of rule violations across communities",
-
"items": {
-
"type": "ref",
-
"ref": "#violation"
-
}
}
-
}
-
}
-
},
-
"moderationRole": {
-
"type": "object",
-
"required": ["communityDid", "role", "startedAt"],
-
"properties": {
-
"communityDid": {
-
"type": "string",
-
"format": "did",
-
"description": "Community where moderation role was held"
-
},
-
"role": {
-
"type": "string",
-
"knownValues": ["moderator", "admin"],
-
"description": "Type of moderation role"
-
},
-
"startedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When the role began"
-
},
-
"endedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When the role ended (null if current)"
-
}
-
}
-
},
-
"violation": {
-
"type": "object",
-
"required": ["communityDid", "ruleViolated", "timestamp", "severity"],
-
"properties": {
-
"communityDid": {
-
"type": "string",
-
"format": "did",
-
"description": "Community where violation occurred"
-
},
-
"ruleViolated": {
-
"type": "string",
-
"description": "Description of the rule that was violated"
-
},
-
"timestamp": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When the violation occurred"
-
},
-
"severity": {
-
"type": "string",
-
"knownValues": ["minor", "moderate", "major", "severe"],
-
"description": "Severity level of the violation"
-
},
-
"resolution": {
-
"type": "string",
-
"description": "How the violation was resolved"
-
},
-
"postUri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "Optional reference to the violating content"
-
}
-
}
-
},
-
"federatedIdentity": {
-
"type": "object",
-
"description": "Verified identity from a federated platform",
-
"required": ["did", "handle", "verifiedAt"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did",
-
"description": "Original DID from the federated platform"
-
},
-
"handle": {
-
"type": "string",
-
"maxLength": 253,
-
"description": "Original handle from the federated platform"
-
},
-
"verifiedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When the federated identity was verified via OAuth"
-
},
-
"lastSyncedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "Last time profile data was synced from the federated platform"
-
},
-
"homePDS": {
-
"type": "string",
-
"description": "Home PDS server URL for the federated account"
-
}
-
}
-
},
-
"geoLocation": {
-
"type": "object",
-
"description": "Geographic location information",
-
"properties": {
-
"country": {
-
"type": "string",
-
"maxLength": 2,
-
"description": "ISO 3166-1 alpha-2 country code"
-
},
-
"region": {
-
"type": "string",
-
"maxLength": 128,
-
"description": "State/province/region name"
-
},
-
"displayName": {
-
"type": "string",
-
"maxLength": 256,
-
"description": "Human-readable location name"
}
}
}
-4
internal/atproto/lexicon/social/coves/actor/updateProfile.json
···
"type": "blob",
"accept": ["image/png", "image/jpeg", "image/webp"],
"maxSize": 2000000
-
},
-
"location": {
-
"type": "ref",
-
"ref": "social.coves.actor.profile#geoLocation"
}
}
}
+3 -83
internal/atproto/lexicon/social/coves/community/get.json
···
"output": {
"encoding": "application/json",
"schema": {
-
"type": "object",
-
"required": ["did", "profile"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"profile": {
-
"type": "ref",
-
"ref": "social.coves.community.profile"
-
},
-
"stats": {
-
"type": "ref",
-
"ref": "#communityStats"
-
},
-
"viewer": {
-
"type": "ref",
-
"ref": "#viewerState",
-
"description": "Viewer's relationship to this community"
-
}
-
}
-
}
-
}
-
},
-
"communityStats": {
-
"type": "object",
-
"required": ["subscriberCount", "memberCount", "postCount", "activePostersCount"],
-
"properties": {
-
"subscriberCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of users subscribed to this community"
-
},
-
"memberCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of users with membership status"
-
},
-
"postCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Total number of posts in this community"
-
},
-
"activePostersCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of unique posters in the last 30 days"
-
},
-
"moderatorCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Number of active moderators"
-
}
-
}
-
},
-
"viewerState": {
-
"type": "object",
-
"description": "The viewing user's relationship to this community",
-
"properties": {
-
"subscribed": {
-
"type": "boolean",
-
"description": "Whether the viewer is subscribed"
-
},
-
"subscriptionUri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the subscription record if subscribed"
-
},
-
"member": {
-
"type": "boolean",
-
"description": "Whether the viewer has membership status (AppView-computed)"
-
},
-
"reputation": {
-
"type": "integer",
-
"description": "Viewer's reputation in this community"
-
},
-
"moderator": {
-
"type": "boolean",
-
"description": "Whether the viewer is a moderator"
-
},
-
"banned": {
-
"type": "boolean",
-
"description": "Whether the viewer is banned from this community"
+
"type": "ref",
+
"ref": "social.coves.community.defs#communityViewDetailed",
+
"description": "Detailed community view with stats and viewer state"
}
}
}
+1 -38
internal/atproto/lexicon/social/coves/community/list.json
···
"type": "array",
"items": {
"type": "ref",
-
"ref": "#communityView"
+
"ref": "social.coves.community.defs#communityView"
}
},
"cursor": {
"type": "string"
-
}
-
}
-
}
-
}
-
},
-
"communityView": {
-
"type": "object",
-
"required": ["did", "profile", "subscriberCount", "postCount"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"profile": {
-
"type": "ref",
-
"ref": "social.coves.community.profile"
-
},
-
"subscriberCount": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"memberCount": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"postCount": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"viewer": {
-
"type": "object",
-
"properties": {
-
"subscribed": {
-
"type": "boolean"
-
},
-
"member": {
-
"type": "boolean"
}
}
}
+1 -96
internal/atproto/lexicon/social/coves/community/profile.json
···
"key": "literal:self",
"record": {
"type": "object",
-
"required": ["handle", "name", "createdAt", "createdBy", "hostedBy"],
+
"required": ["name", "createdAt", "createdBy", "hostedBy"],
"properties": {
-
"handle": {
-
"type": "string",
-
"maxLength": 253,
-
"format": "handle",
-
"description": "atProto handle (e.g., gaming.community.coves.social) - DNS-resolvable name for this community"
-
},
"name": {
"type": "string",
"maxLength": 64,
···
"maxLength": 64,
"description": "Community visibility level"
},
-
"federation": {
-
"type": "ref",
-
"ref": "#federationConfig",
-
"description": "Federation and discovery configuration"
-
},
-
"contentRules": {
-
"type": "ref",
-
"ref": "#contentRules",
-
"description": "Content posting rules and restrictions for this community"
-
},
"moderationType": {
"type": "string",
"knownValues": ["moderator", "sortition"],
···
"maxLength": 32
}
},
-
"memberCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Cached count of community members"
-
},
-
"subscriberCount": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Cached count of community subscribers"
-
},
-
"federatedFrom": {
-
"type": "string",
-
"knownValues": ["lemmy", "coves"],
-
"description": "Platform community originated from"
-
},
-
"federatedId": {
-
"type": "string",
-
"description": "Original ID on federated platform"
-
},
"createdAt": {
"type": "string",
"format": "datetime"
}
-
}
-
}
-
},
-
"federationConfig": {
-
"type": "object",
-
"description": "Federation and discovery configuration for this community",
-
"properties": {
-
"allowExternalDiscovery": {
-
"type": "boolean",
-
"default": true,
-
"description": "Whether other Coves instances can index and discover this community"
-
}
-
}
-
},
-
"contentRules": {
-
"type": "object",
-
"description": "Content posting rules and restrictions for this community. Rules are validated at post creation time.",
-
"properties": {
-
"allowedEmbedTypes": {
-
"type": "array",
-
"description": "Allowed embed types. Empty array = no embeds allowed. Null/undefined = all embed types allowed.",
-
"items": {
-
"type": "string",
-
"knownValues": ["images", "video", "external", "record"]
-
}
-
},
-
"requireText": {
-
"type": "boolean",
-
"default": false,
-
"description": "Whether posts must have text content (non-empty content field)"
-
},
-
"minTextLength": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Minimum character length for post content (0 = no minimum)"
-
},
-
"maxTextLength": {
-
"type": "integer",
-
"minimum": 1,
-
"description": "Maximum character length for post content (overrides global limit if lower)"
-
},
-
"requireTitle": {
-
"type": "boolean",
-
"default": false,
-
"description": "Whether posts must have a title"
-
},
-
"minImages": {
-
"type": "integer",
-
"minimum": 0,
-
"description": "Minimum number of images required (0 = no minimum). Only enforced if images embed is present."
-
},
-
"maxImages": {
-
"type": "integer",
-
"minimum": 1,
-
"description": "Maximum number of images allowed per post (overrides global limit if lower)"
-
},
-
"allowFederated": {
-
"type": "boolean",
-
"default": true,
-
"description": "Whether federated posts (e.g., from app.bsky) are allowed in this community"
}
}
}