A community based topic aggregation platform built on atproto
1# Communities PRD: Federated Forum System
2
3**Status:** In Development
4**Owner:** Platform Team
5**Last Updated:** 2025-10-17
6
7## Overview
8
9Coves communities are federated, instance-scoped forums built on atProto. Each community is identified by a scoped handle (`!gaming@coves.social`) and owns its own atProto repository, enabling true portability and decentralized governance.
10
11## Architecture Evolution
12
13### ✅ V2 Architecture (Current - 2025-10-10)
14
15**Communities own their own repositories:**
16- Each community has its own DID (`did:plc:xxx`)
17- Each community owns its own atProto repository (`at://community_did/...`)
18- Each community has its own PDS account (managed by Coves backend)
19- Communities are truly portable - can migrate between instances by updating DID document
20
21**Repository Structure:**
22```
23Repository: at://did:plc:community789/social.coves.community.profile/self
24Owner: did:plc:community789 (community owns itself)
25Hosted By: did:web:coves.social (instance manages credentials)
26```
27
28**Key Benefits:**
29- ✅ True atProto compliance (matches feed generators, labelers)
30- ✅ Portable URIs (never change when migrating instances)
31- ✅ Self-owned identity model
32- ✅ Standard rkey="self" for singleton profiles
33
34---
35
36## ✅ Completed Features (Updated 2025-10-17)
37
38### Core Infrastructure
39- [x] **V2 Architecture:** Communities own their own repositories
40- [x] **PDS Account Provisioning:** Automatic account creation for each community
41- [x] **Credential Management:** Secure storage of community PDS credentials
42- [x] **Token Refresh:** Automatic refresh of expired access tokens (completed 2025-10-17)
43- [x] **Encryption at Rest:** PostgreSQL pgcrypto for sensitive credentials
44- [x] **Write-Forward Pattern:** Service → PDS → Firehose → AppView
45- [x] **Jetstream Consumer:** Real-time indexing from firehose
46- [x] **V2 Validation:** Strict rkey="self" enforcement (no V1 compatibility)
47
48### Security & Data Protection
49- [x] **Encrypted Credentials:** Access/refresh tokens encrypted in database
50- [x] **Credential Persistence:** PDS credentials survive server restarts
51- [x] **Automatic Token Refresh:** Tokens refresh 5 minutes before expiration (completed 2025-10-17)
52- [x] **Password Fallback:** Re-authentication when refresh tokens expire
53- [x] **Concurrency Safety:** Per-community mutex prevents refresh race conditions
54- [x] **JSON Exclusion:** Credentials never exposed in API responses (`json:"-"` tags)
55- [x] **Password Encryption:** Encrypted (not hashed) for session creation fallback
56- [x] **Timeout Handling:** 30s timeout for write operations, 10s for reads
57
58### Database Schema
59- [x] **Communities Table:** Full metadata with V2 credential columns
60- [x] **Subscriptions Table:** Lightweight feed following
61- [x] **Memberships Table:** Active participation tracking
62- [x] **Moderation Table:** Local moderation actions
63- [x] **Encryption Keys Table:** Secure key management for pgcrypto
64- [x] **Indexes:** Optimized for search, visibility filtering, and lookups
65
66### Service Layer
67- [x] **CreateCommunity:** Provisions PDS account, creates record, persists credentials
68- [x] **UpdateCommunity:** Uses community's own credentials (not instance credentials)
69- [x] **GetCommunity:** Fetches from AppView DB with decrypted credentials
70- [x] **ListCommunities:** Pagination, filtering, sorting
71- [x] **SearchCommunities:** Full-text search on name/description
72- [x] **Subscribe/Unsubscribe:** Create subscription records
73- [x] **Handle Validation:** Scoped handle format (`!name@instance`)
74- [x] **DID Generation:** Uses `did:plc` for portability
75
76### Jetstream Consumer
77- [x] **Profile Events:** Create, update, delete community profiles
78- [x] **Subscription Events:** Index user subscriptions to communities
79- [x] **V2 Enforcement:** Reject non-"self" rkeys (no V1 communities)
80- [x] **Self-Ownership Validation:** Verify owner_did == did
81- [x] **Error Handling:** Graceful handling of malformed events
82
83### Testing Coverage
84- [x] **Integration Tests:** Full CRUD operations
85- [x] **Credential Tests:** Persistence, encryption, decryption
86- [x] **Token Refresh Tests:** JWT parsing, credential updates, concurrency (completed 2025-10-17)
87- [x] **V2 Validation Tests:** Rkey enforcement, self-ownership
88- [x] **Consumer Tests:** Firehose event processing
89- [x] **Repository Tests:** Database operations
90- [x] **Unit Tests:** Service layer logic, timeout handling
91
92---
93
94## 🚧 In Progress / Needs Testing
95
96### XRPC API Endpoints
97**Status:** All core endpoints E2E tested! ✅
98
99**✅ E2E Tested (via community_e2e_test.go):**
100- [x] `social.coves.community.create` - Full E2E test with real PDS
101- [x] `social.coves.community.get` - E2E test validates HTTP endpoint
102- [x] `social.coves.community.list` - E2E test with pagination/filtering
103- [x] `social.coves.community.update` - E2E test verifies write-forward + PDS update
104- [x] `social.coves.community.subscribe` - E2E test verifies subscription in user's repo
105- [x] `social.coves.community.unsubscribe` - E2E test verifies PDS deletion
106
107**📍 Post-Alpha:**
108- [ ] `social.coves.community.search` - Handler exists, defer E2E testing to post-alpha
109
110**✅ OAuth Authentication Complete (2025-10-16):**
111- User access tokens now flow through middleware → handlers → service
112- Subscribe/unsubscribe operations use correct user-scoped credentials
113- All E2E tests validate real PDS authentication with user tokens
114
115---
116
117## ⚠️ Alpha Blockers (Must Complete Before Alpha Launch)
118
119### Critical Missing Features
120- [x] **Community Blocking:** ✅ COMPLETE - Users can block communities from their feeds
121 - ✅ Lexicon: `social.coves.community.block` record type implemented
122 - ✅ Service: `BlockCommunity()` / `UnblockCommunity()` / `GetBlockedCommunities()` / `IsBlocked()`
123 - ✅ Handlers: Block/unblock endpoints implemented
124 - ✅ Repository: Full blocking methods with indexing
125 - ✅ Jetstream Consumer: Real-time indexing of block events
126 - ✅ Integration tests: Comprehensive coverage
127 - **Completed:** 2025-10-16
128 - **Impact:** Users can now hide unwanted communities from their feeds
129
130### ✅ Critical Infrastructure - RESOLVED (2025-10-16)
131- [x] **✅ Subscription Indexing & ContentVisibility - COMPLETE**
132 - **Status:** Subscriptions now fully indexed in AppView with feed slider support
133 - **Completed:** 2025-10-16
134 - **What Was Fixed:**
135 1. ✅ Fixed critical collection name bug (`social.coves.actor.subscription` → `social.coves.community.subscription`)
136 2. ✅ Implemented ContentVisibility (1-5 slider) across all layers (handler, service, consumer, repository)
137 3. ✅ Production Jetstream consumer now running ([cmd/server/main.go:220-243](cmd/server/main.go#L220-L243))
138 4. ✅ Migration 008 adds `content_visibility` column with defaults and constraints
139 5. ✅ Atomic subscriber count updates (SubscribeWithCount/UnsubscribeWithCount)
140 6. ✅ DELETE operations properly handled (unsubscribe indexing)
141 7. ✅ Idempotent operations (safe for Jetstream event replays)
142 8. ✅ atProto naming compliance: singular namespace + `subject` field
143 - **Impact:**
144 - ✅ Users CAN subscribe/unsubscribe (writes to their PDS repo)
145 - ✅ AppView INDEXES subscriptions from Jetstream in real-time
146 - ✅ Can query user's subscriptions (data persisted with contentVisibility)
147 - ✅ Feed generation ENABLED (know who's subscribed with visibility preferences)
148 - ✅ Subscriber counts accurate (atomic updates)
149 - **Testing:**
150 - ✅ 13 comprehensive integration tests (subscription_indexing_test.go) - ALL PASSING
151 - ✅ Enhanced E2E tests verify complete flow (HTTP → PDS → Jetstream → AppView)
152 - ✅ ContentVisibility clamping tested (0→1, 10→5, defaults to 3)
153 - ✅ Idempotency verified (duplicate events handled gracefully)
154 - **Files:**
155 - Implementation Doc: [docs/IMPLEMENTATION_SUBSCRIPTION_INDEXING.md](docs/IMPLEMENTATION_SUBSCRIPTION_INDEXING.md)
156 - Lexicon: [internal/atproto/lexicon/social/coves/community/subscription.json](internal/atproto/lexicon/social/coves/community/subscription.json)
157 - Consumer: [internal/atproto/jetstream/community_consumer.go](internal/atproto/jetstream/community_consumer.go)
158 - Connector: [internal/atproto/jetstream/community_jetstream_connector.go](internal/atproto/jetstream/community_jetstream_connector.go)
159 - Migration: [internal/db/migrations/008_add_content_visibility_to_subscriptions.sql](internal/db/migrations/008_add_content_visibility_to_subscriptions.sql)
160 - Tests: [tests/integration/subscription_indexing_test.go](tests/integration/subscription_indexing_test.go)
161
162### Critical Security (High Priority)
163- [x] **OAuth Authentication:** ✅ COMPLETE - User access tokens flow end-to-end
164 - ✅ Middleware stores user access token in context
165 - ✅ Handlers extract and pass token to service
166 - ✅ Service uses user token for user repo operations (subscribe/unsubscribe)
167 - ✅ All E2E tests pass with real PDS authentication
168 - **Completed:** 2025-10-16
169
170- [x] **Token Refresh Logic:** ✅ COMPLETE - Auto-refresh expired PDS access tokens
171 - ✅ Automatic token refresh before PDS operations (5-minute buffer)
172 - ✅ Password fallback when refresh tokens expire (~2 months)
173 - ✅ Concurrency-safe with per-community mutex locking
174 - ✅ Atomic credential updates in database
175 - ✅ Integration tests and structured logging
176 - **Completed:** 2025-10-17
177 - **See:** [IMPLEMENTATION_TOKEN_REFRESH.md](docs/IMPLEMENTATION_TOKEN_REFRESH.md)
178
179---
180
181## 📍 Beta Features (High Priority - Post Alpha)
182
183### Content Rules System
184**Status:** Lexicon complete (2025-10-18), implementation TODO
185**Priority:** CRITICAL for Alpha - Enables community content policies
186
187Communities can define content posting restrictions via the `contentRules` object in their profile:
188
189**Key Features:**
190- ✅ Lexicon: `contentRules` object defined in `social.coves.community.profile`
191- [ ] Validation: Post creation validates against community rules
192- [ ] AppView indexing: Index post characteristics (embed_type, text_length)
193- [ ] Error handling: Clear `ContentRuleViolation` errors
194
195**Example Use Cases:**
196- **Text-only communities:** "AskCoves" requires text, blocks all embeds
197- **Image communities:** "CovesPics" requires at least 1 image
198- **No restrictions:** General communities allow all content types
199
200**See:** [PRD_GOVERNANCE.md - Content Rules System](PRD_GOVERNANCE.md#content-rules-system) for full details
201
202---
203
204### Posts in Communities
205**Status:** Lexicon designed, implementation TODO
206**Priority:** HIGHEST for Beta 1
207
208- [x] `social.coves.post` already has `community` field ✅
209- [x] Removed `postType` enum in favor of content rules ✅ (2025-10-18)
210- [ ] Create post endpoint with content rules validation
211- [ ] Feed generation for community posts
212- [ ] Post consumer (index community posts from firehose)
213- [ ] Community post count tracking
214- [ ] Decide membership requirements for posting
215
216**Without posts, communities exist but can't be used!**
217
218---
219
220## 📍 Beta Features (Lower Priority)
221
222### Membership System
223**Status:** Lexicon exists, design decisions needed
224**Deferred:** Answer design questions before implementing
225
226- [ ] Decide: Auto-join on first post vs explicit join?
227- [ ] Decide: Reputation tracking in lexicon vs AppView only?
228- [ ] Implement membership record creation (if explicit join)
229- [ ] Member lists endpoint
230- [ ] Reputation tracking (if in lexicon)
231
232### Community Management
233- [ ] **Community Deletion:** Soft delete / permanent delete
234- [ ] **Wiki System:** Lexicon exists, no implementation
235- [ ] **Advanced Rules:** Separate rules records, moderation config
236- [ ] **Moderator Management:** Assign/remove moderators (governance work)
237- [ ] **Categories:** REMOVE from lexicon and code (not needed)
238
239### User Features
240- [ ] **Saved Items:** Save posts/comments for later
241- [ ] **User Flairs:** Per-community user flair (design TBD)
242
243### Instance Moderation
244- [ ] **Delist Community:** Remove from search/directory
245- [ ] **Quarantine Community:** Show warning label
246- [ ] **Remove Community:** Hide from instance AppView
247- [ ] **Moderation Audit Log:** Track all moderation actions
248
249---
250
251## ⏳ TODO Before V1 Production Launch
252
253### Community Discovery & Visibility
254- [ ] **Visibility Enforcement:** Respect public/unlisted/private settings in listings
255- [ ] **Federation Config:** Honor `allowExternalDiscovery` flag
256- [ ] **Search Relevance:** Implement ranking algorithm (members, activity, etc.)
257- [ ] **Directory Endpoint:** Public community directory with filters
258- [ ] **Rate Limiting:** Prevent community creation spam (e.g., 5 per user per hour)
259- [ ] **Handle Collision Detection:** Prevent duplicate community handles
260- [ ] **DID Validation:** Verify DIDs before accepting create requests
261
262### Token Refresh & Resilience
263- [ ] **Retry Mechanism:** Retry failed PDS calls with backoff
264- [ ] **Credential Rotation:** Periodic password rotation for security
265- [ ] **Error Recovery:** Graceful degradation if PDS is unavailable
266
267### Performance & Scaling
268- [ ] **Database Indexes:** Verify all common queries are indexed
269- [ ] **Query Optimization:** Review N+1 query patterns
270- [ ] **Caching Strategy:** Cache frequently accessed communities
271- [ ] **Pagination Limits:** Enforce max results per request
272- [ ] **Connection Pooling:** Optimize PDS HTTP client reuse
273
274### Documentation & Deployment
275- [ ] **API Documentation:** OpenAPI/Swagger specs for all endpoints
276- [ ] **Deployment Guide:** Production setup instructions
277- [ ] **Migration Guide:** How to upgrade from test to production
278- [ ] **Monitoring Guide:** Metrics and alerting setup
279- [ ] **Security Checklist:** Pre-launch security audit
280
281### Infrastructure & DNS
282- [ ] **DNS Wildcard Setup:** Configure `*.community.coves.social` for community handle resolution
283- [ ] **Well-Known Endpoint:** Implement `.well-known/atproto-did` handler for `*.community.coves.social` subdomains
284
285---
286
287## Out of Scope (Future Versions)
288
289### V3: Federation & Discovery
290- [ ] Cross-instance community search
291- [ ] Federated moderation signals
292- [ ] Trust networks between instances
293- [ ] Moderation signal subscription
294
295### V4: Community Governance
296- [ ] Community-owned governance (voting on moderators)
297- [ ] Migration voting (community votes to move instances)
298- [ ] Custom domain DIDs (`did:web:gaming.community`)
299- [ ] Governance thresholds and time locks
300
301---
302
303## Recent Critical Fixes (2025-10-10)
304
305### Security & Credential Management
306**Issue:** PDS credentials were created but never persisted
307**Fix:** Service layer now immediately persists credentials via `repo.Create()`
308**Impact:** Communities can now be updated after creation (credentials survive restarts)
309
310**Issue:** Credentials stored in plaintext in PostgreSQL
311**Fix:** Added pgcrypto encryption for access/refresh tokens
312**Impact:** Database compromise no longer exposes active tokens
313
314**Issue:** UpdateCommunity used instance credentials instead of community credentials
315**Fix:** Changed to use `existing.DID` and `existing.PDSAccessToken`
316**Impact:** Updates now correctly authenticate as the community itself
317
318### V2 Architecture Enforcement
319**Issue:** Consumer accepted V1 communities with TID-based rkeys
320**Fix:** Strict validation - only rkey="self" accepted
321**Impact:** No legacy V1 data in production
322
323**Issue:** PDS write operations timed out (10s too short)
324**Fix:** Dynamic timeout - writes get 30s, reads get 10s
325**Impact:** Community creation no longer fails on slow PDS operations
326
327---
328
329## Lexicon Summary
330
331### `social.coves.community.profile`
332**Status:** ✅ Implemented and tested
333
334**Required Fields:**
335- `handle` - atProto handle (DNS-resolvable, e.g., `gaming.community.coves.social`)
336- `name` - Short community name for !mentions (e.g., `gaming`)
337- `createdBy` - DID of user who created community
338- `hostedBy` - DID of hosting instance
339- `visibility` - `"public"`, `"unlisted"`, or `"private"`
340- `federation.allowExternalDiscovery` - Boolean
341
342**Note:** The `!gaming@coves.social` format is derived client-side from `name` + instance for UI display. The `handle` field contains only the DNS-resolvable atProto handle.
343
344**Optional Fields:**
345- `displayName` - Display name for UI
346- `description` - Community description
347- `descriptionFacets` - Rich text annotations
348- `avatar` - Blob reference for avatar image
349- `banner` - Blob reference for banner image
350- `moderationType` - `"moderator"` or `"sortition"`
351- `contentWarnings` - Array of content warning types
352- `contentRules` - Content posting restrictions (see [PRD_GOVERNANCE.md](PRD_GOVERNANCE.md#content-rules-system))
353 - `allowedEmbedTypes` - Array of allowed embed types (images, video, external, record)
354 - `requireText` - Whether text content is required
355 - `minTextLength` / `maxTextLength` - Text length constraints
356 - `requireTitle` - Whether title is required
357 - `minImages` / `maxImages` - Image count constraints
358 - `allowFederated` - Whether federated posts allowed
359- `memberCount` - Cached count
360- `subscriberCount` - Cached count
361
362### `social.coves.community.subscription`
363**Status:** ✅ Schema exists, consumer TODO
364
365**Fields:**
366- `community` - DID of community being subscribed to
367- `subscribedAt` - Timestamp
368
369### `social.coves.post` (Community Extension)
370**Status:** ⏳ TODO
371
372**New Field:**
373- `community` - Optional DID of community this post belongs to
374
375---
376
377## Success Metrics
378
379### Pre-Launch Checklist
380- [ ] All XRPC endpoints have E2E tests
381- [ ] OAuth authentication working on all protected endpoints
382- [ ] Rate limiting prevents abuse
383- [ ] Communities can be created, updated, searched, and subscribed to
384- [ ] Jetstream consumer indexes events in < 1 second
385- [ ] Database handles 10,000+ communities without performance issues
386- [ ] Security audit completed
387
388### V1 Launch Goals
389- Communities can be created with scoped handles
390- Posts can be made to communities (when implemented)
391- Community discovery works on local instance
392- All three visibility levels function correctly
393- Basic moderation (delist/remove) works
394
395---
396
397## Technical Decisions Log
398
399### 2025-10-18: Content Rules Over Post Type Enum
400**Decision:** Remove `postType` enum from post creation; use flexible `contentRules` in community profile instead
401
402**Rationale:**
403- `postType` enum forced users to explicitly select type (bad UX - app should infer from structure)
404- Enum was rigid - couldn't support nuanced rules like "text required, images optional"
405- Content rules are more extensible (add new constraints without changing post lexicon)
406- Follows atProto philosophy: describe data structure, not UI intent
407- Enables both community restrictions ("text only") AND user filtering ("show videos only")
408
409**Implementation:**
410- Community profile contains optional `contentRules` object
411- Post validation checks structure against rules at creation time
412- AppView indexes post characteristics (embed_type, text_length) for filtering
413- Errors use `ContentRuleViolation` instead of `InvalidPostType`
414
415**Examples:**
416- "AskCoves": `allowedEmbedTypes: []` + `requireText: true` + `minTextLength: 50`
417- "CovesPics": `allowedEmbedTypes: ["images"]` + `minImages: 1`
418- General communities: `contentRules: null` (no restrictions)
419
420**Trade-offs Accepted:**
421- Validation logic more complex than simple enum check (but more powerful)
422- Communities can't programmatically restrict to exact "article" vs "text" types (but structure-based rules are better)
423
424**See:** [PRD_GOVERNANCE.md - Content Rules System](PRD_GOVERNANCE.md#content-rules-system)
425
426---
427
428### 2025-10-11: Single Handle Field (atProto-Compliant)
429**Decision:** Use single `handle` field containing DNS-resolvable atProto handle; remove `atprotoHandle` field
430
431**Rationale:**
432- Matches Bluesky pattern: `app.bsky.actor.profile` has one `handle` field
433- Reduces confusion about which handle is "real"
434- Simplifies lexicon (one field vs two)
435- `!gaming@coves.social` display format is client-side UX concern, not protocol concern
436- Follows separation of concerns: protocol layer uses DNS handles, UI layer formats for display
437
438**Implementation:**
439- Lexicon: `handle` = `gaming.community.coves.social` (DNS-resolvable)
440- Client derives display: `!${name}@${instance}` from `name` + parsed instance
441- Rich text facets can encode community mentions with `!` prefix for UX
442
443**Trade-offs Accepted:**
444- Clients must parse/format for display (but already do this for `@user` mentions)
445- No explicit "display handle" in record (but `displayName` serves this purpose)
446
447---
448
449### 2025-10-10: V2 Architecture Completed
450- Migrated from instance-owned to community-owned repositories
451- Each community now has own PDS account
452- Credentials encrypted at rest using pgcrypto
453- Strict V2 enforcement (no V1 compatibility)
454
455### 2025-10-08: DID Architecture & atProto Compliance
456- Migrated from `did:coves` to `did:plc` (portable DIDs)
457- Added required `did` field to lexicon
458- Fixed critical `record_uri` bug
459- Matches Bluesky feed generator pattern
460
461---
462
463## References
464
465- atProto Lexicon Spec: https://atproto.com/specs/lexicon
466- DID Web Spec: https://w3c-ccg.github.io/did-method-web/
467- Bluesky Handle System: https://atproto.com/specs/handle
468- PLC Directory: https://plc.directory