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 586587PDS creates community record 588589Firehose broadcasts creation 590591AppView indexes community (if allowed) 592593PostgreSQL stores community metadata 594595Community 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