A community based topic aggregation platform built on atproto

docs(communities): Document handle refactor and moderator storage decisions

Add comprehensive technical decisions to PRDs documenting architecture
choices for community handles and moderator record storage.

PRD_COMMUNITIES.md:
- Add technical decision: Single handle field (2025-10-11)
- Update lexicon summary to reflect DNS-valid handle approach
- Add DNS infrastructure checklist items (wildcard setup, well-known endpoint)
- Document that !name@instance format is client-side display only

PRD_GOVERNANCE.md:
- Add technical decision: Moderator records storage location (2025-10-11)
- Document security analysis comparing user repo vs community repo
- Explain attack vector for malicious self-hosted instances
- Rationale: Community repo provides better security and federation

Key decisions documented:
1. Single handle field matches Bluesky pattern (app.bsky.actor.profile)
2. Separation of concerns: protocol (DNS handle) vs presentation (!prefix)
3. Moderator records in community repo prevents forgery attacks
4. DNS wildcard required for *.communities.coves.social resolution

Infrastructure requirements added:
- [ ] DNS Wildcard Setup: Configure *.communities.coves.social
- [ ] Well-Known Endpoint: Implement .well-known/atproto-did handler

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

+60 -3
+29 -3
docs/PRD_COMMUNITIES.md
··· 171 171 - [ ] **Monitoring Guide:** Metrics and alerting setup 172 172 - [ ] **Security Checklist:** Pre-launch security audit 173 173 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 177 + 174 178 --- 175 179 176 180 ## Out of Scope (Future Versions) ··· 221 225 **Status:** ✅ Implemented and tested 222 226 223 227 **Required Fields:** 224 - - `handle` - Scoped handle (`!gaming@coves.social`) 225 - - `atprotoHandle` - Real atProto handle (`gaming.communities.coves.social`) 226 - - `name` - Community name 228 + - `handle` - atProto handle (DNS-resolvable, e.g., `gaming.communities.coves.social`) 229 + - `name` - Short community name for !mentions (e.g., `gaming`) 227 230 - `createdBy` - DID of user who created community 228 231 - `hostedBy` - DID of hosting instance 229 232 - `visibility` - `"public"`, `"unlisted"`, or `"private"` 230 233 - `federation.allowExternalDiscovery` - Boolean 234 + 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. 231 236 232 237 **Optional Fields:** 233 238 - `displayName` - Display name for UI ··· 276 281 --- 277 282 278 283 ## Technical Decisions Log 284 + 285 + ### 2025-10-11: Single Handle Field (atProto-Compliant) 286 + **Decision:** Use single `handle` field containing DNS-resolvable atProto handle; remove `atprotoHandle` field 287 + 288 + **Rationale:** 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 294 + 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 299 + 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) 303 + 304 + --- 279 305 280 306 ### 2025-10-10: V2 Architecture Completed 281 307 - Migrated from instance-owned to community-owned repositories
+31
docs/PRD_GOVERNANCE.md
··· 388 388 389 389 ## Technical Decisions Log 390 390 391 + ### 2025-10-11: Moderator Records Storage Location 392 + **Decision:** Store moderator records in community's repository (`at://community_did/social.coves.community.moderator/{tid}`), not user's repository 393 + 394 + **Rationale:** 395 + 1. **Federation security**: Community's PDS can write/delete records in its own repo without cross-PDS coordination 396 + 2. **Attack resistance**: Malicious self-hosted instances cannot forge or retain moderator status after revocation 397 + 3. **Single source of truth**: Community's repo is authoritative; no need to check multiple repos + revocation lists 398 + 4. **Instant revocation**: Deleting the record immediately removes moderator status across all instances 399 + 5. **Simpler implementation**: No invitation flow, no multi-step acceptance, no revocation reconciliation 400 + 401 + **Security Analysis:** 402 + - **Option B (user's repo) vulnerability**: Attacker could self-host malicious AppView that ignores revocation signals stored in community's AppView database, presenting their moderator record as "proof" of authority 403 + - **Option A (community's repo) security**: Even malicious instances must query community's PDS for authoritative moderator list; attacker cannot forge records in community's repository 404 + 405 + **Alternatives Considered:** 406 + - **User's repo**: Follows atProto pattern for relationships (like `app.bsky.graph.follow`), provides user consent model, but introduces cross-instance write complexity and security vulnerabilities 407 + - **Hybrid (both repos)**: Assignment in community's repo + acceptance in user's repo provides consent without compromising security, but significantly increases complexity 408 + 409 + **Trade-offs Accepted:** 410 + - No explicit user consent (moderators are appointed, not invited) 411 + - Users cannot easily query "what do I moderate?" without AppView index 412 + - Doesn't follow standard atProto relationship pattern (but matches service account pattern like feed generators) 413 + 414 + **Implementation Notes:** 415 + - Moderator records are source of truth for permissions 416 + - AppView indexes these records from firehose for efficient querying 417 + - User consent can be added later via optional acceptance records without changing security model 418 + - Matches Bluesky's pattern: relationships in user's repo, service configuration in service's repo 419 + 420 + --- 421 + 391 422 ### 2025-10-10: V1 Role-Based Model Selected 392 423 **Decision:** Start with simple creator/moderator two-tier system 393 424