···
# Communities PRD: Federated Forum System
3
+
**Status:** In Development
5
-
**Last Updated:** 2025-10-07
5
+
**Last Updated:** 2025-10-10
9
-
Coves communities are federated, instance-scoped forums built on atProto. Each community is identified by a scoped handle (`!gaming@coves.social`) and owned by a DID, enabling future portability and community governance.
9
+
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.
11
+
## Architecture Evolution
13
-
**V1 (MVP):** Instance-owned communities with scoped handles
14
-
**V2 (Post-Launch):** Cross-instance discovery and moderation signal federation
15
-
**V3 (Future):** Community-owned DIDs with migration capabilities via community voting
19
-
1. **Scoped by default:** All communities use `!name@instance.com` format
20
-
2. **DID-based ownership:** Communities are owned by DIDs (initially instance, eventually community)
21
-
3. **Web DID compatible:** Communities can use `did:web` for custom domains (e.g., `!photography@lens.club`)
22
-
4. **Federation-ready:** Design for cross-instance discovery and moderation from day one
23
-
5. **Community sovereignty:** Future path to community ownership and migration
25
-
## Identity & Namespace
27
-
### Community Handle Format
33
-
!gaming@coves.social
34
-
!photography@lens.club
36
-
!my-book-club@personal.coves.io
41
-
**V1: Instance-Owned**
45
-
"handle": "!gaming@coves.social",
46
-
"did": "did:web:coves.social:community:gaming",
47
-
"owner": "did:web:coves.social",
48
-
"createdBy": "did:plc:user123",
49
-
"hostedBy": "did:web:coves.social",
50
-
"created": "2025-10-07T12:00:00Z"
55
-
**Future: Community-Owned**
59
-
"handle": "!gaming@coves.social",
60
-
"did": "did:web:gaming.community",
61
-
"owner": "did:web:gaming.community",
62
-
"createdBy": "did:plc:user123",
63
-
"hostedBy": "did:web:coves.social",
66
-
"votingEnabled": true
72
-
### Why Scoped Names?
74
-
- **No namespace conflicts:** Each instance controls its own namespace
75
-
- **Clear ownership:** `@instance` shows who hosts it
76
-
- **Decentralized:** No global registry required
77
-
- **Web DID ready:** Communities can become `did:web` and use custom domains
78
-
- **Fragmentation handled socially:** Community governance and moderation quality drives membership
80
-
## Visibility & Discoverability
82
-
### Visibility Tiers
84
-
**Public (Default)**
85
-
- Indexed by home instance
86
-
- Appears in search results
87
-
- Listed in community directory
88
-
- Can be federated to other instances
91
-
- Accessible via direct link
92
-
- Not in search results
93
-
- Not in public directory
94
-
- Members can invite others
100
-
- Requires approval to join
102
-
### Discovery Configuration
105
-
type CommunityVisibility struct {
106
-
Level string // "public", "unlisted", "private"
107
-
AllowExternalDiscovery bool // Can other instances index this?
108
-
AllowedInstances []string // Whitelist (empty = all if public)
114
-
// Public gaming community, federate everywhere
116
-
"visibility": "public",
117
-
"allowExternalDiscovery": true,
118
-
"allowedInstances": []
13
+
### ✅ V2 Architecture (Current - 2025-10-10)
121
-
// Book club, public on home instance only
123
-
"visibility": "public",
124
-
"allowExternalDiscovery": false,
125
-
"allowedInstances": []
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
128
-
// Private beta testing community
130
-
"visibility": "private",
131
-
"allowExternalDiscovery": false,
132
-
"allowedInstances": ["coves.social", "trusted.instance"]
21
+
**Repository Structure:**
136
-
## Moderation & Federation
138
-
### Moderation Actions (Local Only)
140
-
Communities can be moderated locally by the hosting instance:
143
-
type ModerationAction struct {
144
-
CommunityDID string
145
-
Action string // "delist", "quarantine", "remove"
148
-
Timestamp time.Time
149
-
BroadcastSignal bool // Share with network?
23
+
Repository: at://did:plc:community789/social.coves.community.profile/self
24
+
Owner: did:plc:community789 (community owns itself)
25
+
Hosted By: did:web:coves.social (instance manages credentials)
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
156
-
- Removed from search/directory
157
-
- Existing members can still access
158
-
- Not deleted, just hidden
161
-
- Visible with warning label
162
-
- "This community may violate guidelines"
163
-
- Can still be accessed with acknowledgment
36
+
## ✅ Completed Features (2025-10-10)
166
-
- Community hidden from instance AppView
167
-
- Data still exists in firehose
168
-
- Other instances can choose to ignore removal
170
-
### Federation Reality
172
-
**What you can control:**
173
-
- What YOUR AppView indexes
174
-
- What moderation signals you broadcast
175
-
- What other instances' signals you honor
177
-
**What you cannot control:**
178
-
- Self-hosted PDS/AppView can index anything
179
-
- Other instances may ignore your moderation
180
-
- Community data lives in firehose regardless
182
-
**Moderation is local AppView filtering, not network-wide censorship.**
184
-
### Moderation Signal Federation (V2)
186
-
Instances can subscribe to each other's moderation feeds:
190
-
"moderationFeed": "did:web:coves.social:moderation",
191
-
"action": "remove",
192
-
"target": "did:web:coves.social:community:hate-speech",
193
-
"reason": "Violates community guidelines",
194
-
"timestamp": "2025-10-07T14:30:00Z",
195
-
"evidence": "https://coves.social/moderation/case/123"
199
-
Other instances can:
200
-
- Auto-apply trusted instance moderation
201
-
- Show warnings based on signals
202
-
- Ignore signals entirely
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] **Encryption at Rest:** PostgreSQL pgcrypto for sensitive credentials
43
+
- [x] **Write-Forward Pattern:** Service → PDS → Firehose → AppView
44
+
- [x] **Jetstream Consumer:** Real-time indexing from firehose
45
+
- [x] **V2 Validation:** Strict rkey="self" enforcement (no V1 compatibility)
47
+
### Security & Data Protection
48
+
- [x] **Encrypted Credentials:** Access/refresh tokens encrypted in database
49
+
- [x] **Credential Persistence:** PDS credentials survive server restarts
50
+
- [x] **JSON Exclusion:** Credentials never exposed in API responses (`json:"-"` tags)
51
+
- [x] **Password Hashing:** bcrypt for PDS account passwords
52
+
- [x] **Timeout Handling:** 30s timeout for write operations, 10s for reads
206
-
### ✅ Completed (2025-10-08)
55
+
- [x] **Communities Table:** Full metadata with V2 credential columns
56
+
- [x] **Subscriptions Table:** Lightweight feed following
57
+
- [x] **Memberships Table:** Active participation tracking
58
+
- [x] **Moderation Table:** Local moderation actions
59
+
- [x] **Encryption Keys Table:** Secure key management for pgcrypto
60
+
- [x] **Indexes:** Optimized for search, visibility filtering, and lookups
208
-
**Core Functionality:**
209
-
- [x] Create communities (instance-owned DID)
210
-
- [x] Scoped handle format (`!name@instance`)
211
-
- [x] Three visibility levels (public, unlisted, private)
212
-
- [x] Basic community metadata (name, description, rules)
213
-
- [x] Write-forward to PDS (communities as atProto records)
214
-
- [x] Jetstream consumer (index communities from firehose)
63
+
- [x] **CreateCommunity:** Provisions PDS account, creates record, persists credentials
64
+
- [x] **UpdateCommunity:** Uses community's own credentials (not instance credentials)
65
+
- [x] **GetCommunity:** Fetches from AppView DB with decrypted credentials
66
+
- [x] **ListCommunities:** Pagination, filtering, sorting
67
+
- [x] **SearchCommunities:** Full-text search on name/description
68
+
- [x] **Subscribe/Unsubscribe:** Create subscription records
69
+
- [x] **Handle Validation:** Scoped handle format (`!name@instance`)
70
+
- [x] **DID Generation:** Uses `did:plc` for portability
216
-
**Technical Infrastructure:**
217
-
- [x] Lexicon: `social.coves.community.profile` with `did` field (atProto compliant!)
218
-
- [x] DID format: `did:plc:xxx` (portable, federated)
219
-
- [x] PostgreSQL indexing for local communities
220
-
- [x] Service layer (business logic)
221
-
- [x] Repository layer (database)
222
-
- [x] Consumer layer (firehose indexing)
223
-
- [x] Environment config (`IS_DEV_ENV`, `PLC_DIRECTORY_URL`)
72
+
### Jetstream Consumer
73
+
- [x] **Profile Events:** Create, update, delete community profiles
74
+
- [x] **Subscription Events:** Index user subscriptions to communities
75
+
- [x] **V2 Enforcement:** Reject non-"self" rkeys (no V1 communities)
76
+
- [x] **Self-Ownership Validation:** Verify owner_did == did
77
+
- [x] **Error Handling:** Graceful handling of malformed events
225
-
**Critical Fixes:**
226
-
- [x] Fixed `record_uri` bug (now points to correct repository location)
227
-
- [x] Added required `did` field to lexicon (atProto compliance)
228
-
- [x] Consumer correctly separates community DID from repository DID
229
-
- [x] E2E test passes (PDS write → firehose → AppView indexing)
79
+
### Testing Coverage
80
+
- [x] **Integration Tests:** Full CRUD operations
81
+
- [x] **Credential Tests:** Persistence, encryption, decryption
82
+
- [x] **V2 Validation Tests:** Rkey enforcement, self-ownership
83
+
- [x] **Consumer Tests:** Firehose event processing
84
+
- [x] **Repository Tests:** Database operations
85
+
- [x] **Unit Tests:** Service layer logic, timeout handling
233
-
**API Endpoints (XRPC):**
234
-
- [x] `social.coves.community.create` (handler exists, needs testing)
235
-
- [ ] `social.coves.community.get` (handler exists, needs testing)
236
-
- [ ] `social.coves.community.list` (handler exists, needs testing)
237
-
- [ ] `social.coves.community.search` (handler exists, needs testing)
238
-
- [x] `social.coves.community.subscribe` (handler exists)
239
-
- [x] `social.coves.community.unsubscribe` (handler exists)
89
+
## 🚧 In Progress / Needs Testing
241
-
**Subscriptions & Memberships:**
242
-
- [x] Database schema (subscriptions, memberships tables)
243
-
- [x] Repository methods (subscribe, unsubscribe, list)
244
-
- [ ] Consumer processing (index subscription events from firehose)
245
-
- [ ] Membership tracking (convert subscription → membership on first post?)
91
+
### XRPC API Endpoints
92
+
**Status:** Handlers exist, need comprehensive E2E testing
247
-
### ⏳ TODO Before V1 Launch
94
+
- [ ] `social.coves.community.create` - **Handler exists**, needs E2E test with real PDS
95
+
- [ ] `social.coves.community.get` - **Handler exists**, needs E2E test
96
+
- [ ] `social.coves.community.update` - **Handler exists**, needs E2E test with community credentials
97
+
- [ ] `social.coves.community.list` - **Handler exists**, needs E2E test with pagination
98
+
- [ ] `social.coves.community.search` - **Handler exists**, needs E2E test with queries
99
+
- [ ] `social.coves.community.subscribe` - **Handler exists**, needs E2E test
100
+
- [ ] `social.coves.community.unsubscribe` - **Handler exists**, needs E2E test
250
-
- [ ] Test all XRPC endpoints end-to-end
251
-
- [ ] Implement OAuth middleware (protect create/update endpoints)
252
-
- [ ] Add authorization checks (who can create/update/delete?)
253
-
- [ ] Handle validation (prevent duplicate handles, validate DIDs)
254
-
- [ ] Rate limiting (prevent community spam)
103
+
- E2E tests that verify complete flow: HTTP → Service → PDS → Firehose → Consumer → DB → HTTP response
104
+
- Test with real PDS instance (not mocked)
105
+
- Verify Jetstream consumer picks up events in real-time
256
-
**Community Discovery:**
257
-
- [ ] Community list endpoint (pagination, filtering)
258
-
- [ ] Community search (full-text search on name/description)
259
-
- [ ] Visibility enforcement (respect public/unlisted/private)
260
-
- [ ] Federation config (respect `allowExternalDiscovery`)
107
+
### Posts in Communities
108
+
**Status:** Lexicon designed, implementation TODO
262
-
**Posts in Communities:**
- [ ] Extend `social.coves.post` lexicon with `community` field
264
-
- [ ] Create post endpoint (require community membership?)
265
-
- [ ] Feed generation (show posts in community)
111
+
- [ ] Create post endpoint (with community membership validation?)
112
+
- [ ] Feed generation for community posts
- [ ] Post consumer (index community posts from firehose)
114
+
- [ ] Community post count tracking
268
-
**Moderation (Basic):**
269
-
- [ ] Remove community from AppView (delist)
270
-
- [ ] Quarantine community (show warning)
271
-
- [ ] Moderation audit log
272
-
- [ ] Admin endpoints (for instance operators)
117
+
- Decide membership requirements for posting
118
+
- Design feed generation algorithm
119
+
- Implement post indexing in consumer
120
+
- Add tests for post creation/listing
274
-
**Testing & Documentation:**
275
-
- [ ] Integration tests for all flows
276
-
- [ ] API documentation (XRPC endpoints)
277
-
- [ ] Deployment guide (PDS setup, environment config)
278
-
- [ ] Migration guide (how to upgrade from test to production)
280
-
### Out of Scope (V2+)
124
+
## ⏳ TODO Before V1 Production Launch
282
-
- [ ] Moderation signal federation
283
-
- [ ] Community-owned DIDs
284
-
- [ ] Migration/portability
285
-
- [ ] Governance voting
286
-
- [ ] Custom domain DIDs
126
+
### Critical Security & Authorization
127
+
- [ ] **OAuth Middleware:** Protect create/update/delete endpoints
128
+
- [ ] **Authorization Checks:** Verify user is community creator/moderator
129
+
- [ ] **Rate Limiting:** Prevent community creation spam (e.g., 5 per user per hour)
130
+
- [ ] **Handle Collision Detection:** Prevent duplicate community handles
131
+
- [ ] **DID Validation:** Verify DIDs before accepting create requests
132
+
- [ ] **Token Refresh Logic:** Handle expired PDS access tokens
288
-
## Phase 2: Federation & Discovery
134
+
### Community Discovery & Visibility
135
+
- [ ] **Visibility Enforcement:** Respect public/unlisted/private settings in listings
136
+
- [ ] **Federation Config:** Honor `allowExternalDiscovery` flag
137
+
- [ ] **Search Relevance:** Implement ranking algorithm (members, activity, etc.)
138
+
- [ ] **Directory Endpoint:** Public community directory with filters
291
-
- Cross-instance community search
292
-
- Federated moderation signals
293
-
- Trust networks between instances
140
+
### Membership & Participation
141
+
- [ ] **Membership Tracking:** Auto-create membership on first post
142
+
- [ ] **Reputation System:** Track user participation per community
143
+
- [ ] **Subscription → Membership Flow:** Define conversion logic
144
+
- [ ] **Member Lists:** Endpoint to list community members
145
+
- [ ] **Moderator Assignment:** Allow creators to add moderators
297
-
// Cross-instance discovery
298
-
type FederationConfig struct {
299
-
DiscoverPeers []string // Other Coves instances to index
300
-
TrustModerationFrom []string // Auto-apply moderation signals
301
-
ShareCommunitiesWith []string // Allow these instances to index ours
147
+
### Moderation (Basic)
148
+
- [ ] **Delist Community:** Remove from search/directory
149
+
- [ ] **Quarantine Community:** Show warning label
150
+
- [ ] **Remove Community:** Hide from instance AppView
151
+
- [ ] **Moderation Audit Log:** Track all moderation actions
152
+
- [ ] **Admin Endpoints:** Instance operator tools
304
-
// Moderation trust network
305
-
type ModerationTrust struct {
307
-
TrustLevel string // "auto-apply", "show-warning", "ignore"
308
-
Categories []string // Which violations to trust ("spam", "nsfw", etc)
154
+
### Token Refresh & Resilience
155
+
- [ ] **Refresh Token Logic:** Auto-refresh expired PDS access tokens
156
+
- [ ] **Retry Mechanism:** Retry failed PDS calls with backoff
157
+
- [ ] **Credential Rotation:** Periodic password rotation for security
158
+
- [ ] **Error Recovery:** Graceful degradation if PDS is unavailable
312
-
**User Experience:**
160
+
### Performance & Scaling
161
+
- [ ] **Database Indexes:** Verify all common queries are indexed
162
+
- [ ] **Query Optimization:** Review N+1 query patterns
163
+
- [ ] **Caching Strategy:** Cache frequently accessed communities
164
+
- [ ] **Pagination Limits:** Enforce max results per request
165
+
- [ ] **Connection Pooling:** Optimize PDS HTTP client reuse
317
-
!golang@coves.social (45k members)
318
-
Hosted on coves.social
167
+
### Documentation & Deployment
168
+
- [ ] **API Documentation:** OpenAPI/Swagger specs for all endpoints
169
+
- [ ] **Deployment Guide:** Production setup instructions
170
+
- [ ] **Migration Guide:** How to upgrade from test to production
171
+
- [ ] **Monitoring Guide:** Metrics and alerting setup
172
+
- [ ] **Security Checklist:** Pre-launch security audit
321
-
!golang@dev.forums (12k members)
322
-
Hosted on dev.forums
323
-
Focused on systems programming
174
+
### Infrastructure & DNS
175
+
- [ ] **DNS Wildcard Setup:** Configure `*.communities.coves.social` for community handle resolution
176
+
- [ ] **Well-Known Endpoint:** Implement `.well-known/atproto-did` handler for `*.communities.coves.social` subdomains
326
-
!go@programming.zone (3k members)
327
-
Hosted on programming.zone
328
-
⚠️ Flagged by trusted moderators
332
-
## Implementation Log
180
+
## Out of Scope (Future Versions)
334
-
### 2025-10-08: DID Architecture & atProto Compliance
182
+
### V3: Federation & Discovery
183
+
- [ ] Cross-instance community search
184
+
- [ ] Federated moderation signals
185
+
- [ ] Trust networks between instances
186
+
- [ ] Moderation signal subscription
336
-
**Major Decisions:**
188
+
### V4: Community Governance
189
+
- [ ] Community-owned governance (voting on moderators)
190
+
- [ ] Migration voting (community votes to move instances)
191
+
- [ ] Custom domain DIDs (`did:web:gaming.community`)
192
+
- [ ] Governance thresholds and time locks
338
-
1. **Migrated from `did:coves` to `did:plc`**
339
-
- Communities now use proper PLC DIDs (portable across instances)
340
-
- Added `IS_DEV_ENV` flag (dev = generate without PLC registration, prod = register)
341
-
- Matches Bluesky's feed generator pattern
343
-
2. **Fixed Critical `record_uri` Bug**
344
-
- Problem: Consumer was setting community DID as repository owner
345
-
- Fix: Correctly separate community DID (entity) from repository DID (storage)
346
-
- Result: URIs now point to actual data location (federation works!)
196
+
## Recent Critical Fixes (2025-10-10)
348
-
3. **Added Required `did` Field to Lexicon**
349
-
- atProto research revealed communities MUST have their own DID field
350
-
- Matches `app.bsky.feed.generator` pattern (service has DID, record stored elsewhere)
351
-
- Enables future migration to community-owned repositories
198
+
### Security & Credential Management
199
+
**Issue:** PDS credentials were created but never persisted
200
+
**Fix:** Service layer now immediately persists credentials via `repo.Create()`
201
+
**Impact:** Communities can now be updated after creation (credentials survive restarts)
353
-
**Architecture Insights:**
203
+
**Issue:** Credentials stored in plaintext in PostgreSQL
204
+
**Fix:** Added pgcrypto encryption for access/refresh tokens
205
+
**Impact:** Database compromise no longer exposes active tokens
356
-
User Profile (Bluesky):
357
-
at://did:plc:user123/app.bsky.actor.profile/self
358
-
↑ Repository location IS the identity
359
-
No separate "did" field needed
207
+
**Issue:** UpdateCommunity used instance credentials instead of community credentials
208
+
**Fix:** Changed to use `existing.DID` and `existing.PDSAccessToken`
209
+
**Impact:** Updates now correctly authenticate as the community itself
361
-
Feed Generator (Bluesky):
362
-
at://did:plc:creator456/app.bsky.feed.generator/cool-feed
363
-
Record contains: {"did": "did:web:feedgen.service", ...}
364
-
↑ Service has own DID, record stored in creator's repo
211
+
### V2 Architecture Enforcement
212
+
**Issue:** Consumer accepted V1 communities with TID-based rkeys
213
+
**Fix:** Strict validation - only rkey="self" accepted
214
+
**Impact:** No legacy V1 data in production
366
-
Community (Coves V1):
367
-
at://did:plc:instance123/social.coves.community.profile/rkey
368
-
Record contains: {"did": "did:plc:community789", ...}
369
-
↑ Community has own DID, record stored in instance repo
371
-
Community (Coves V2 - Future):
372
-
at://did:plc:community789/social.coves.community.profile/self
373
-
Record contains: {"owner": "did:plc:instance123", ...}
374
-
↑ Community owns its own repo, instance manages it
379
-
1. **Keypair Management**: Coves can manage community keypairs (like Bluesky manages user keys)
380
-
2. **PDS Authentication**: Can create PDS accounts for communities, Coves stores credentials
381
-
3. **Migration Path**: Current V1 enables future V2 without breaking changes
385
-
- V1 (Current): Simple, ships fast, limited portability
386
-
- V2 (Future): Complex, true portability, matches atProto entity model
388
-
**Decision: Ship V1 now, plan V2 migration.**
216
+
**Issue:** PDS write operations timed out (10s too short)
217
+
**Fix:** Dynamic timeout - writes get 30s, reads get 10s
218
+
**Impact:** Community creation no longer fails on slow PDS operations
392
-
## CRITICAL: DID Architecture Decision (2025-10-08)
394
-
### Current State: Hybrid Approach
396
-
**V1 Implementation (Current):**
398
-
Community DID: did:plc:community789 (portable identity)
399
-
Repository: at://did:plc:instance123/social.coves.community.profile/rkey
400
-
Owner: did:plc:instance123 (instance manages it)
404
-
"did": "did:plc:community789", // Community's portable DID
405
-
"owner": "did:plc:instance123", // Instance owns the repository
406
-
"hostedBy": "did:plc:instance123", // Where it's currently hosted
407
-
"createdBy": "did:plc:user456" // User who created it
411
-
**Why this matters:**
412
-
- ✅ Community has portable DID (can be referenced across network)
413
-
- ✅ Record URI points to actual data location (federation works)
414
-
- ✅ Clear separation: community identity ≠ storage location
415
-
- ⚠️ Limited portability: Moving instances requires deleting/recreating record
417
-
### V2 Option: True Community Repositories
419
-
**Future Architecture (under consideration):**
421
-
Community DID: did:plc:community789
422
-
Repository: at://did:plc:community789/social.coves.community.profile/self
423
-
Owner: did:plc:instance123 (in metadata, not repo owner)
426
-
- Own PDS account (managed by Coves backend)
427
-
- Own signing keypair (stored by Coves, like Bluesky stores user keys)
428
-
- Own repository (true data portability)
432
-
- ✅ True portability: URI never changes when migrating
433
-
- ✅ Matches atProto entity model (feed generators, labelers)
434
-
- ✅ Community can move between instances via DID document update
437
-
- Coves must generate keypairs for each community
438
-
- Coves must create PDS accounts for each community
439
-
- Coves must securely store community credentials
440
-
- More infrastructure to manage
442
-
**Decision:** Start with V1 (current), plan for V2 migration path.
444
-
### Migration Path V1 → V2
446
-
When ready for true portability:
447
-
1. Generate keypair for existing community
448
-
2. Register community's DID document with PLC
449
-
3. Create PDS account for community (Coves manages credentials)
450
-
4. Migrate record from instance repo to community repo
451
-
5. Update AppView to index from new location
453
-
The `did` field in records makes this migration possible!
455
-
## Phase 3: Community Ownership
458
-
- Transfer ownership from instance to community
459
-
- Enable community governance
460
-
- Allow migration between instances
224
+
### `social.coves.community.profile`
225
+
**Status:** ✅ Implemented and tested
227
+
**Required Fields:**
228
+
- `handle` - atProto handle (DNS-resolvable, e.g., `gaming.communities.coves.social`)
229
+
- `name` - Short community name for !mentions (e.g., `gaming`)
230
+
- `createdBy` - DID of user who created community
231
+
- `hostedBy` - DID of hosting instance
232
+
- `visibility` - `"public"`, `"unlisted"`, or `"private"`
233
+
- `federation.allowExternalDiscovery` - Boolean
464
-
**Governance System:**
466
-
type CommunityGovernance struct {
468
-
VotingPower string // "one-person-one-vote", "reputation-weighted"
469
-
QuorumPercent int // % required for votes to pass
470
-
Moderators []string // DIDs with mod powers
235
+
**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.
474
-
**Migration Flow:**
476
-
1. Community votes on migration (e.g., from coves.social to gaming.forum)
477
-
2. Vote passes (66% threshold)
478
-
3. Community DID ownership transfers
479
-
4. New instance re-indexes community data from firehose
480
-
5. Handle updates: !gaming@gaming.forum
481
-
6. Old instance can keep archive or redirect
237
+
**Optional Fields:**
238
+
- `displayName` - Display name for UI
239
+
- `description` - Community description
240
+
- `descriptionFacets` - Rich text annotations
241
+
- `avatar` - Blob reference for avatar image
242
+
- `banner` - Blob reference for banner image
243
+
- `moderationType` - `"moderator"` or `"sortition"`
244
+
- `contentWarnings` - Array of content warning types
245
+
- `memberCount` - Cached count
246
+
- `subscriberCount` - Cached count
487
-
"community": "!gaming@gaming.forum",
488
-
"did": "did:web:gaming.community",
489
-
"previousHost": "did:web:coves.social",
490
-
"currentHost": "did:web:gaming.forum",
491
-
"transferredAt": "2025-12-15T10:00:00Z",
492
-
"governanceSignatures": ["sig1", "sig2", "sig3"]
248
+
### `social.coves.community.subscription`
249
+
**Status:** ✅ Schema exists, consumer TODO
498
-
### `social.coves.community`
503
-
"id": "social.coves.community",
510
-
"required": ["handle", "name", "createdAt"],
514
-
"description": "Scoped handle (!name@instance)"
519
-
"description": "Display name"
527
-
"items": {"type": "string"}
531
-
"enum": ["public", "unlisted", "private"],
532
-
"default": "public"
537
-
"allowExternalDiscovery": {"type": "boolean", "default": true},
538
-
"allowedInstances": {
540
-
"items": {"type": "string"}
546
-
"description": "DID of community owner"
550
-
"description": "DID of user who created community"
554
-
"description": "DID of hosting instance"
558
-
"format": "datetime"
252
+
- `community` - DID of community being subscribed to
253
+
- `subscribedAt` - Timestamp
### `social.coves.post` (Community Extension)
574
-
"description": "DID of community this post belongs to"
259
+
- `community` - Optional DID of community this post belongs to
580
-
## Technical Architecture
585
-
User creates community
587
-
PDS creates community record
589
-
Firehose broadcasts creation
591
-
AppView indexes community (if allowed)
593
-
PostgreSQL stores community metadata
595
-
Community appears in local search/directory
598
-
### Database Schema (AppView)
601
-
CREATE TABLE communities (
602
-
id SERIAL PRIMARY KEY,
603
-
did TEXT UNIQUE NOT NULL,
604
-
handle TEXT UNIQUE NOT NULL, -- !name@instance
605
-
name TEXT NOT NULL,
608
-
visibility TEXT NOT NULL DEFAULT 'public',
609
-
federation_config JSONB,
610
-
owner_did TEXT NOT NULL,
611
-
created_by_did TEXT NOT NULL,
612
-
hosted_by_did TEXT NOT NULL,
613
-
created_at TIMESTAMP NOT NULL,
614
-
updated_at TIMESTAMP NOT NULL,
615
-
member_count INTEGER DEFAULT 0,
616
-
post_count INTEGER DEFAULT 0
619
-
CREATE INDEX idx_communities_handle ON communities(handle);
620
-
CREATE INDEX idx_communities_visibility ON communities(visibility);
621
-
CREATE INDEX idx_communities_hosted_by ON communities(hosted_by_did);
623
-
CREATE TABLE community_moderation (
624
-
id SERIAL PRIMARY KEY,
625
-
community_did TEXT NOT NULL REFERENCES communities(did),
626
-
action TEXT NOT NULL, -- 'delist', 'quarantine', 'remove'
628
-
instance_did TEXT NOT NULL,
629
-
broadcast BOOLEAN DEFAULT FALSE,
630
-
created_at TIMESTAMP NOT NULL
634
-
## API Endpoints (XRPC)
639
-
social.coves.community.create
640
-
social.coves.community.get
641
-
social.coves.community.update
642
-
social.coves.community.list
643
-
social.coves.community.search
644
-
social.coves.community.join
645
-
social.coves.community.leave
649
-
### V3 (Governance)
652
-
social.coves.community.transferOwnership
653
-
social.coves.community.proposeVote
654
-
social.coves.community.castVote
655
-
social.coves.community.migrate
661
-
- [ ] Communities can be created with scoped handles
662
-
- [ ] Posts can be made to communities
663
-
- [ ] Community discovery works on local instance
664
-
- [ ] All three visibility levels function correctly
665
-
- [ ] Basic moderation (delist/remove) works
667
-
### V2 (Federation)
668
-
- [ ] Cross-instance community search returns results
669
-
- [ ] Moderation signals are broadcast and received
670
-
- [ ] Trust networks prevent spam communities
672
-
### V3 (Governance)
673
-
- [ ] Community ownership can be transferred
674
-
- [ ] Voting system enables community decisions
675
-
- [ ] Communities can migrate between instances
677
-
## Security Considerations
265
+
### Pre-Launch Checklist
266
+
- [ ] All XRPC endpoints have E2E tests
267
+
- [ ] OAuth authentication working on all protected endpoints
268
+
- [ ] Rate limiting prevents abuse
269
+
- [ ] Communities can be created, updated, searched, and subscribed to
270
+
- [ ] Jetstream consumer indexes events in < 1 second
271
+
- [ ] Database handles 10,000+ communities without performance issues
272
+
- [ ] Security audit completed
679
-
### Every Operation Must:
680
-
- [ ] Validate DID ownership
681
-
- [ ] Check community visibility settings
682
-
- [ ] Verify instance authorization
683
-
- [ ] Use parameterized queries
684
-
- [ ] Rate limit community creation
685
-
- [ ] Log moderation actions
274
+
### V1 Launch Goals
275
+
- Communities can be created with scoped handles
276
+
- Posts can be made to communities (when implemented)
277
+
- Community discovery works on local instance
278
+
- All three visibility levels function correctly
279
+
- Basic moderation (delist/remove) works
687
-
### Risks & Mitigations:
689
-
**Community Squatting**
690
-
- Risk: Instance creates popular names and sits on them
691
-
- Mitigation: Activity requirements (auto-archive inactive communities)
283
+
## Technical Decisions Log
693
-
**Spam Communities**
694
-
- Risk: Bad actors create thousands of spam communities
695
-
- Mitigation: Rate limits, moderation signals, trust networks
285
+
### 2025-10-11: Single Handle Field (atProto-Compliant)
286
+
**Decision:** Use single `handle` field containing DNS-resolvable atProto handle; remove `atprotoHandle` field
697
-
**Migration Abuse**
698
-
- Risk: Community ownership stolen via fake votes
699
-
- Mitigation: Governance thresholds, time locks, signature verification
289
+
- Matches Bluesky pattern: `app.bsky.actor.profile` has one `handle` field
290
+
- Reduces confusion about which handle is "real"
291
+
- Simplifies lexicon (one field vs two)
292
+
- `!gaming@coves.social` display format is client-side UX concern, not protocol concern
293
+
- Follows separation of concerns: protocol layer uses DNS handles, UI layer formats for display
702
-
- Risk: Private communities discovered via firehose
703
-
- Mitigation: Encrypt sensitive metadata, only index allowed instances
295
+
**Implementation:**
296
+
- Lexicon: `handle` = `gaming.communities.coves.social` (DNS-resolvable)
297
+
- Client derives display: `!${name}@${instance}` from `name` + parsed instance
298
+
- Rich text facets can encode community mentions with `!` prefix for UX
300
+
**Trade-offs Accepted:**
301
+
- Clients must parse/format for display (but already do this for `@user` mentions)
302
+
- No explicit "display handle" in record (but `displayName` serves this purpose)
707
-
1. **Should we support community aliases?** (e.g., `!gaming` → `!videogames`)
708
-
2. **What's the minimum member count for community creation?** (prevent spam)
709
-
3. **How do we handle abandoned communities?** (creator leaves, no mods)
710
-
4. **Should communities have their own PDS?** (advanced self-hosting)
711
-
5. **Cross-posting between communities?** (one post in multiple communities)
713
-
## Migration from V1 → V2 → V3
306
+
### 2025-10-10: V2 Architecture Completed
307
+
- Migrated from instance-owned to community-owned repositories
308
+
- Each community now has own PDS account
309
+
- Credentials encrypted at rest using pgcrypto
310
+
- Strict V2 enforcement (no V1 compatibility)
715
-
### V1 to V2 (Adding Federation)
716
-
- Backward compatible: All V1 communities work in V2
717
-
- New fields added to lexicon (optional)
718
-
- Existing communities opt-in to federation
312
+
### 2025-10-08: DID Architecture & atProto Compliance
313
+
- Migrated from `did:coves` to `did:plc` (portable DIDs)
314
+
- Added required `did` field to lexicon
315
+
- Fixed critical `record_uri` bug
316
+
- Matches Bluesky feed generator pattern
720
-
### V2 to V3 (Community Ownership)
721
-
- Instance can propose ownership transfer to community
722
-
- Community votes to accept
723
-
- DID ownership updates
724
-
- No breaking changes to existing communities
- 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
731
-
- Coves Builder Guide: `/docs/CLAUDE-BUILD.md`
733
-
## Approval & Sign-Off
735
-
- [ ] Product Lead Review
736
-
- [ ] Engineering Lead Review
737
-
- [ ] Security Review
738
-
- [ ] Legal/Policy Review (especially moderation aspects)
743
-
1. Review and approve PRD
744
-
2. Create V1 implementation tickets
745
-
3. Design lexicon schema
746
-
4. Build community creation flow
747
-
5. Implement local discovery
748
-
6. Write integration tests
325
+
- PLC Directory: https://plc.directory