Communities PRD: Federated Forum System#
Status: In Development Owner: Platform Team Last Updated: 2025-10-10
Overview#
Coves 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.
Architecture Evolution#
✅ V2 Architecture (Current - 2025-10-10)#
Communities own their own repositories:
- Each community has its own DID (
did:plc:xxx) - Each community owns its own atProto repository (
at://community_did/...) - Each community has its own PDS account (managed by Coves backend)
- Communities are truly portable - can migrate between instances by updating DID document
Repository Structure:
Repository: at://did:plc:community789/social.coves.community.profile/self
Owner: did:plc:community789 (community owns itself)
Hosted By: did:web:coves.social (instance manages credentials)
Key Benefits:
- ✅ True atProto compliance (matches feed generators, labelers)
- ✅ Portable URIs (never change when migrating instances)
- ✅ Self-owned identity model
- ✅ Standard rkey="self" for singleton profiles
✅ Completed Features (2025-10-10)#
Core Infrastructure#
- V2 Architecture: Communities own their own repositories
- PDS Account Provisioning: Automatic account creation for each community
- Credential Management: Secure storage of community PDS credentials
- Encryption at Rest: PostgreSQL pgcrypto for sensitive credentials
- Write-Forward Pattern: Service → PDS → Firehose → AppView
- Jetstream Consumer: Real-time indexing from firehose
- V2 Validation: Strict rkey="self" enforcement (no V1 compatibility)
Security & Data Protection#
- Encrypted Credentials: Access/refresh tokens encrypted in database
- Credential Persistence: PDS credentials survive server restarts
- JSON Exclusion: Credentials never exposed in API responses (
json:"-"tags) - Password Hashing: bcrypt for PDS account passwords
- Timeout Handling: 30s timeout for write operations, 10s for reads
Database Schema#
- Communities Table: Full metadata with V2 credential columns
- Subscriptions Table: Lightweight feed following
- Memberships Table: Active participation tracking
- Moderation Table: Local moderation actions
- Encryption Keys Table: Secure key management for pgcrypto
- Indexes: Optimized for search, visibility filtering, and lookups
Service Layer#
- CreateCommunity: Provisions PDS account, creates record, persists credentials
- UpdateCommunity: Uses community's own credentials (not instance credentials)
- GetCommunity: Fetches from AppView DB with decrypted credentials
- ListCommunities: Pagination, filtering, sorting
- SearchCommunities: Full-text search on name/description
- Subscribe/Unsubscribe: Create subscription records
- Handle Validation: Scoped handle format (
!name@instance) - DID Generation: Uses
did:plcfor portability
Jetstream Consumer#
- Profile Events: Create, update, delete community profiles
- Subscription Events: Index user subscriptions to communities
- V2 Enforcement: Reject non-"self" rkeys (no V1 communities)
- Self-Ownership Validation: Verify owner_did == did
- Error Handling: Graceful handling of malformed events
Testing Coverage#
- Integration Tests: Full CRUD operations
- Credential Tests: Persistence, encryption, decryption
- V2 Validation Tests: Rkey enforcement, self-ownership
- Consumer Tests: Firehose event processing
- Repository Tests: Database operations
- Unit Tests: Service layer logic, timeout handling
🚧 In Progress / Needs Testing#
XRPC API Endpoints#
Status: Handlers exist, need comprehensive E2E testing
-
social.coves.community.create- Handler exists, needs E2E test with real PDS -
social.coves.community.get- Handler exists, needs E2E test -
social.coves.community.update- Handler exists, needs E2E test with community credentials -
social.coves.community.list- Handler exists, needs E2E test with pagination -
social.coves.community.search- Handler exists, needs E2E test with queries -
social.coves.community.subscribe- Handler exists, needs E2E test -
social.coves.community.unsubscribe- Handler exists, needs E2E test
What's needed:
- E2E tests that verify complete flow: HTTP → Service → PDS → Firehose → Consumer → DB → HTTP response
- Test with real PDS instance (not mocked)
- Verify Jetstream consumer picks up events in real-time
Posts in Communities#
Status: Lexicon designed, implementation TODO
- Extend
social.coves.postlexicon withcommunityfield - Create post endpoint (with community membership validation?)
- Feed generation for community posts
- Post consumer (index community posts from firehose)
- Community post count tracking
What's needed:
- Decide membership requirements for posting
- Design feed generation algorithm
- Implement post indexing in consumer
- Add tests for post creation/listing
⏳ TODO Before V1 Production Launch#
Critical Security & Authorization#
- OAuth Middleware: Protect create/update/delete endpoints
- Authorization Checks: Verify user is community creator/moderator
- Rate Limiting: Prevent community creation spam (e.g., 5 per user per hour)
- Handle Collision Detection: Prevent duplicate community handles
- DID Validation: Verify DIDs before accepting create requests
- Token Refresh Logic: Handle expired PDS access tokens
Community Discovery & Visibility#
- Visibility Enforcement: Respect public/unlisted/private settings in listings
- Federation Config: Honor
allowExternalDiscoveryflag - Search Relevance: Implement ranking algorithm (members, activity, etc.)
- Directory Endpoint: Public community directory with filters
Membership & Participation#
- Membership Tracking: Auto-create membership on first post
- Reputation System: Track user participation per community
- Subscription → Membership Flow: Define conversion logic
- Member Lists: Endpoint to list community members
- Moderator Assignment: Allow creators to add moderators
Moderation (Basic)#
- Delist Community: Remove from search/directory
- Quarantine Community: Show warning label
- Remove Community: Hide from instance AppView
- Moderation Audit Log: Track all moderation actions
- Admin Endpoints: Instance operator tools
Token Refresh & Resilience#
- Refresh Token Logic: Auto-refresh expired PDS access tokens
- Retry Mechanism: Retry failed PDS calls with backoff
- Credential Rotation: Periodic password rotation for security
- Error Recovery: Graceful degradation if PDS is unavailable
Performance & Scaling#
- Database Indexes: Verify all common queries are indexed
- Query Optimization: Review N+1 query patterns
- Caching Strategy: Cache frequently accessed communities
- Pagination Limits: Enforce max results per request
- Connection Pooling: Optimize PDS HTTP client reuse
Documentation & Deployment#
- API Documentation: OpenAPI/Swagger specs for all endpoints
- Deployment Guide: Production setup instructions
- Migration Guide: How to upgrade from test to production
- Monitoring Guide: Metrics and alerting setup
- Security Checklist: Pre-launch security audit
Infrastructure & DNS#
- DNS Wildcard Setup: Configure
*.communities.coves.socialfor community handle resolution - Well-Known Endpoint: Implement
.well-known/atproto-didhandler for*.communities.coves.socialsubdomains
Out of Scope (Future Versions)#
V3: Federation & Discovery#
- Cross-instance community search
- Federated moderation signals
- Trust networks between instances
- Moderation signal subscription
V4: Community Governance#
- Community-owned governance (voting on moderators)
- Migration voting (community votes to move instances)
- Custom domain DIDs (
did:web:gaming.community) - Governance thresholds and time locks
Recent Critical Fixes (2025-10-10)#
Security & Credential Management#
Issue: PDS credentials were created but never persisted
Fix: Service layer now immediately persists credentials via repo.Create()
Impact: Communities can now be updated after creation (credentials survive restarts)
Issue: Credentials stored in plaintext in PostgreSQL Fix: Added pgcrypto encryption for access/refresh tokens Impact: Database compromise no longer exposes active tokens
Issue: UpdateCommunity used instance credentials instead of community credentials
Fix: Changed to use existing.DID and existing.PDSAccessToken
Impact: Updates now correctly authenticate as the community itself
V2 Architecture Enforcement#
Issue: Consumer accepted V1 communities with TID-based rkeys Fix: Strict validation - only rkey="self" accepted Impact: No legacy V1 data in production
Issue: PDS write operations timed out (10s too short) Fix: Dynamic timeout - writes get 30s, reads get 10s Impact: Community creation no longer fails on slow PDS operations
Lexicon Summary#
social.coves.community.profile#
Status: ✅ Implemented and tested
Required Fields:
handle- atProto handle (DNS-resolvable, e.g.,gaming.communities.coves.social)name- Short community name for !mentions (e.g.,gaming)createdBy- DID of user who created communityhostedBy- DID of hosting instancevisibility-"public","unlisted", or"private"federation.allowExternalDiscovery- Boolean
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.
Optional Fields:
displayName- Display name for UIdescription- Community descriptiondescriptionFacets- Rich text annotationsavatar- Blob reference for avatar imagebanner- Blob reference for banner imagemoderationType-"moderator"or"sortition"contentWarnings- Array of content warning typesmemberCount- Cached countsubscriberCount- Cached count
social.coves.community.subscription#
Status: ✅ Schema exists, consumer TODO
Fields:
community- DID of community being subscribed tosubscribedAt- Timestamp
social.coves.post (Community Extension)#
Status: ⏳ TODO
New Field:
community- Optional DID of community this post belongs to
Success Metrics#
Pre-Launch Checklist#
- All XRPC endpoints have E2E tests
- OAuth authentication working on all protected endpoints
- Rate limiting prevents abuse
- Communities can be created, updated, searched, and subscribed to
- Jetstream consumer indexes events in < 1 second
- Database handles 10,000+ communities without performance issues
- Security audit completed
V1 Launch Goals#
- Communities can be created with scoped handles
- Posts can be made to communities (when implemented)
- Community discovery works on local instance
- All three visibility levels function correctly
- Basic moderation (delist/remove) works
Technical Decisions Log#
2025-10-11: Single Handle Field (atProto-Compliant)#
Decision: Use single handle field containing DNS-resolvable atProto handle; remove atprotoHandle field
Rationale:
- Matches Bluesky pattern:
app.bsky.actor.profilehas onehandlefield - Reduces confusion about which handle is "real"
- Simplifies lexicon (one field vs two)
!gaming@coves.socialdisplay format is client-side UX concern, not protocol concern- Follows separation of concerns: protocol layer uses DNS handles, UI layer formats for display
Implementation:
- Lexicon:
handle=gaming.communities.coves.social(DNS-resolvable) - Client derives display:
!${name}@${instance}fromname+ parsed instance - Rich text facets can encode community mentions with
!prefix for UX
Trade-offs Accepted:
- Clients must parse/format for display (but already do this for
@usermentions) - No explicit "display handle" in record (but
displayNameserves this purpose)
2025-10-10: V2 Architecture Completed#
- Migrated from instance-owned to community-owned repositories
- Each community now has own PDS account
- Credentials encrypted at rest using pgcrypto
- Strict V2 enforcement (no V1 compatibility)
2025-10-08: DID Architecture & atProto Compliance#
- Migrated from
did:covestodid:plc(portable DIDs) - Added required
didfield to lexicon - Fixed critical
record_uribug - Matches Bluesky feed generator pattern
References#
- atProto Lexicon Spec: https://atproto.com/specs/lexicon
- DID Web Spec: https://w3c-ccg.github.io/did-method-web/
- Bluesky Handle System: https://atproto.com/specs/handle
- PLC Directory: https://plc.directory