A community based topic aggregation platform built on atproto
1# Communities PRD: Federated Forum System
2
3**Status:** Draft
4**Owner:** Platform Team
5**Last Updated:** 2025-10-07
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 owned by a DID, enabling future portability and community governance.
10
11## Vision
12
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
16
17## Core Principles
18
191. **Scoped by default:** All communities use `!name@instance.com` format
202. **DID-based ownership:** Communities are owned by DIDs (initially instance, eventually community)
213. **Web DID compatible:** Communities can use `did:web` for custom domains (e.g., `!photography@lens.club`)
224. **Federation-ready:** Design for cross-instance discovery and moderation from day one
235. **Community sovereignty:** Future path to community ownership and migration
24
25## Identity & Namespace
26
27### Community Handle Format
28
29```
30!{name}@{instance}
31
32Examples:
33!gaming@coves.social
34!photography@lens.club
35!golang@dev.forums
36!my-book-club@personal.coves.io
37```
38
39### DID Ownership
40
41**V1: Instance-Owned**
42```json
43{
44 "community": {
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"
51 }
52}
53```
54
55**Future: Community-Owned**
56```json
57{
58 "community": {
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",
64 "governance": {
65 "type": "multisig",
66 "votingEnabled": true
67 }
68 }
69}
70```
71
72### Why Scoped Names?
73
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
79
80## Visibility & Discoverability
81
82### Visibility Tiers
83
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
89
90**Unlisted**
91- Accessible via direct link
92- Not in search results
93- Not in public directory
94- Members can invite others
95
96**Private**
97- Invite-only
98- Not discoverable
99- Not federated
100- Requires approval to join
101
102### Discovery Configuration
103
104```go
105type CommunityVisibility struct {
106 Level string // "public", "unlisted", "private"
107 AllowExternalDiscovery bool // Can other instances index this?
108 AllowedInstances []string // Whitelist (empty = all if public)
109}
110```
111
112**Examples:**
113```json
114// Public gaming community, federate everywhere
115{
116 "visibility": "public",
117 "allowExternalDiscovery": true,
118 "allowedInstances": []
119}
120
121// Book club, public on home instance only
122{
123 "visibility": "public",
124 "allowExternalDiscovery": false,
125 "allowedInstances": []
126}
127
128// Private beta testing community
129{
130 "visibility": "private",
131 "allowExternalDiscovery": false,
132 "allowedInstances": ["coves.social", "trusted.instance"]
133}
134```
135
136## Moderation & Federation
137
138### Moderation Actions (Local Only)
139
140Communities can be moderated locally by the hosting instance:
141
142```go
143type ModerationAction struct {
144 CommunityDID string
145 Action string // "delist", "quarantine", "remove"
146 Reason string
147 Instance string
148 Timestamp time.Time
149 BroadcastSignal bool // Share with network?
150}
151```
152
153**Action Types:**
154
155**Delist**
156- Removed from search/directory
157- Existing members can still access
158- Not deleted, just hidden
159
160**Quarantine**
161- Visible with warning label
162- "This community may violate guidelines"
163- Can still be accessed with acknowledgment
164
165**Remove**
166- Community hidden from instance AppView
167- Data still exists in firehose
168- Other instances can choose to ignore removal
169
170### Federation Reality
171
172**What you can control:**
173- What YOUR AppView indexes
174- What moderation signals you broadcast
175- What other instances' signals you honor
176
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
181
182**Moderation is local AppView filtering, not network-wide censorship.**
183
184### Moderation Signal Federation (V2)
185
186Instances can subscribe to each other's moderation feeds:
187
188```json
189{
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"
196}
197```
198
199Other instances can:
200- Auto-apply trusted instance moderation
201- Show warnings based on signals
202- Ignore signals entirely
203
204## MVP (V1) Scope
205
206### ✅ Completed (2025-10-08)
207
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)
215
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`)
224
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)
230
231### 🚧 In Progress
232
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)
240
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?)
246
247### ⏳ TODO Before V1 Launch
248
249**Critical Path:**
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)
255
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`)
261
262**Posts in Communities:**
263- [ ] Extend `social.coves.post` lexicon with `community` field
264- [ ] Create post endpoint (require community membership?)
265- [ ] Feed generation (show posts in community)
266- [ ] Post consumer (index community posts from firehose)
267
268**Moderation (Basic):**
269- [ ] Remove community from AppView (delist)
270- [ ] Quarantine community (show warning)
271- [ ] Moderation audit log
272- [ ] Admin endpoints (for instance operators)
273
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)
279
280### Out of Scope (V2+)
281
282- [ ] Moderation signal federation
283- [ ] Community-owned DIDs
284- [ ] Migration/portability
285- [ ] Governance voting
286- [ ] Custom domain DIDs
287
288## Phase 2: Federation & Discovery
289
290**Goals:**
291- Cross-instance community search
292- Federated moderation signals
293- Trust networks between instances
294
295**Features:**
296```go
297// Cross-instance discovery
298type 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
302}
303
304// Moderation trust network
305type ModerationTrust struct {
306 InstanceDID string
307 TrustLevel string // "auto-apply", "show-warning", "ignore"
308 Categories []string // Which violations to trust ("spam", "nsfw", etc)
309}
310```
311
312**User Experience:**
313```
314Search: "golang"
315
316Results:
317!golang@coves.social (45k members)
318 Hosted on coves.social
319 [Join]
320
321!golang@dev.forums (12k members)
322 Hosted on dev.forums
323 Focused on systems programming
324 [Join]
325
326!go@programming.zone (3k members)
327 Hosted on programming.zone
328 ⚠️ Flagged by trusted moderators
329 [View Details]
330```
331
332## Implementation Log
333
334### 2025-10-08: DID Architecture & atProto Compliance
335
336**Major Decisions:**
337
3381. **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
342
3432. **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!)
347
3483. **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
352
353**Architecture Insights:**
354
355```
356User Profile (Bluesky):
357 at://did:plc:user123/app.bsky.actor.profile/self
358 ↑ Repository location IS the identity
359 No separate "did" field needed
360
361Feed 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
365
366Community (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
370
371Community (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
375```
376
377**Key Findings:**
378
3791. **Keypair Management**: Coves can manage community keypairs (like Bluesky manages user keys)
3802. **PDS Authentication**: Can create PDS accounts for communities, Coves stores credentials
3813. **Migration Path**: Current V1 enables future V2 without breaking changes
382
383**Trade-offs:**
384
385- V1 (Current): Simple, ships fast, limited portability
386- V2 (Future): Complex, true portability, matches atProto entity model
387
388**Decision: Ship V1 now, plan V2 migration.**
389
390---
391
392## CRITICAL: DID Architecture Decision (2025-10-08)
393
394### Current State: Hybrid Approach
395
396**V1 Implementation (Current):**
397```
398Community DID: did:plc:community789 (portable identity)
399Repository: at://did:plc:instance123/social.coves.community.profile/rkey
400Owner: did:plc:instance123 (instance manages it)
401
402Record structure:
403{
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
408}
409```
410
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
416
417### V2 Option: True Community Repositories
418
419**Future Architecture (under consideration):**
420```
421Community DID: did:plc:community789
422Repository: at://did:plc:community789/social.coves.community.profile/self
423Owner: did:plc:instance123 (in metadata, not repo owner)
424
425Community gets:
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)
429```
430
431**Benefits:**
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
435
436**Complexity:**
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
441
442**Decision:** Start with V1 (current), plan for V2 migration path.
443
444### Migration Path V1 → V2
445
446When ready for true portability:
4471. Generate keypair for existing community
4482. Register community's DID document with PLC
4493. Create PDS account for community (Coves manages credentials)
4504. Migrate record from instance repo to community repo
4515. Update AppView to index from new location
452
453The `did` field in records makes this migration possible!
454
455## Phase 3: Community Ownership
456
457**Goals:**
458- Transfer ownership from instance to community
459- Enable community governance
460- Allow migration between instances
461
462**Features:**
463
464**Governance System:**
465```go
466type CommunityGovernance struct {
467 Enabled bool
468 VotingPower string // "one-person-one-vote", "reputation-weighted"
469 QuorumPercent int // % required for votes to pass
470 Moderators []string // DIDs with mod powers
471}
472```
473
474**Migration Flow:**
475```
4761. Community votes on migration (e.g., from coves.social to gaming.forum)
4772. Vote passes (66% threshold)
4783. Community DID ownership transfers
4794. New instance re-indexes community data from firehose
4805. Handle updates: !gaming@gaming.forum
4816. Old instance can keep archive or redirect
482```
483
484**DID Transfer:**
485```json
486{
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"]
493}
494```
495
496## Lexicon Design
497
498### `social.coves.community`
499
500```json
501{
502 "lexicon": 1,
503 "id": "social.coves.community",
504 "defs": {
505 "main": {
506 "type": "record",
507 "key": "tid",
508 "record": {
509 "type": "object",
510 "required": ["handle", "name", "createdAt"],
511 "properties": {
512 "handle": {
513 "type": "string",
514 "description": "Scoped handle (!name@instance)"
515 },
516 "name": {
517 "type": "string",
518 "maxLength": 64,
519 "description": "Display name"
520 },
521 "description": {
522 "type": "string",
523 "maxLength": 3000
524 },
525 "rules": {
526 "type": "array",
527 "items": {"type": "string"}
528 },
529 "visibility": {
530 "type": "string",
531 "enum": ["public", "unlisted", "private"],
532 "default": "public"
533 },
534 "federation": {
535 "type": "object",
536 "properties": {
537 "allowExternalDiscovery": {"type": "boolean", "default": true},
538 "allowedInstances": {
539 "type": "array",
540 "items": {"type": "string"}
541 }
542 }
543 },
544 "owner": {
545 "type": "string",
546 "description": "DID of community owner"
547 },
548 "createdBy": {
549 "type": "string",
550 "description": "DID of user who created community"
551 },
552 "hostedBy": {
553 "type": "string",
554 "description": "DID of hosting instance"
555 },
556 "createdAt": {
557 "type": "string",
558 "format": "datetime"
559 }
560 }
561 }
562 }
563 }
564}
565```
566
567### `social.coves.post` (Community Extension)
568
569```json
570{
571 "properties": {
572 "community": {
573 "type": "string",
574 "description": "DID of community this post belongs to"
575 }
576 }
577}
578```
579
580## Technical Architecture
581
582### Data Flow
583
584```
585User creates community
586 ↓
587PDS creates community record
588 ↓
589Firehose broadcasts creation
590 ↓
591AppView indexes community (if allowed)
592 ↓
593PostgreSQL stores community metadata
594 ↓
595Community appears in local search/directory
596```
597
598### Database Schema (AppView)
599
600```sql
601CREATE 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,
606 description TEXT,
607 rules JSONB,
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
617);
618
619CREATE INDEX idx_communities_handle ON communities(handle);
620CREATE INDEX idx_communities_visibility ON communities(visibility);
621CREATE INDEX idx_communities_hosted_by ON communities(hosted_by_did);
622
623CREATE 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'
627 reason TEXT,
628 instance_did TEXT NOT NULL,
629 broadcast BOOLEAN DEFAULT FALSE,
630 created_at TIMESTAMP NOT NULL
631);
632```
633
634## API Endpoints (XRPC)
635
636### V1 (MVP)
637
638```
639social.coves.community.create
640social.coves.community.get
641social.coves.community.update
642social.coves.community.list
643social.coves.community.search
644social.coves.community.join
645social.coves.community.leave
646```
647
648
649### V3 (Governance)
650
651```
652social.coves.community.transferOwnership
653social.coves.community.proposeVote
654social.coves.community.castVote
655social.coves.community.migrate
656```
657
658## Success Metrics
659
660### V1 (MVP)
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
666
667### V2 (Federation)
668- [ ] Cross-instance community search returns results
669- [ ] Moderation signals are broadcast and received
670- [ ] Trust networks prevent spam communities
671
672### V3 (Governance)
673- [ ] Community ownership can be transferred
674- [ ] Voting system enables community decisions
675- [ ] Communities can migrate between instances
676
677## Security Considerations
678
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
686
687### Risks & Mitigations:
688
689**Community Squatting**
690- Risk: Instance creates popular names and sits on them
691- Mitigation: Activity requirements (auto-archive inactive communities)
692
693**Spam Communities**
694- Risk: Bad actors create thousands of spam communities
695- Mitigation: Rate limits, moderation signals, trust networks
696
697**Migration Abuse**
698- Risk: Community ownership stolen via fake votes
699- Mitigation: Governance thresholds, time locks, signature verification
700
701**Privacy Leaks**
702- Risk: Private communities discovered via firehose
703- Mitigation: Encrypt sensitive metadata, only index allowed instances
704
705## Open Questions
706
7071. **Should we support community aliases?** (e.g., `!gaming` → `!videogames`)
7082. **What's the minimum member count for community creation?** (prevent spam)
7093. **How do we handle abandoned communities?** (creator leaves, no mods)
7104. **Should communities have their own PDS?** (advanced self-hosting)
7115. **Cross-posting between communities?** (one post in multiple communities)
712
713## Migration from V1 → V2 → V3
714
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
719
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
725
726## References
727
728- atProto Lexicon Spec: https://atproto.com/specs/lexicon
729- DID Web Spec: https://w3c-ccg.github.io/did-method-web/
730- Bluesky Handle System: https://atproto.com/specs/handle
731- Coves Builder Guide: `/docs/CLAUDE-BUILD.md`
732
733## Approval & Sign-Off
734
735- [ ] Product Lead Review
736- [ ] Engineering Lead Review
737- [ ] Security Review
738- [ ] Legal/Policy Review (especially moderation aspects)
739
740---
741
742**Next Steps:**
7431. Review and approve PRD
7442. Create V1 implementation tickets
7453. Design lexicon schema
7464. Build community creation flow
7475. Implement local discovery
7486. Write integration tests