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.
This PR implements the V2 Communities Architecture with critical fixes
for production readiness.
## V2 Architecture Highlights
**Communities now own their own repositories:**
- Each community has its own DID (did:plc:xxx)
- Each community owns its own atProto repository (at://community_did/...)
- Communities are truly portable (can migrate between instances)
- Follows atProto patterns (matches feed generators, labelers)
## Critical Fixes
1. **PDS Credential Persistence**: Fixed bug where credentials were lost
on server restart, causing community updates to fail
2. **Encryption at Rest**: Community PDS credentials encrypted using
PostgreSQL pgcrypto
3. **Handle Simplification**: Single handle field (removed duplicate
atProtoHandle), using subdomain pattern (*.communities.coves.social)
4. **Default Domain Fix**: Changed from coves.local → coves.social to
avoid .local TLD validation errors
5. **V2 Enforcement**: Removed V1 compatibility, strict rkey="self"
## Testing
- ✅ Full E2E test coverage (PDS → Jetstream → AppView)
- ✅ Integration tests for credential persistence
- ✅ Unit tests for V2 validation
- ✅ Real Jetstream firehose consumption
## Documentation
- Updated PRD_COMMUNITIES.md with V2 status
- Created PRD_BACKLOG.md for technical debt tracking
- Documented handle refactor and security considerations
## Security Notes
- Added TODO for did:web domain verification (prevents impersonation)
- Documented in PRD_BACKLOG.md as P0 priority
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added infrastructure/technical TODOs:
- OAuth authentication for community actions (P1)
- Jetstream consumer race condition (P2)
- Structured logging migration (P3)
- PDS URL resolution from DID (P3)
- PLC directory registration for prod (P3)
Feature-specific TODOs (avatars, moderator checks, update/delete handlers)
are tracked in their respective PRDs (PRD_COMMUNITIES, PRD_GOVERNANCE).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem:
- Default INSTANCE_DID was did:web:coves.local
- instanceDomain extracted as "coves.local"
- Community handles generated as "{name}.communities.coves.local"
- .local TLD is disallowed per atProto spec (RFC 6762)
- Result: Community creation failed immediately with InvalidHandleError
Solution:
- Changed default to did:web:coves.social (.social is valid TLD)
- Added TODO comment documenting did:web domain verification security issue
- Created docs/PRD_BACKLOG.md to track follow-up work
Security Note:
Self-hosters can currently set INSTANCE_DID to any domain without
verification. This enables domain impersonation attacks. Added to
backlog (P0) to implement did:web verification per atProto spec.
Testing:
- All integration tests pass (TestCommunity_E2E)
- Community handles now: gaming.communities.coves.social ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add logging for internal server errors in community XRPC handlers to
aid debugging during development. Internal errors are now logged before
returning generic error responses to clients.
Changes:
- Add log import to errors.go
- Log actual error details when returning InternalServerError
- Add TODO comment to migrate to proper structured logger
Rationale:
During E2E testing, internal errors were being silently swallowed making
debugging difficult. This change logs the actual error while still
returning safe generic error messages to clients.
Security note:
- Internal error details are NOT exposed to clients
- Logging is for server-side debugging only
- Generic "InternalServerError" message still returned to clients
TODO: Replace log.Printf with structured logger (e.g., zerolog/zap)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update development PostgreSQL port from 5433 to 5435 to avoid conflicts
with existing local PostgreSQL installations or other services.
Changes:
- .env.dev: Update POSTGRES_PORT to 5435
- docker-compose.dev.yml: Update health checks to use wget instead of curl
- cmd/server/main.go: Update default DATABASE_URL to use port 5435
Additional improvements:
- Replace curl with wget in Docker healthchecks (more reliable in Alpine)
- Update comments to reflect new port configuration
Ports summary:
- Dev PostgreSQL: 5435 (was 5433)
- Test PostgreSQL: 5434 (unchanged)
- PDS: 3001
- AppView: 8081
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>