Clone this repository
For self-hosted knots, clone URLs may differ based on your setup.
Download tar.gz
Add containerized deployment configuration:
- Dockerfile: Python 3.11-slim with cron support
- docker-compose.yml: Service definition with volume mounts
- docker-entrypoint.sh: Cron setup and environment handling
- crontab: 10-minute polling schedule
- .env.example: Environment variable template
Deployment uses the same pattern as the Kagi News aggregator.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add 137 tests covering all aggregator components:
- test_config.py: Config loading, validation, URL schemes, env overrides
- test_coves_client.py: API client, authentication, error handling
- test_link_extractor.py: URL detection, normalization, thumbnail fetching
- test_models.py: Data models, validation, immutability
- test_rss_fetcher.py: Feed fetching, retries, rate limit handling
- test_state_manager.py: State persistence, atomic writes, cleanup
All tests use mocking to avoid external dependencies.
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a new aggregator that pulls posts with streamable.com video links
from Reddit RSS feeds and posts them to Coves communities.
Core components:
- RSS feed fetcher with retry logic and exponential backoff
- Link extractor for detecting streamable.com URLs
- Coves API client with proper error handling
- State manager with atomic writes and corruption recovery
- Configuration loader with validation
Features:
- Configurable subreddit-to-community mappings
- Deduplication via persistent state
- Video embed metadata (embedType, provider, domain)
- Anti-detection jitter for Reddit rate limits
馃 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>