Clone this repository
For self-hosted knots, clone URLs may differ based on your setup.
Download tar.gz
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>
The test was using AddUser() which creates a mock token, but subscription
and other write operations need the real PDS access token. Using
AddUserWithPDSToken() stores the actual PDS token so write-forward works.
All tests now pass without --short flag.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix concurrent modification test to accept either ErrConflict (409)
or CID mismatch error (400 "Record was at")
- Shorten e2epost community name to stay under handle length limit
Remaining: TestFullUserJourney_E2E fails due to PDS token validation
issue ("InvalidToken") - this is a test infrastructure issue where the
community's stored access token is no longer valid with the PDS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes:
- Exclude deleted top-level comments from ListByParentWithHotRank query
(nested deleted comments still preserved via ListByParentsBatch)
- Fix OAuth E2E tests: unwrap MobileAwareStoreWrapper for cleanup methods
- Fix hostedby security tests: conditionally skip DID verification
- Fix concurrent_scenarios_test: use correct column name (commenter_did)
- Fix user_journey_e2e_test: use correct column name (commenter_did)
- Fix handle length issues: use shorter prefixes with 6-digit timestamps
to stay under ATProto's 32-character handle limit
Pre-commit hook:
- Add go vet check that rejects commits with static analysis issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix TestHandleClientMetadata to expect full metadata URL per atproto OAuth spec
- Fix TestVoteRepo_Delete to match GetByURI behavior (excludes soft-deleted votes)
- Fix TestPostgresOAuthStore_CleanupExpiredSessions test isolation
- Fix lexicon IDs to use lowerCamelCase (getProfile, updateProfile) per atproto spec
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change community handle format to simplify DNS/Caddy configuration:
- Old: gaming.community.coves.social
- New: c-gaming.coves.social
This works with single-level wildcard certificates (*.coves.social)
while keeping the same user-facing display format (!gaming@coves.social).
Changes:
- Add migration 022 to update existing handles in database
- Update handle generation in pds_provisioning.go
- Update GetDisplayHandle() parsing in community.go
- Update scoped identifier resolution in service.go
- Update PDS_SERVICE_HANDLE_DOMAINS in docker-compose.dev.yml
Also addresses PR review feedback:
- Fix LRU cache error handling (panic on critical failure)
- Add logging for JSON marshal failures in facets
- Add DEBUG logging for domain extraction fallbacks
- Add DEBUG logging for GetDisplayHandle parse failures
- Add WARNING log for non-did:web hostedBy
- Add edge case tests for malformed handle inputs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add true E2E integration tests that verify the full write-forward flow
through real PDS and Jetstream infrastructure:
- Comment E2E tests: create, update, delete with real Jetstream indexing
- Comment authorization tests: verify users cannot modify others' comments
- Comment validation tests: verify proper error handling
- Community update E2E tests: single and multiple updates via Jetstream
These tests require dev infrastructure (make dev-up) and are skipped
in short mode. Fixed race condition where Jetstream subscription started
after create event was emitted.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add optional sources array to create_external_embed() for including
article source citations in post embeds. Sources are passed through
from parsed Kagi news stories to the embed structure.
- Add sources parameter to CovesClient.create_external_embed()
- Pass story sources to embed in Aggregator.run()
- Add comprehensive unit tests for create_external_embed()
- Add integration tests for posting with/without sources
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a user changes their handle on their PDS, all active OAuth sessions
are now updated to reflect the new handle. This ensures mobile/web apps
display the correct handle without requiring re-authentication.
Implementation:
- Add UpdateHandleByDID method to PostgresOAuthStore
- Add SessionHandleUpdater interface for dependency injection
- Use functional options pattern for consumer configuration
- Pass verified handle through mobile callback flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --remove-orphans flag and network cleanup to prevent stale
containers from accumulating during development.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>