Clone this repository
For self-hosted knots, clone URLs may differ based on your setup.
Download tar.gz
Hot ranking formula now uses (score + 1) instead of score to prevent
new posts with 0 votes from sinking to the bottom. Previously,
0 / time_decay = 0 caused all unvoted posts to have rank 0.
Affects discover, timeline, and community feed repos.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update community handles to use c-{name}.{instance} pattern and
enable additional feeds (US News, Science).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend auth middleware to accept both OAuth sealed tokens (users) and
PDS service JWTs (aggregators). Uses Indigo's ServiceAuthValidator for
JWT signature verification against DID document public keys.
Security model:
- Detect token format upfront (detect-and-route, not fallback)
- JWT auth restricted to DIDs in aggregators table
- Aggregators self-register via social.coves.aggregator.service record
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 is text-only, so skip converting Bluesky URLs to post embeds.
The social.coves.embed.post lexicon requires a valid CID in strongRef,
which we don't have without calling ResolvePost. This caused P1 bug
where empty CID violated lexicon validation.
Changes:
- Disable tryConvertBlueskyURLToPostEmbed (return false, remove dead code)
- Add TestBlueskyPostCrossPosting_E2E_LivePDS integration test that
writes posts with Bluesky URLs directly to dev PDS to catch lexicon
validation errors
- Create beads for Phase 2 embed conversion (Coves-p44) and functional
options refactoring (Coves-8k1, Coves-jdf, etc.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix quoted post mapping for recordWithMedia#view embeds
- Handle nested viewRecord structure where content is in embed.record.record
- Add mapViewRecordToResult and mapNestedViewRecordToResult functions
- Properly extract text and author from value field (not record field)
- Implement age-based cache TTL decay for scalability
- Fresh posts (< 24h): 15 min TTL (engagement changing rapidly)
- Recent posts (1-7 days): 1 hour TTL
- Old posts (7+ days): 24 hour TTL
- Unavailable posts: 15 min TTL (allow re-checking)
- Add comprehensive unit tests for TTL calculation
- Add integration tests for live Bluesky API interaction
- Update integration tests for new blueskyService parameter
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enable users to embed Bluesky posts by pasting bsky.app URLs. Posts are
resolved at response time with text, author info, and engagement stats.
## New Package: internal/core/blueskypost/
Core service following the unfurl package pattern:
- types.go: BlueskyPostResult, Author structs with ErrCircuitOpen sentinel
- interfaces.go: Service and Repository interfaces
- repository.go: PostgreSQL cache with TTL (1 hour) and AT-URI validation
- url_parser.go: bsky.app URL → AT-URI conversion with rkey validation
- fetcher.go: Bluesky public API client using SSRF-safe HTTP client
- circuit_breaker.go: Failure protection (3 failures, 5min open)
- service.go: Cache-first resolution with circuit breaker integration
## Features
- Detect bsky.app URLs in post creation, convert to social.coves.embed.post
- Resolve Bluesky posts at feed response time via TransformPostEmbeds()
- Support for quoted posts (1 level deep)
- Media indicators (hasMedia, mediaCount) without rendering (Phase 2)
- Typed error handling with retryable flag for transient failures
- Debug logging for embed processing traceability
## Integration
- Updated discover, timeline, communityFeed handlers
- Wired blueskypost service in cmd/server/main.go
- Database migration for bluesky_post_cache table
## Test Coverage: 73.1%
- url_parser_test.go: URL parsing, validation, edge cases
- circuit_breaker_test.go: State transitions, thread safety
- service_test.go: Cache hit/miss, circuit breaker integration
- fetcher_test.go: Post mapping, media detection, quotes
- repository_test.go: AT-URI validation
## Out of Scope (Phase 2)
- Rendering embedded images/videos
- Moderation labels (NSFW handling)
- Deep quote chains (>1 level nesting)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update GetCommunity() to accept DIDs, canonical handles (c-name.domain),
scoped identifiers (!name@domain), and at-identifiers (@handle)
- Fix subscribe/unsubscribe handlers to let service handle identifier resolution
(removes redundant ResolveCommunityIdentifier calls)
- Add community error handling to aggregator handlers to properly return
404/400 instead of 500 for community errors
- Add communityService dependency to listForCommunity handler for identifier
resolution
- Preserve original identifier in error messages for better debugging
- Add comprehensive unit tests for subscribe/unsubscribe handlers
- Add GetCommunity identifier resolution integration tests
- Add handle format tests for listForCommunity E2E tests
Fixes issue where endpoints only accepted DIDs and rejected valid handles
like c-worldnews.coves.social
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>