A community based topic aggregation platform built on atproto

chore: update docs and references for lexicon migration

Updates remaining documentation, code references, and configuration
to reflect the lexicon namespace migration and labels changes.

Changes:
- Update docs (PRDs, CLAUDE.md) with new namespace references
- Update API routes and handlers for community.post
- Update aggregator client references
- Update feed system documentation
- Remove deprecated interaction/comment schemas (moved to feed/comment)

No functional changes - documentation and reference updates only.

+3 -3
aggregators/kagi-news/src/coves_client.py
···
Handles:
- Authentication with aggregator credentials
-
- Creating posts in communities (social.coves.post.create)
+
- Creating posts in communities (social.coves.community.post.create)
- External embed formatting
"""
···
self.authenticate()
try:
-
# Prepare post data for social.coves.post.create endpoint
+
# Prepare post data for social.coves.community.post.create endpoint
post_data = {
"community": community_handle,
"content": content,
···
logger.info(f"Creating post in community: {community_handle}")
# Make direct HTTP request to XRPC endpoint
-
url = f"{self.api_url}/xrpc/social.coves.post.create"
+
url = f"{self.api_url}/xrpc/social.coves.community.post.create"
headers = {
"Authorization": f"Bearer {self.client._session.access_jwt}",
"Content-Type": "application/json"
+2 -2
cmd/server/main.go
···
postJetstreamURL := os.Getenv("POST_JETSTREAM_URL")
if postJetstreamURL == "" {
// Listen to post record creation events
-
postJetstreamURL = "ws://localhost:6008/subscribe?wantedCollections=social.coves.post.record"
+
postJetstreamURL = "ws://localhost:6008/subscribe?wantedCollections=social.coves.community.post"
}
postEventConsumer := jetstream.NewPostEventConsumer(postRepo, communityRepo, userService)
···
}()
log.Printf("Started Jetstream post consumer: %s", postJetstreamURL)
-
log.Println(" - Indexing: social.coves.post.record CREATE operations")
+
log.Println(" - Indexing: social.coves.community.post CREATE operations")
log.Println(" - UPDATE/DELETE indexing deferred until those features are implemented")
// Start Jetstream consumer for aggregators
+5 -5
docs/COMMUNITY_FEEDS.md
···
```go
type PostView struct {
-
URI string // at://did:plc:abc/social.coves.post.record/123
+
URI string // at://did:plc:abc/social.coves.community.post.record/123
CID string // Content ID
RKey string // Record key (TID)
Author *AuthorView // Author with handle, avatar, reputation
···
"feed": [
{
"post": {
-
"uri": "at://did:plc:gaming123/social.coves.post.record/abc",
+
"uri": "at://did:plc:gaming123/social.coves.community.post.record/abc",
"cid": "bafyrei...",
"author": {
"did": "did:plc:alice",
···
GET /xrpc/social.coves.feed.getSkeleton?feed=at://alice/feed/best-memes
→ Returns: [uri1, uri2, uri3, ...]
-
GET /xrpc/social.coves.post.get?uris=[...]
+
GET /xrpc/social.coves.community.post.get?uris=[...]
→ Returns: [full posts]
```
···
## Lexicon Updates
-
### Updated: `social.coves.post.get`
+
### Updated: `social.coves.community.post.get`
**Changes:**
1. ✅ Batch URIs: `uri` → `uris[]` (max 25)
···
// Custom feed (power users)
GET /xrpc/social.coves.feed.getSkeleton?feed=at://alice/feed/best-memes
→ Returns URIs
-
GET /xrpc/social.coves.post.get?uris=[...]
+
GET /xrpc/social.coves.community.post.get?uris=[...]
→ Hydrates posts
```
+1 -1
docs/FEED_SYSTEM_IMPLEMENTATION.md
···
"feed": [
{
"post": {
-
"uri": "at://did:plc:community-gaming/social.coves.post.record/3k...",
+
"uri": "at://did:plc:community-gaming/social.coves.community.post.record/3k...",
"cid": "bafyrei...",
"author": {
"did": "did:plc:alice",
+1 -1
docs/PRD_GOVERNANCE.md
···
- [ ] Go structs: `ContentRules` type in community models
- [ ] Repository: Parse and store `contentRules` from community profiles
- [ ] Service: `ValidatePostAgainstRules(post, community)` function
-
- [ ] Handler: Integrate validation into `social.coves.post.create`
+
- [ ] Handler: Integrate validation into `social.coves.community.post.create`
- [ ] AppView indexing: Index post characteristics (embed_type, text_length, etc.)
- [ ] Tests: Comprehensive rule validation tests
- [ ] Documentation: Content rules guide for community creators
+17 -17
docs/PRD_POSTS.md
···
**Repository Structure:**
```
-
Repository: at://did:plc:community789/social.coves.post.record/3k2a4b5c6d7e
+
Repository: at://did:plc:community789/social.coves.community.post.record/3k2a4b5c6d7e
Owner: did:plc:community789 (community owns the post)
Author: did:plc:user123 (tracked in record metadata)
Hosted By: did:web:coves.social (instance manages community credentials)
···
**Implementation checklist:**
- [x] Lexicon: `contentRules` in `social.coves.community.profile` ✅
-
- [x] Lexicon: `postType` removed from `social.coves.post.create` ✅
+
- [x] Lexicon: `postType` removed from `social.coves.community.post.create` ✅
- [ ] Validation: `ValidatePostAgainstRules()` service function
- [ ] Handler: Integrate validation in post creation endpoint
- [ ] AppView: Index derived characteristics (embed_type, text_length, etc.)
···
**Priority:** CRITICAL - Posts are the foundation of the platform
#### Create Post
-
- [x] Lexicon: `social.coves.post.record` ✅
-
- [x] Lexicon: `social.coves.post.create` ✅
+
- [x] Lexicon: `social.coves.community.post.record` ✅
+
- [x] Lexicon: `social.coves.community.post.create` ✅
- [x] Removed `postType` enum in favor of content rules ✅ (2025-10-18)
- [x] Removed `postType` from record and get lexicons ✅ (2025-10-18)
-
- [x] **Handler:** `POST /xrpc/social.coves.post.create` ✅ (Alpha - see IMPLEMENTATION_POST_CREATION.md)
+
- [x] **Handler:** `POST /xrpc/social.coves.community.post.create` ✅ (Alpha - see IMPLEMENTATION_POST_CREATION.md)
- ✅ Accept: community (DID/handle), title (optional), content, facets, embed, contentLabels
- ✅ Validate: User is authenticated, community exists, content within limits
- ✅ Write: Create record in **community's PDS repository**
···
- [x] **E2E Test:** Create text post → Write to **community's PDS** → Index via Jetstream → Verify in AppView ✅
#### Get Post
-
- [x] Lexicon: `social.coves.post.get` ✅
-
- [ ] **Handler:** `GET /xrpc/social.coves.post.get?uri=at://...`
+
- [x] Lexicon: `social.coves.community.post.get` ✅
+
- [ ] **Handler:** `GET /xrpc/social.coves.community.post.get?uri=at://...`
- Accept: AT-URI of post
- Return: Full post view with author, community, stats, viewer state
- [ ] **Service Layer:** `PostService.Get(uri, viewerDID)`
···
- [ ] **E2E Test:** Get post by URI → Verify all fields populated
#### Update Post
-
- [x] Lexicon: `social.coves.post.update` ✅
-
- [ ] **Handler:** `POST /xrpc/social.coves.post.update`
+
- [x] Lexicon: `social.coves.community.post.update` ✅
+
- [ ] **Handler:** `POST /xrpc/social.coves.community.post.update`
- Accept: uri, title, content, facets, embed, contentLabels, editNote
- Validate: User is post author, within 24-hour edit window
- Write: Update record in **community's PDS**
···
- [ ] **E2E Test:** Update post → Verify edit reflected in AppView
#### Delete Post
-
- [x] Lexicon: `social.coves.post.delete` ✅
-
- [ ] **Handler:** `POST /xrpc/social.coves.post.delete`
+
- [x] Lexicon: `social.coves.community.post.delete` ✅
+
- [ ] **Handler:** `POST /xrpc/social.coves.community.post.delete`
- Accept: uri
- Validate: User is post author OR community moderator
- Write: Delete record from **community's PDS**
···
#### Post Event Handling
- [x] **Consumer:** `PostConsumer.HandlePostEvent()` ✅ (2025-10-19)
-
- ✅ Listen for `social.coves.post.record` CREATE from **community repositories**
+
- ✅ Listen for `social.coves.community.post.record` CREATE from **community repositories**
- ✅ Parse post record, extract author DID and community DID (from AT-URI owner)
- ⚠️ **Derive post characteristics:** DEFERRED (embed_type, text_length, has_title, has_embed for content rules filtering)
- ✅ Insert in AppView PostgreSQL (CREATE only - UPDATE/DELETE deferred)
···
- [ ] **Tag Storage:** Tags live in **user's repository** (users own their tags)
#### Crossposting
-
- [x] Lexicon: `social.coves.post.crosspost` ✅
+
- [x] Lexicon: `social.coves.community.post.crosspost` ✅
- [ ] **Crosspost Tracking:** Share post to multiple communities
- [ ] **Implementation:** Create new post record in each community's repository
- [ ] **Crosspost Chain:** Track all crosspost relationships
···
- [ ] **AppView Query:** Endpoint to fetch user's saved posts
### Post Search
-
- [x] Lexicon: `social.coves.post.search` ✅
+
- [x] Lexicon: `social.coves.community.post.search` ✅
- [ ] **Search Parameters:**
- Query string (q)
- Filter by community
···
- **Reuses Token Refresh:** Can leverage existing community credential management
**Implementation Details:**
-
- Post AT-URI: `at://community_did/social.coves.post.record/tid`
+
- Post AT-URI: `at://community_did/social.coves.community.post.record/tid`
- Write operations use community's PDS credentials (encrypted, stored in AppView)
- Author tracked in post record's `author` field (DID)
- Moderators can delete any post in their community
···
## Lexicon Summary
-
### `social.coves.post.record`
+
### `social.coves.community.post.record`
**Status:** ✅ Defined, implementation TODO
**Last Updated:** 2025-10-18 (removed `postType` enum)
···
- Post "type" is derived from structure (has embed? what embed type? has title? text length?)
- Community's `contentRules` validate post structure at creation time
-
### `social.coves.post.create` (Procedure)
+
### `social.coves.community.post.create` (Procedure)
**Status:** ✅ Defined, implementation TODO
**Last Updated:** 2025-10-18 (removed `postType` parameter)
+4 -4
docs/aggregators/PRD_AGGREGATORS.md
···
1. **Aggregators are Actors, Not a Separate System**
- Each aggregator has its own DID
- Authenticate as themselves via JWT
-
- Use existing `social.coves.post.create` endpoint
+
- Use existing `social.coves.community.post.create` endpoint
- Post record's `author` field = aggregator DID (server-populated)
- No separate posting API needed
···
Aggregator Service (External)
│ 1. Authenticates as aggregator DID (JWT)
-
│ 2. Calls social.coves.post.create
+
│ 2. Calls social.coves.community.post.create
Coves AppView Handler
···
### For Aggregators
-
- **`social.coves.post.create`** - Modified to handle aggregator auth
+
- **`social.coves.community.post.create`** - Modified to handle aggregator auth
- **`social.coves.aggregator.getAuthorizations`** - Query authorized communities
### For Discovery
···
---
-
### 2025-10-19: Reuse `social.coves.post.create` Endpoint
+
### 2025-10-19: Reuse `social.coves.community.post.create` Endpoint
**Decision:** Aggregators use existing post creation endpoint.
**Rationale:**
+3 -3
docs/aggregators/PRD_KAGI_NEWS_RSS.md
···
│ 3. Deduplication: Tracks posted items via JSON state file │
│ 4. Feed Mapper: Maps feed URLs to community handles │
│ 5. Post Formatter: Converts to Coves post format │
-
│ 6. Post Publisher: Calls social.coves.post.create via XRPC │
+
│ 6. Post Publisher: Calls social.coves.community.post.create via XRPC │
│ 7. Blob Uploader: Handles image upload to ATProto │
└─────────────────────────────────────────────────────────────┘
│ Authenticated XRPC calls
┌─────────────────────────────────────────────────────────────┐
-
│ Coves AppView (social.coves.post.create) │
+
│ Coves AppView (social.coves.community.post.create) │
│ - Validates aggregator authorization │
│ - Creates post with author = did:plc:[aggregator-did] │
│ - Indexes to community feeds │
···
```json
{
-
"$type": "social.coves.post.record",
+
"$type": "social.coves.community.post.record",
"author": "did:plc:[aggregator-did]",
"community": "world-news.coves.social",
"title": "{Kagi story title}",
+1 -1
internal/api/handlers/post/create.go
···
}
}
-
// HandleCreate handles POST /xrpc/social.coves.post.create
+
// HandleCreate handles POST /xrpc/social.coves.community.post.create
// Creates a new post in a community's repository
func (h *CreateHandler) HandleCreate(w http.ResponseWriter, r *http.Request) {
// 1. Check HTTP method
+7 -7
internal/api/routes/post.go
···
)
// RegisterPostRoutes registers post-related XRPC endpoints on the router
-
// Implements social.coves.post.* lexicon endpoints
+
// Implements social.coves.community.post.* lexicon endpoints
func RegisterPostRoutes(r chi.Router, service posts.Service, authMiddleware *middleware.AtProtoAuthMiddleware) {
// Initialize handlers
createHandler := post.NewCreateHandler(service)
// Procedure endpoints (POST) - require authentication
-
// social.coves.post.create - create a new post in a community
-
r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.post.create", createHandler.HandleCreate)
+
// social.coves.community.post.create - create a new post in a community
+
r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.community.post.create", createHandler.HandleCreate)
// Future endpoints (Beta):
-
// r.Get("/xrpc/social.coves.post.get", getHandler.HandleGet)
-
// r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.post.update", updateHandler.HandleUpdate)
-
// r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.post.delete", deleteHandler.HandleDelete)
-
// r.Get("/xrpc/social.coves.post.list", listHandler.HandleList)
+
// r.Get("/xrpc/social.coves.community.post.get", getHandler.HandleGet)
+
// r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.community.post.update", updateHandler.HandleUpdate)
+
// r.With(authMiddleware.RequireAuth).Post("/xrpc/social.coves.community.post.delete", deleteHandler.HandleDelete)
+
// r.Get("/xrpc/social.coves.community.post.list", listHandler.HandleList)
}
+80
internal/atproto/lexicon/social/coves/feed/comment.json
···
+
{
+
"lexicon": 1,
+
"id": "social.coves.feed.comment",
+
"defs": {
+
"main": {
+
"type": "record",
+
"description": "A comment on a post or another comment. Comments live in user repositories and support nested threading.",
+
"key": "tid",
+
"record": {
+
"type": "object",
+
"required": ["reply", "content", "createdAt"],
+
"properties": {
+
"reply": {
+
"type": "ref",
+
"ref": "#replyRef",
+
"description": "Reference to the post and parent being replied to"
+
},
+
"content": {
+
"type": "string",
+
"maxGraphemes": 3000,
+
"maxLength": 30000,
+
"description": "Comment text content"
+
},
+
"facets": {
+
"type": "array",
+
"description": "Annotations for rich text (mentions, links, etc.)",
+
"items": {
+
"type": "ref",
+
"ref": "social.coves.richtext.facet"
+
}
+
},
+
"embed": {
+
"type": "union",
+
"description": "Embedded media or quoted posts",
+
"refs": [
+
"social.coves.embed.images",
+
"social.coves.embed.post"
+
]
+
},
+
"langs": {
+
"type": "array",
+
"description": "Languages used in the comment content (ISO 639-1)",
+
"maxLength": 3,
+
"items": {
+
"type": "string",
+
"format": "language"
+
}
+
},
+
"labels": {
+
"type": "ref",
+
"ref": "com.atproto.label.defs#selfLabels",
+
"description": "Self-applied content labels"
+
},
+
"createdAt": {
+
"type": "string",
+
"format": "datetime",
+
"description": "Timestamp of comment creation"
+
}
+
}
+
}
+
},
+
"replyRef": {
+
"type": "object",
+
"description": "References for maintaining thread structure. Root always points to the original post, parent points to the immediate parent (post or comment).",
+
"required": ["root", "parent"],
+
"properties": {
+
"root": {
+
"type": "ref",
+
"ref": "com.atproto.repo.strongRef",
+
"description": "Strong reference to the original post that started the thread"
+
},
+
"parent": {
+
"type": "ref",
+
"ref": "com.atproto.repo.strongRef",
+
"description": "Strong reference to the immediate parent (post or comment) being replied to"
+
}
+
}
+
}
+
}
+
}
-86
internal/atproto/lexicon/social/coves/interaction/comment.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.interaction.comment",
-
"defs": {
-
"main": {
-
"type": "record",
-
"description": "A comment on a post or another comment",
-
"key": "tid",
-
"record": {
-
"type": "object",
-
"required": ["subject", "content", "createdAt"],
-
"properties": {
-
"subject": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of post or comment being replied to"
-
},
-
"content": {
-
"type": "union",
-
"refs": ["#textContent", "#imageContent", "#stickerContent"]
-
},
-
"location": {
-
"type": "ref",
-
"ref": "social.coves.actor.profile#geoLocation"
-
},
-
"translatedFrom": {
-
"type": "string",
-
"maxLength": 10,
-
"description": "Language code if auto-translated (ISO 639-1)"
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
}
-
}
-
}
-
},
-
"textContent": {
-
"type": "object",
-
"required": ["text"],
-
"properties": {
-
"text": {
-
"type": "string",
-
"maxLength": 10000,
-
"description": "Comment text"
-
},
-
"facets": {
-
"type": "array",
-
"description": "Rich text annotations",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.richtext.facet"
-
}
-
}
-
}
-
},
-
"imageContent": {
-
"type": "object",
-
"required": ["image"],
-
"properties": {
-
"image": {
-
"type": "ref",
-
"ref": "social.coves.embed.images#image"
-
},
-
"caption": {
-
"type": "string",
-
"maxLength": 1000
-
}
-
}
-
},
-
"stickerContent": {
-
"type": "object",
-
"required": ["stickerId"],
-
"properties": {
-
"stickerId": {
-
"type": "string",
-
"description": "Reference to a sticker in a sticker pack"
-
},
-
"stickerPackId": {
-
"type": "string",
-
"description": "Reference to the sticker pack"
-
}
-
}
-
}
-
}
-
}
-75
internal/atproto/lexicon/social/coves/interaction/createComment.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.interaction.createComment",
-
"defs": {
-
"main": {
-
"type": "procedure",
-
"description": "Create a comment on a post or another comment",
-
"input": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["parent", "text"],
-
"properties": {
-
"parent": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the post or comment being replied to"
-
},
-
"text": {
-
"type": "string",
-
"maxGraphemes": 3000,
-
"maxLength": 30000,
-
"description": "Comment text"
-
},
-
"textFacets": {
-
"type": "array",
-
"description": "Rich text annotations",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.richtext.facet"
-
}
-
}
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri", "cid"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the created comment"
-
},
-
"cid": {
-
"type": "string",
-
"format": "cid",
-
"description": "CID of the created comment"
-
}
-
}
-
}
-
},
-
"errors": [
-
{
-
"name": "ParentNotFound",
-
"description": "Parent post or comment not found"
-
},
-
{
-
"name": "NotAuthorized",
-
"description": "User is not authorized to comment"
-
},
-
{
-
"name": "ThreadLocked",
-
"description": "Comment thread is locked"
-
},
-
{
-
"name": "Banned",
-
"description": "User is banned from this community"
-
}
-
]
-
}
-
}
-
}
-41
internal/atproto/lexicon/social/coves/interaction/deleteComment.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.interaction.deleteComment",
-
"defs": {
-
"main": {
-
"type": "procedure",
-
"description": "Delete a comment",
-
"input": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the comment to delete"
-
}
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"properties": {}
-
}
-
},
-
"errors": [
-
{
-
"name": "CommentNotFound",
-
"description": "Comment not found"
-
},
-
{
-
"name": "NotAuthorized",
-
"description": "User is not authorized to delete this comment"
-
}
-
]
-
}
-
}
-
}
-118
internal/atproto/lexicon/social/coves/post/create.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.create",
-
"defs": {
-
"main": {
-
"type": "procedure",
-
"description": "Create a new post in a community",
-
"input": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["community"],
-
"properties": {
-
"community": {
-
"type": "string",
-
"format": "at-identifier",
-
"description": "DID or handle of the community to post in"
-
},
-
"title": {
-
"type": "string",
-
"maxGraphemes": 300,
-
"maxLength": 3000,
-
"description": "Post title (optional for microblog, image, and video posts)"
-
},
-
"content": {
-
"type": "string",
-
"maxLength": 50000,
-
"description": "Post content - main text for text posts, description for media, etc."
-
},
-
"facets": {
-
"type": "array",
-
"description": "Rich text annotations for content",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.richtext.facet"
-
}
-
},
-
"embed": {
-
"type": "union",
-
"description": "Embedded content - images, videos, external links, or quoted posts",
-
"refs": [
-
"social.coves.embed.images",
-
"social.coves.embed.video",
-
"social.coves.embed.external",
-
"social.coves.embed.post"
-
]
-
},
-
"originalAuthor": {
-
"type": "ref",
-
"ref": "social.coves.post.record#originalAuthor",
-
"description": "For microblog posts - information about the original author"
-
},
-
"federatedFrom": {
-
"type": "ref",
-
"ref": "social.coves.federation.post",
-
"description": "Reference to original federated post (for microblog posts)"
-
},
-
"contentLabels": {
-
"type": "array",
-
"description": "Self-applied content labels",
-
"items": {
-
"type": "string",
-
"knownValues": ["nsfw", "spoiler", "violence"],
-
"maxLength": 32
-
}
-
},
-
"location": {
-
"type": "ref",
-
"ref": "social.coves.actor.profile#geoLocation",
-
"description": "Geographic location where post was created"
-
}
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri", "cid"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the created post"
-
},
-
"cid": {
-
"type": "string",
-
"format": "cid",
-
"description": "CID of the created post"
-
}
-
}
-
}
-
},
-
"errors": [
-
{
-
"name": "CommunityNotFound",
-
"description": "Community not found"
-
},
-
{
-
"name": "NotAuthorized",
-
"description": "User is not authorized to post in this community"
-
},
-
{
-
"name": "Banned",
-
"description": "User is banned from this community"
-
},
-
{
-
"name": "InvalidContent",
-
"description": "Post content violates community rules"
-
},
-
{
-
"name": "ContentRuleViolation",
-
"description": "Post violates community content rules (e.g., embeds not allowed, text too short)"
-
}
-
]
-
}
-
}
-
}
-39
internal/atproto/lexicon/social/coves/post/crosspost.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.crosspost",
-
"defs": {
-
"main": {
-
"type": "record",
-
"description": "A record tracking crosspost relationships between posts",
-
"key": "tid",
-
"record": {
-
"type": "object",
-
"required": ["originalPost", "crosspostOf", "createdAt"],
-
"properties": {
-
"originalPost": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the original post in the crosspost chain"
-
},
-
"crosspostOf": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the immediate parent this is a crosspost of"
-
},
-
"allCrossposts": {
-
"type": "array",
-
"description": "Array of AT-URIs of all posts in the crosspost chain",
-
"items": {
-
"type": "string",
-
"format": "at-uri"
-
}
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
}
-
}
-
}
-
}
-
}
-
}
-41
internal/atproto/lexicon/social/coves/post/delete.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.delete",
-
"defs": {
-
"main": {
-
"type": "procedure",
-
"description": "Delete a post",
-
"input": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the post to delete"
-
}
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"properties": {}
-
}
-
},
-
"errors": [
-
{
-
"name": "PostNotFound",
-
"description": "Post not found"
-
},
-
{
-
"name": "NotAuthorized",
-
"description": "User is not authorized to delete this post"
-
}
-
]
-
}
-
}
-
}
-294
internal/atproto/lexicon/social/coves/post/get.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.get",
-
"defs": {
-
"main": {
-
"type": "query",
-
"description": "Get posts by AT-URI. Supports batch fetching for feed hydration. Returns posts in same order as input URIs.",
-
"parameters": {
-
"type": "params",
-
"required": ["uris"],
-
"properties": {
-
"uris": {
-
"type": "array",
-
"description": "List of post AT-URIs to fetch (max 25)",
-
"items": {
-
"type": "string",
-
"format": "at-uri"
-
},
-
"maxLength": 25,
-
"minLength": 1
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["posts"],
-
"properties": {
-
"posts": {
-
"type": "array",
-
"description": "Array of post views. May include notFound/blocked entries for missing posts.",
-
"items": {
-
"type": "union",
-
"refs": ["#postView", "#notFoundPost", "#blockedPost"]
-
}
-
}
-
}
-
}
-
},
-
"errors": [
-
{"name": "InvalidRequest", "description": "Invalid URI format or empty array"}
-
]
-
},
-
"postView": {
-
"type": "object",
-
"required": ["uri", "cid", "author", "record", "community", "createdAt", "indexedAt"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri"
-
},
-
"cid": {
-
"type": "string",
-
"format": "cid"
-
},
-
"author": {
-
"type": "ref",
-
"ref": "#authorView"
-
},
-
"record": {
-
"type": "unknown",
-
"description": "The actual post record (text, image, video, etc.)"
-
},
-
"community": {
-
"type": "ref",
-
"ref": "#communityRef"
-
},
-
"title": {
-
"type": "string"
-
},
-
"text": {
-
"type": "string"
-
},
-
"textFacets": {
-
"type": "array",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.richtext.facet"
-
}
-
},
-
"embed": {
-
"type": "union",
-
"description": "Embedded content (images, video, link preview, or quoted post)",
-
"refs": [
-
"social.coves.embed.images#view",
-
"social.coves.embed.video#view",
-
"social.coves.embed.external#view",
-
"social.coves.embed.record#view",
-
"social.coves.embed.recordWithMedia#view"
-
]
-
},
-
"language": {
-
"type": "string",
-
"format": "language"
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
},
-
"editedAt": {
-
"type": "string",
-
"format": "datetime"
-
},
-
"indexedAt": {
-
"type": "string",
-
"format": "datetime",
-
"description": "When this post was indexed by the AppView"
-
},
-
"stats": {
-
"type": "ref",
-
"ref": "#postStats"
-
},
-
"viewer": {
-
"type": "ref",
-
"ref": "#viewerState"
-
}
-
}
-
},
-
"authorView": {
-
"type": "object",
-
"required": ["did", "handle"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"handle": {
-
"type": "string",
-
"format": "handle"
-
},
-
"displayName": {
-
"type": "string"
-
},
-
"avatar": {
-
"type": "string",
-
"format": "uri"
-
},
-
"reputation": {
-
"type": "integer",
-
"description": "Author's reputation in the community"
-
}
-
}
-
},
-
"communityRef": {
-
"type": "object",
-
"required": ["did", "name"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"name": {
-
"type": "string"
-
},
-
"avatar": {
-
"type": "string",
-
"format": "uri"
-
}
-
}
-
},
-
"notFoundPost": {
-
"type": "object",
-
"description": "Post was not found (deleted, never indexed, or invalid URI)",
-
"required": ["uri", "notFound"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri"
-
},
-
"notFound": {
-
"type": "boolean",
-
"const": true
-
}
-
}
-
},
-
"blockedPost": {
-
"type": "object",
-
"description": "Post is blocked due to viewer blocking author/community, or community moderation",
-
"required": ["uri", "blocked"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri"
-
},
-
"blocked": {
-
"type": "boolean",
-
"const": true
-
},
-
"blockedBy": {
-
"type": "string",
-
"enum": ["author", "community", "moderator"],
-
"description": "What caused the block: viewer blocked author, viewer blocked community, or post was removed by moderators"
-
},
-
"author": {
-
"type": "ref",
-
"ref": "#blockedAuthor"
-
},
-
"community": {
-
"type": "ref",
-
"ref": "#blockedCommunity"
-
}
-
}
-
},
-
"blockedAuthor": {
-
"type": "object",
-
"description": "Minimal author info for blocked posts",
-
"required": ["did"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
}
-
}
-
},
-
"blockedCommunity": {
-
"type": "object",
-
"description": "Minimal community info for blocked posts",
-
"required": ["did"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"name": {
-
"type": "string"
-
}
-
}
-
},
-
"postStats": {
-
"type": "object",
-
"required": ["upvotes", "downvotes", "score", "commentCount"],
-
"properties": {
-
"upvotes": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"downvotes": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"score": {
-
"type": "integer",
-
"description": "Calculated score (upvotes - downvotes)"
-
},
-
"commentCount": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"shareCount": {
-
"type": "integer",
-
"minimum": 0
-
},
-
"tagCounts": {
-
"type": "object",
-
"description": "Aggregate counts of tags applied by community members",
-
"additionalProperties": {
-
"type": "integer",
-
"minimum": 0
-
}
-
}
-
}
-
},
-
"viewerState": {
-
"type": "object",
-
"properties": {
-
"vote": {
-
"type": "string",
-
"enum": ["up", "down"],
-
"description": "Viewer's vote on this post"
-
},
-
"voteUri": {
-
"type": "string",
-
"format": "at-uri"
-
},
-
"saved": {
-
"type": "boolean"
-
},
-
"savedUri": {
-
"type": "string",
-
"format": "at-uri"
-
},
-
"tags": {
-
"type": "array",
-
"description": "Tags applied by the viewer to this post",
-
"items": {
-
"type": "string",
-
"maxLength": 32
-
}
-
}
-
}
-
}
-
}
-
}
-99
internal/atproto/lexicon/social/coves/post/getCrosspostChain.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.getCrosspostChain",
-
"defs": {
-
"main": {
-
"type": "procedure",
-
"description": "Get all crossposts in a crosspost chain for a given post",
-
"input": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of any post in the crosspost chain"
-
}
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["crossposts"],
-
"properties": {
-
"crossposts": {
-
"type": "array",
-
"description": "All posts in the crosspost chain",
-
"items": {
-
"type": "ref",
-
"ref": "#crosspostView"
-
}
-
}
-
}
-
}
-
}
-
},
-
"crosspostView": {
-
"type": "object",
-
"required": ["uri", "community", "author", "createdAt"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the post"
-
},
-
"community": {
-
"type": "object",
-
"required": ["uri", "name"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the community"
-
},
-
"name": {
-
"type": "string",
-
"description": "Display name of the community"
-
},
-
"handle": {
-
"type": "string",
-
"description": "Handle of the community"
-
}
-
}
-
},
-
"author": {
-
"type": "object",
-
"required": ["did", "handle"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did"
-
},
-
"handle": {
-
"type": "string"
-
},
-
"displayName": {
-
"type": "string"
-
},
-
"avatar": {
-
"type": "string",
-
"format": "uri"
-
}
-
}
-
},
-
"isOriginal": {
-
"type": "boolean",
-
"description": "Whether this is the original post in the chain"
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
}
-
}
-
}
-
}
-
}
-129
internal/atproto/lexicon/social/coves/post/record.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.record",
-
"defs": {
-
"main": {
-
"type": "record",
-
"description": "A unified post record supporting multiple content types",
-
"key": "tid",
-
"record": {
-
"type": "object",
-
"required": ["$type", "community", "author", "createdAt"],
-
"properties": {
-
"$type": {
-
"type": "string",
-
"const": "social.coves.post.record",
-
"description": "The record type identifier"
-
},
-
"community": {
-
"type": "string",
-
"format": "at-identifier",
-
"description": "DID or handle of the community this was posted to"
-
},
-
"author": {
-
"type": "string",
-
"format": "did",
-
"description": "DID of the user who created this post. Server-populated from authenticated session; clients MUST NOT provide this field. Required for attribution, moderation, and accountability."
-
},
-
"title": {
-
"type": "string",
-
"maxGraphemes": 300,
-
"maxLength": 3000,
-
"description": "Post title (optional for microblog, image, and video posts)"
-
},
-
"content": {
-
"type": "string",
-
"maxLength": 50000,
-
"description": "Post content - main text for text posts, description for media, etc."
-
},
-
"facets": {
-
"type": "array",
-
"description": "Rich text annotations for content",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.richtext.facet"
-
}
-
},
-
"embed": {
-
"type": "union",
-
"description": "Embedded content - images, videos, external links, or quoted posts",
-
"refs": [
-
"social.coves.embed.images",
-
"social.coves.embed.video",
-
"social.coves.embed.external",
-
"social.coves.embed.post"
-
]
-
},
-
"originalAuthor": {
-
"type": "ref",
-
"ref": "#originalAuthor",
-
"description": "For microblog posts - information about the original author from federated platform"
-
},
-
"contentLabels": {
-
"type": "array",
-
"description": "Self-applied content labels",
-
"items": {
-
"type": "string",
-
"knownValues": ["nsfw", "spoiler", "violence"],
-
"maxLength": 32
-
}
-
},
-
"federatedFrom": {
-
"type": "ref",
-
"ref": "social.coves.federation.post",
-
"description": "Reference to original federated post (if applicable)"
-
},
-
"location": {
-
"type": "ref",
-
"ref": "social.coves.actor.profile#geoLocation",
-
"description": "Geographic location where post was created"
-
},
-
"crosspostOf": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "If this is a crosspost, AT-URI of the post this is a crosspost of"
-
},
-
"crosspostChain": {
-
"type": "array",
-
"description": "Array of AT-URIs of all posts in the crosspost chain (including this one)",
-
"items": {
-
"type": "string",
-
"format": "at-uri"
-
}
-
},
-
"createdAt": {
-
"type": "string",
-
"format": "datetime"
-
}
-
}
-
}
-
},
-
"originalAuthor": {
-
"type": "object",
-
"description": "Information about the original author from a federated platform",
-
"required": ["handle"],
-
"properties": {
-
"did": {
-
"type": "string",
-
"format": "did",
-
"description": "Original author's DID (if available)"
-
},
-
"handle": {
-
"type": "string",
-
"maxLength": 253,
-
"description": "Original author's handle"
-
},
-
"displayName": {
-
"type": "string",
-
"maxLength": 640,
-
"description": "Original author's display name"
-
},
-
"avatar": {
-
"type": "string",
-
"format": "uri",
-
"description": "URL to original author's avatar"
-
}
-
}
-
}
-
}
-
}
-80
internal/atproto/lexicon/social/coves/post/search.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.search",
-
"defs": {
-
"main": {
-
"type": "query",
-
"description": "Search for posts",
-
"parameters": {
-
"type": "params",
-
"required": ["q"],
-
"properties": {
-
"q": {
-
"type": "string",
-
"description": "Search query"
-
},
-
"community": {
-
"type": "string",
-
"format": "at-identifier",
-
"description": "Filter by specific community"
-
},
-
"author": {
-
"type": "string",
-
"format": "at-identifier",
-
"description": "Filter by author"
-
},
-
"type": {
-
"type": "string",
-
"enum": ["text", "image", "video", "article", "microblog"],
-
"description": "Filter by post type"
-
},
-
"tags": {
-
"type": "array",
-
"items": {
-
"type": "string"
-
},
-
"description": "Filter by tags"
-
},
-
"sort": {
-
"type": "string",
-
"enum": ["relevance", "new", "top"],
-
"default": "relevance"
-
},
-
"timeframe": {
-
"type": "string",
-
"enum": ["hour", "day", "week", "month", "year", "all"],
-
"default": "all"
-
},
-
"limit": {
-
"type": "integer",
-
"minimum": 1,
-
"maximum": 100,
-
"default": 50
-
},
-
"cursor": {
-
"type": "string"
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["posts"],
-
"properties": {
-
"posts": {
-
"type": "array",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.post.getFeed#feedPost"
-
}
-
},
-
"cursor": {
-
"type": "string"
-
}
-
}
-
}
-
}
-
}
-
}
-
}
-104
internal/atproto/lexicon/social/coves/post/update.json
···
-
{
-
"lexicon": 1,
-
"id": "social.coves.post.update",
-
"defs": {
-
"main": {
-
"type": "procedure",
-
"description": "Update an existing post",
-
"input": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the post to update"
-
},
-
"title": {
-
"type": "string",
-
"maxGraphemes": 300,
-
"maxLength": 3000,
-
"description": "Updated title"
-
},
-
"content": {
-
"type": "string",
-
"maxLength": 50000,
-
"description": "Updated content - main text for text posts, description for media, etc."
-
},
-
"facets": {
-
"type": "array",
-
"description": "Updated rich text annotations for content",
-
"items": {
-
"type": "ref",
-
"ref": "social.coves.richtext.facet"
-
}
-
},
-
"embed": {
-
"type": "union",
-
"description": "Updated embedded content (note: changing embed type may be restricted)",
-
"refs": [
-
"social.coves.embed.images",
-
"social.coves.embed.video",
-
"social.coves.embed.external",
-
"social.coves.embed.post"
-
]
-
},
-
"contentLabels": {
-
"type": "array",
-
"description": "Updated content labels",
-
"items": {
-
"type": "string",
-
"knownValues": ["nsfw", "spoiler", "violence"],
-
"maxLength": 32
-
}
-
},
-
"editNote": {
-
"type": "string",
-
"maxLength": 300,
-
"description": "Optional note explaining the edit"
-
}
-
}
-
}
-
},
-
"output": {
-
"encoding": "application/json",
-
"schema": {
-
"type": "object",
-
"required": ["uri", "cid"],
-
"properties": {
-
"uri": {
-
"type": "string",
-
"format": "at-uri",
-
"description": "AT-URI of the updated post"
-
},
-
"cid": {
-
"type": "string",
-
"format": "cid",
-
"description": "New CID of the updated post"
-
}
-
}
-
}
-
},
-
"errors": [
-
{
-
"name": "PostNotFound",
-
"description": "Post not found"
-
},
-
{
-
"name": "NotAuthorized",
-
"description": "User is not authorized to edit this post"
-
},
-
{
-
"name": "EditWindowExpired",
-
"description": "Edit window has expired (posts can only be edited within 24 hours)"
-
},
-
{
-
"name": "InvalidUpdate",
-
"description": "Invalid update operation (e.g., changing post type)"
-
}
-
]
-
}
-
}
-
}