code
Clone this repository
https://tangled.org/bretton.dev/coves
git@knot.bretton.dev:bretton.dev/coves
For self-hosted knots, clone URLs may differ based on your setup.
**COMPREHENSIVE UPDATE**:
Restructured PRD_COMMUNITIES.md to focus on:
1. ✅ What's been implemented and tested
2. ⏳ What's in progress
3. 📋 What's remaining before V1 launch
**COMPLETED WORK** (documented):
- V2 Architecture (communities own PDS accounts)
- Credential management (persistence + encryption)
- Jetstream consumer (real-time firehose indexing)
- Repository layer (PostgreSQL with atomic operations)
- XRPC endpoints (create, get, update, list)
- Comprehensive test coverage
**CRITICAL FIXES** (documented):
- P0: PDS credential persistence
- P0: UpdateCommunity authentication
- V2 enforcement (removed V1 compatibility)
- Encryption at rest
- Dynamic timeouts
**ROADMAP** (documented):
- OAuth flows (in progress)
- Rate limiting and visibility enforcement
- Posts in communities
- Moderation tools
- Federation improvements
**CHANGES**:
- Removed code examples (kept PRD focused on status)
- Added "Recent Critical Fixes" section
- Organized by implementation status
- Clear V1 launch checklist
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**NEW TESTS**:
1. **community_credentials_test.go** - Integration tests:
- TestCommunityRepository_CredentialPersistence
* Verify PDS credentials saved to database
* Verify credentials retrievable after creation
- TestCommunityRepository_EncryptedCredentials
* Verify credentials encrypted in database (not plaintext)
* Verify decryption works correctly on retrieval
- TestCommunityRepository_V2OwnershipModel
* Verify owner_did == did (self-owned)
2. **community_v2_validation_test.go** - Integration tests:
- TestCommunityConsumer_V2RKeyValidation
* Accept rkey="self" (V2 communities)
* Reject rkey with TID pattern (V1 communities)
* Reject custom rkeys
- TestCommunityConsumer_AtprotoHandleField
* Verify handle field handling in consumer
3. **community_service_test.go** - Unit tests:
- TestCommunityService_PDSTimeouts
* Verify write ops use 30s timeout
* Verify read ops use 10s timeout
- TestCommunityService_UpdateWithCredentials
* Verify UpdateCommunity fetches credentials from DB
* Verify error if credentials missing
- TestCommunityService_CredentialPersistence
* Verify repo.Create() called with credentials
4. **community_e2e_test.go** - Enhanced E2E tests:
- Fixed .local TLD issue (changed to .social)
- Fixed handle length issue (use shorter test names)
- Complete flow: Service → PDS → Jetstream → Consumer → DB → XRPC
**TEST COVERAGE**:
- ✅ P0 credential persistence bug
- ✅ P0 UpdateCommunity authentication bug
- ✅ Encryption at rest
- ✅ V2 rkey validation
- ✅ Dynamic timeout logic
- ✅ End-to-end write-forward flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**SECURITY ENHANCEMENT**: Encrypt community PDS access/refresh tokens
in PostgreSQL using pgcrypto extension.
**IMPLEMENTATION**:
1. **Migration 006**:
- Enable pgcrypto extension
- Create encryption_keys table with single 256-bit key
- Add encrypted BYTEA columns: pds_access_token_encrypted, pds_refresh_token_encrypted
- Generate random encryption key on first run (idempotent)
- Add index for communities with credentials
2. **Repository Layer**:
- Encrypt on INSERT using pgp_sym_encrypt()
- Decrypt on SELECT using pgp_sym_decrypt()
- Inline encryption/decryption (no application-layer crypto)
- Empty strings stored as NULL (skip encryption)
**KEY MANAGEMENT**:
- Single symmetric key stored in encryption_keys table
- Key persists across restarts via PostgreSQL storage
- Future: Support key rotation via rotated_at timestamp
**TRADE-OFFS**:
- Performance: Inline crypto adds ~1-2ms per query
- Security: Keys stored in same DB (acceptable for self-hosted)
- Simplicity: No external KMS required for initial version
**FUTURE ENHANCEMENTS**:
- External KMS integration (AWS KMS, Vault)
- Key rotation support
- Per-community keys (if needed)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**CONTEXT**: Pre-production system should not support V1 communities.
All communities must use V2 architecture from day one.
**CHANGES**:
1. **Jetstream Consumer** - Strict V2 validation:
- REJECT any community profile with rkey != "self"
- V1 used TID-based rkeys (e.g., "3km4..."), V2 uses "self"
- Removed V1 owner field handling
- Added clear error messages for V1 detection
2. **Lexicon Schema** - Removed V1 fields:
- Removed "owner" field (V1: owner != community DID)
- V2 principle: community IS the owner (self-owned)
3. **Domain Model** - Simplified ownership:
- Removed OwnerDID field from Community struct
- V2: owner_did always equals did (enforced at creation)
**V2 ARCHITECTURE PRINCIPLES**:
- Community owns its own PDS account (did)
- Community owns its own repository (at://did/...)
- Profile always at rkey="self" (not TID-based)
- Self-owned: owner_did == did (no separate owner)
**IMPACT**:
- Cleaner codebase without V1/V2 branching logic
- Prevents accidental V1 community creation
- Enforces architectural constraints at every layer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**PROBLEM**: PDS credentials were never saved to database, making it impossible
to update community profiles later or re-authenticate if tokens expire.
**ROOT CAUSE**: After provisioning PDS account and creating profile record,
credentials were only stored in memory (returned Community struct) but never
persisted via repository.Create().
**FIX**: Call repo.Create() immediately after PDS provisioning to persist:
- pds_access_token
- pds_refresh_token
- pds_url
- did (from PDS createAccount response)
**IMPACT**:
- Communities can now be updated using their own credentials
- Token refresh will work when access tokens expire
- Critical for V2 write-forward architecture (community updates own profile)
**ARCHITECTURE**:
This fix enables the proper V2 flow:
1. Create community → Store credentials in DB
2. Update community → Fetch credentials from DB → Authenticate as community → Write-forward to PDS
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed syntax error in 005 migration where pds_url column was missing
a trailing comma, causing migration failures.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>