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.
Use errors.As instead of direct type assertion for proper wrapped error
handling, and handle additional close error codes (CloseGoingAway,
CloseAbnormalClosure) and EOF conditions to prevent gorilla/websocket
from panicking on subsequent reads.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change OAuth client_id from /oauth/client-metadata.json to
/oauth-client-metadata.json to match atproto's "conventional" pattern.
This causes the PDS OAuth authorization screen to display just
"coves.social" instead of the full URL path, providing a cleaner
user experience.
Per atproto spec, a "conventional" client_id must be:
- https:// protocol
- pathname exactly /oauth-client-metadata.json
- no port or query string
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The listCommunities endpoint was missing OptionalAuth middleware, which
meant GetUserDID() always returned empty string even for authenticated
requests. This caused viewer.subscribed to never be populated.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes the issue where authenticated users couldn't see their subscription
status in the community list response, causing "My Communities" tab to show
no results and all community tiles to show "Join" instead of "Joined".
Changes:
- Add CommunityViewerState struct with tri-state *bool semantics
- Add GetSubscribedCommunityDIDs batch query to repository
- Add PopulateCommunityViewerState helper for viewer enrichment
- Update ListHandler to inject repo and populate viewer state
- Update route registration to pass repository through
The endpoint remains public but now enriches responses with viewer.subscribed
when the request is authenticated.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add block_test.go with comprehensive unit tests for BlockHandler
- Add TestUnsubscribeHandler_RequiresOAuthSession test
- Add debugging context to ResumeSession errors (DID, sessionID)
- Add PDS error type detection in handleServiceError (ErrBadRequest,
ErrNotFound, ErrConflict, ErrUnauthorized, ErrForbidden)
- Add OAuth misconfiguration logging in getPDSClient
- Remove unused deleteRecordOnPDSAs method
- Remove unused oauthStore field from communityService
- Wrap identifier resolution errors with operation context
(subscribe:, unsubscribe:, block:, unblock:)
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace plain Bearer token authentication with proper DPoP-authenticated
OAuth sessions for subscribe, unsubscribe, block, and unblock operations.
The issue was that atProto OAuth tokens are DPoP-bound and require both
an access token AND a DPoP proof header. The community service was using
plain Bearer authentication which caused "Malformed token" errors.
Changes:
- Update Service interface to use *oauth.ClientSessionData
- Add PDSClientFactory pattern for testability
- Update handlers to get OAuth session from middleware
- Update unit tests to inject OAuth session into context
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add user comment history endpoint for profile pages with:
- Lexicon definition following AT Protocol conventions
- Handler with proper error handling (400/404/500 distinction)
- Cursor-based pagination with composite key (createdAt|uri)
- Optional community filtering
- Viewer vote state population for authenticated users
- Comprehensive handler tests (15 tests)
- Comprehensive service tests (13 tests)
Key fixes from PR review:
- resolutionFailedError now returns 500 (not 400)
- Added warning log for missing votes table
- Improved SQL parameter documentation for cursor filters
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add aggregated user statistics to the social.coves.actor.getProfile response:
- postCount, commentCount, communityCount, membershipCount, reputation
- Efficient single-query with scalar subqueries
- Flat response structure matching lexicon specification
PR review fixes:
- Log database errors in ResolveHandleToDID before fallback (silent-failure-hunter)
- Marshal JSON to bytes before writing to prevent partial responses
- Wrap GetByDID errors with context for debugging
- Document intentional reputation counting for banned users
Tests: comprehensive coverage for all stat types including soft-delete
filtering, subscription counting, and non-existent DID handling.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Jetstream identity event consumer was intentionally changed to only
UPDATE existing users, not create new ones. This prevents indexing
millions of Bluesky users who never interact with Coves. Users are now
indexed during:
- OAuth login
- Signup (via RegisterAccount.IndexUser())
Test fixes:
- integration/jetstream_consumer_test.go: Pre-create users before
testing identity event handling; renamed tests to reflect behavior
- integration/community_e2e_test.go: Add test data cleanup to prevent
cross-test pollution affecting alphabetical sort test
- integration/user_test.go: Add comprehensive test data cleanup
(subscriptions, posts, communities) in setupTestDB
- e2e/error_recovery_test.go: Pre-create users in all identity event
tests (reconnection, malformed events, PDS unavailability, etc.)
- e2e/user_signup_test.go: Query AppView API instead of test database
to verify user creation; removed unused Jetstream consumer setup
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add test-all target that runs all tests with live infrastructure
- Check dev stack, AppView, and test database before running tests
- Add LOG_ENABLED=false support to silence app logs during tests
- Add TestMain to integration, e2e, unit, and tests packages
- Fail fast on first test failure for quick feedback
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete implementation for retrieving a user's posts by DID or handle:
- New XRPC endpoint: GET /xrpc/social.coves.actor.getPosts
- Handle resolution with local DB fallback (faster for indexed users)
- Filters: posts_with_replies, posts_no_replies, posts_with_media
- Community-scoped filtering
- Cursor-based pagination with proper duplicate prevention
- Viewer vote state population (when authenticated)
- Bluesky post embed resolution
Key improvements:
- ResolveHandleToDID now checks local DB first before external DNS/HTTPS
- Proper error distinction: actorNotFoundError vs resolutionFailedError
- Infrastructure failures (DB down, DNS errors) return 500, not 404
Includes comprehensive E2E tests against live PDS infrastructure.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add UserIndexer interface and IndexUser method to index users after
successful OAuth authentication
- Change Jetstream consumer to only UPDATE existing users, not create new
ones (prevents indexing millions of unrelated Bluesky users)
- Add sentinel errors ErrUserNotFound and ErrHandleAlreadyTaken for
proper error handling instead of string matching
- Fix silent failure: Jetstream now propagates database errors instead
of silently dropping events during outages
- Remove redundant DID lookup in OAuth callback by reusing verifiedIdent
- Remove bsky.social fallback - not all users are on Bluesky
- Add compile-time interface satisfaction check for UserIndexer
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>