commits
- Add github.com/google/uuid for DPoP proof jti generation
- Add .cache/ to .gitignore for Go build cache
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document DPoP token binding implementation:
- Explain DPoP security model and why it's not a fallback
- Add flow diagrams for DPoP verification process
- Document replay protection with NonceCache
- Add code examples for DPoP verification
- List implemented security features and future enhancements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
JWT improvements:
- Add Confirmation field for DPoP cnf.jkt claim binding
- Reorder Claims struct fields for optimal memory alignment
Test improvements:
- Replace os.Setenv/os.Unsetenv with t.Setenv for cleaner tests
- Use t.Cleanup for automatic environment restoration
- Use UUID for DPoP proof jti to ensure test uniqueness
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance AtProtoAuthMiddleware with DPoP token binding support:
- Add Stop() method to prevent goroutine leaks on shutdown
- Require DPoP proof when token has cnf.jkt claim
- Treat DPoP-bound tokens without proof as unauthenticated in OptionalAuth
- Honor X-Forwarded-Proto header for URI verification behind proxies
Security model:
- DPoP is ADDITIONAL security, never a fallback for failed verification
- Token signature must be verified BEFORE checking DPoP binding
- Missing DPoP proof for bound tokens results in rejection
Tests added for:
- Middleware Stop() cleanup
- OptionalAuth with DPoP-bound tokens
- X-Forwarded-Proto handling
- DPoP replay protection integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add proof-of-possession verification for OAuth access tokens:
- DPoPVerifier for validating DPoP proof JWTs
- NonceCache for replay attack prevention with background cleanup
- JWK thumbprint calculation per RFC 7638
- Support for ES256 signing algorithm
- Configurable clock skew and proof age limits
Security features:
- Validates htm (HTTP method) and htu (HTTP URI) claims
- Enforces iat freshness within 5-minute window
- Tracks jti values to prevent proof reuse
- Calculates and validates JWK thumbprints for token binding
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add ES256 federation support and JWT config caching:
- DID-based key fetcher for verifying tokens from any PDS
- O(1) issuer whitelist lookups with cached config
- Environment configuration documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Document the dual JWT verification methods (HS256 + ES256) in environment
configuration files:
- HS256: For your own PDS (fast, shared secret, no network calls)
- ES256: For federated users (DID resolution, works with any PDS)
Updates:
- .env.dev: Add HS256_ISSUERS for local development
- .env.prod.example: Add JWT Authentication section with documentation
- docker-compose.prod.yml: Pass PDS_JWT_SECRET and HS256_ISSUERS to appview
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cache HS256_ISSUERS, PDS_JWT_SECRET, and IS_DEV_ENV at startup instead
of reading environment variables on every token verification request.
- Add jwtConfig struct with sync.Once initialization
- Use map[string]struct{} for O(1) issuer whitelist lookups
- Add InitJWTConfig() for explicit startup initialization
- Add ResetJWTConfigForTesting() for test isolation
- Update main.go to call InitJWTConfig() at startup
Before: 2-3 os.Getenv() calls + O(n) string iteration per request
After: Single pointer dereference + O(1) map lookup per request
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add support for verifying ES256 service auth tokens from federated users.
This enables users from any PDS (bsky.social, etc.) to authenticate with
Coves instances.
- DIDKeyFetcher: resolves DID documents via PLC directory to get public keys
- CombinedKeyFetcher: routes to DID or JWKS based on issuer format
- Supports did:plc: and did:web: issuers
- Converts atcrypto JWK to Go ecdsa.PublicKey for jwt-go verification
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Only did:web:coves.social can now create communities in production.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace local disk blobstore with S3-compatible storage configuration.
This allows blobs to be stored in OVH Object Storage while keeping
record data (CAR files, SQLite) on local NVMe.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds optional sources field to social.coves.embed.external lexicon
to support aggregator megathreads that combine multiple news sources.
Changes:
- Add #source definition with uri, title, domain, and optional sourcePost
- Add sources array (max 50) to #external for aggregated links
- Add maxLength constraints to domain (253) and provider (100) fields
- Update descriptions to clarify primary vs aggregated content
This enables LLM aggregators to create megathread posts that reference
multiple source articles, with optional strongRef to existing Coves
posts for future feed deprioritization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed camelCase NSIDs to lowercase to comply with atProto Lexicon
specification which requires NSIDs to use only lowercase letters:
- social.coves.actor.getProfile → social.coves.actor.getprofile
- social.coves.actor.updateProfile → social.coves.actor.updateprofile
Updated all code references including routes, tests, and documentation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Apply gofumpt formatting with extra-rules across all packages
- Fix mock interface signatures to match updated Service/Repository interfaces
- Fix ineffassign bugs in community_repo.go (sortColumn/sortOrder)
- Fix unchecked error returns in production code (register.go)
- Fix unchecked error returns in test files (defer closures)
- Optimize struct field alignment per govet fieldalignment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Standardize import ordering and formatting using gofumpt.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add configurable allowlist to restrict who can create communities during alpha.
Self-hosters can set their own DID in the env var.
- Add allowedCommunityCreators field to CreateHandler
- Load comma-separated DIDs from COMMUNITY_CREATORS env var
- Return 403 CommunityCreationRestricted for non-allowed users
- Empty/unset env var allows all authenticated users
- Filter empty strings from allowlist defensively
- Add comprehensive unit tests for allowlist behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update Go to 1.24 in Dockerfile
- Fix migrations path (internal/db/migrations)
- Add /xrpc/_health endpoint for Docker healthcheck
- Fix PORT env var precedence (PORT > APPVIEW_PORT)
- Add custom lexicon Jetstream URLs
- Add CURSOR_SECRET env var
- Comment out partial email config (PDS requires both or neither)
- Update Go to 1.24 in Dockerfile
- Fix migrations path (internal/db/migrations)
- Add custom lexicon Jetstream URLs
- Add CURSOR_SECRET env var
- Comment out partial email config (PDS requires both or neither)
- Docker configuration (Dockerfile, docker-compose.prod.yml)
- Caddy reverse proxy with HSTS, CSP, wildcard SSL
- Deployment scripts (deploy.sh, setup-production.sh, backup.sh)
- DID key generation script
- OAuth callback with XSS protection
- Environment template (.env.prod.example)
Align social.coves.community.list endpoint to lexicon specification
with comprehensive testing and atProto compliance.
**Summary:**
- ✅ Lexicon-compliant parameter handling
- ✅ atProto-standard pagination (cursor-based)
- ✅ Input validation for all parameters
- ✅ Performance optimization (removed COUNT query)
- ✅ Comprehensive test coverage (8 new test cases)
- ✅ All tests passing
**Changes:**
- Add visibility parameter to lexicon
- Implement sort enum (popular/active/new/alphabetical)
- Fix cursor type (string vs int)
- Remove undocumented "total" field
- Add input validation for visibility and sort
- Update test suite with comprehensive coverage
Ready for alpha deployment 🚀
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive test coverage for social.coves.community.list
endpoint with all parameter combinations.
**New Test Cases:**
- List with sort=popular (default)
- List with sort=active
- List with sort=new
- List with sort=alphabetical (validates actual ordering)
- List with invalid sort value (expects 400)
- List with visibility filter
- List with default sort (no parameter)
- List with limit bounds validation
**Test Cleanup:**
- Remove deprecated "total" field from response structs
- Add "cursor" field to all list response structs
- Update repository tests for new List() signature
All tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Align social.coves.community.list handler to lexicon specification
following atProto standards.
**Changes:**
- Add visibility parameter (public/unlisted/private) to lexicon
- Implement sort enum mapping (popular→subscriber_count,
active→post_count, new→created_at, alphabetical→name)
- Add input validation for sort and visibility parameters
- Enforce limit bounds (1-100, default 50)
- Update ListCommunitiesRequest struct with new parameters
- Remove deprecated hostedBy parameter
**atProto Compliance:**
- Use string cursor type (not int)
- Remove undocumented "total" field (follows Bluesky patterns)
- Eliminate COUNT query for better performance
- Return empty cursor when pagination complete
**Performance:**
- Single query instead of COUNT + SELECT
- Proper cursor-based pagination
**Code Quality:**
- Fix magic number in GetDisplayHandle (11 → len(".community."))
- Add TODO comments for future category/language filters
Addresses lexicon contract violations and follows atProto design
patterns from bluesky-social/atproto#4245.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add bd (beads) issue tracking section to project instructions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive documentation for AI agents on using bd (beads)
for issue tracking, including workflow, priorities, and best practices
for managing planning documents in history/ directory.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create starter issues for tracking Coves development:
- Complete post creation and moderation features [P1]
- Implement aggregator feed federation [P1]
- Add comprehensive API documentation [P2, blocked by features]
Issues tracked in .beads/issues.jsonl with dependency graph.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Set up Beads dependency-aware issue tracker to provide persistent
memory and task tracking across agent sessions. Issues will use
prefix 'Coves-1, Coves-2, etc.' and sync via JSONL in git.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update project guidelines and best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add complete Docker configuration for containerized deployment.
Files added:
- Dockerfile: Multi-stage Python 3.11 image with cron scheduler
- docker-compose.yml: Simple deployment configuration
- docker-entrypoint.sh: Startup script with validation
- .dockerignore: Build optimization
Features:
- Automated cron scheduling (daily at 1 PM UTC)
- Health checks (verifies cron is running)
- Log rotation (10MB max, 3 files)
- Auto-restart on failure
- Environment-based configuration
- Single command deployment: docker compose up -d
The container runs cron internally and streams logs to stdout,
making it production-ready and easy to monitor.
Updated README with comprehensive Docker deployment documentation
including quick start, configuration, testing, and production
deployment best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add Kagi-specific automated registration script and update README.
Changes:
- Move setup-kagi-aggregator.sh to kagi-news/scripts/setup.sh
- Add comprehensive Registration section to README
- Document automated vs manual setup options
- Explain registration workflow and requirements
- Update project structure to reflect new scripts
The setup script automates all 4 registration steps:
1. PDS account creation
2. .well-known file generation
3. Coves registration via XRPC
4. Service declaration creation
This makes the Kagi aggregator self-contained and ready to be
split into its own repository.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive setup scripts and documentation for aggregator registration.
Scripts:
- 1-create-pds-account.sh: Create PDS account for aggregator
- 2-setup-wellknown.sh: Generate .well-known/atproto-did file
- 3-register-with-coves.sh: Register with Coves instance via XRPC
- 4-create-service-declaration.sh: Create service declaration record
Documentation:
- Detailed README with step-by-step instructions
- Troubleshooting guide
- Configuration examples (nginx/Apache)
- Security best practices
These scripts automate the 4-step process of:
1. Creating a DID for the aggregator
2. Proving domain ownership
3. Registering with Coves
4. Publishing service metadata
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement social.coves.aggregator.register endpoint for aggregator registration.
Features:
- Lexicon schema for registration request/response
- Domain verification via .well-known/atproto-did
- DID resolution and validation
- User table insertion for aggregators
- Comprehensive integration tests
The endpoint allows aggregators to register with a Coves instance by:
1. Providing their DID and domain
2. Verifying domain ownership via .well-known file
3. Getting indexed into the users table
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements mandatory bidirectional did:web verification matching Bluesky's
security model. This prevents domain impersonation attacks by requiring
DID documents to claim the handle domain in their alsoKnownAs field.
Security Improvements:
- MANDATORY bidirectional verification (hard-fail, not soft-fail)
- Verifies domain matching (handle domain == hostedBy domain)
- Fetches DID document from https://domain/.well-known/did.json
- Verifies DID document ID matches claimed DID
- NEW: Verifies DID document claims handle in alsoKnownAs field
- Rejects communities that fail verification (was: log warning only)
- Cache TTL increased from 1h to 24h (matches Bluesky recommendations)
Implementation:
- Location: internal/atproto/jetstream/community_consumer.go
- Verification runs in AppView Jetstream consumer (not creation API)
- Impact: Controls AppView indexing and federation trust
- Performance: Bounded LRU cache (1000 entries), rate limiting (10 req/s)
Attack Prevention:
✓ Domain impersonation (can't claim did:web:nintendo.com without owning it)
✓ DNS hijacking (bidirectional check fails even with DNS control)
✓ Reputation hijacking (can't point your domain to someone else's DID)
✓ AppView pollution (only legitimate communities indexed)
✓ Federation trust (other instances can verify instance identity)
Tests:
- Updated existing tests to handle mandatory verification
- Added comprehensive bidirectional verification tests with mock HTTP server
- All tests passing ✅
Documentation:
- PRD_BACKLOG.md: Marked did:web verification as COMPLETE
- PRD_ALPHA_GO_LIVE.md: Added production deployment requirements
- Clarified architecture: AppView (coves.social) + PDS (coves.me)
- Added PDS deployment checklist (separate domain required)
- Updated production environment checklist
- Added Jetstream configuration (Bluesky production firehose)
Production Requirements:
- Deploy .well-known/did.json to coves.social with alsoKnownAs field
- Set SKIP_DID_WEB_VERIFICATION=false (production)
- PDS must be on separate domain (coves.me, not coves.social)
- Jetstream connects to wss://jetstream2.us-east.bsky.network/subscribe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive PRD for implementing Lemmy-style federation in Beta:
**Overview:**
- Enable users on any Coves instance to post to communities on other instances
- Maintain community ownership (posts live in community repos)
- Use atProto-native service authentication pattern
**Key Features:**
- Cross-instance posting: user@instance-a posts to !community@instance-b
- atProto service auth via com.atproto.server.getServiceAuth
- Community moderation control maintained
- Allowlist-based federation (manual for Beta)
**Technical Approach:**
- Service-to-service JWT authentication
- Community credentials delegation to user's instance
- Proper record ownership in community repos
- Security: signature verification, rate limiting, moderation
**Deferred to Future:**
- Automatic instance discovery (Beta uses manual allowlist)
- Cross-instance moderation delegation
- Content mirroring/replication
- User migration between instances
**Target:** Beta Release
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated PRD to show all 6 E2E test suites are complete:
**Status Change:**
- From: "Pre-Alpha"
- To: "Pre-Alpha → **E2E Testing Complete** 🎉"
**Updates:**
- Added major progress update section at top
- Marked all E2E test sections (lines 211-312) as ✅ COMPLETE
- Added actual implementation times and test file locations
- Updated timeline estimate: 65-80 hours → 50-65 hours remaining
- Updated success criteria to show E2E test milestones achieved
- Updated next steps to show E2E tests moved to completed status
- Added celebration message for major milestone
**Test Suite Completion:**
✅ Full User Journey (40 min)
✅ Blob Upload (35 min)
✅ Multi-Community Timeline (30 min)
✅ Concurrent Scenarios (45 min - with race detection)
✅ Rate Limiting (25 min)
✅ Error Recovery (30 min)
Total: ~3 hours of E2E test implementation
**Next Phase:** P0 blockers (JWT verification, DPoP fix, did:web verification)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented all 6 critical E2E test suites required for Alpha launch:
1. **User Journey E2E Test** (user_journey_e2e_test.go)
- Tests complete user flow: signup → create community → post → comment → vote
- Uses real PDS accounts and Jetstream WebSocket subscription
- Validates full atProto write-forward architecture
- Fixed silent fallback: now fails by default if Jetstream times out
- Use ALLOW_SIMULATION_FALLBACK=true env var to enable fallback in CI
2. **Blob Upload E2E Test** (blob_upload_e2e_test.go)
- Tests image upload to PDS via com.atproto.repo.uploadBlob
- Fixed to use REAL PDS credentials instead of fake tokens
- Tests PNG, JPEG, and WebP (MIME only) format validation
- Validates blob references in post records
- Tests multiple images and external embed thumbnails
3. **Concurrent Scenarios Test** (concurrent_scenarios_test.go)
- Tests race conditions with 20-30 simultaneous users
- Added database record verification to detect duplicates/lost records
- Uses COUNT(*) and COUNT(DISTINCT) queries to catch race conditions
- Tests concurrent: voting, mixed voting, commenting, subscriptions
4. **Multi-Community Timeline Test** (timeline_test.go)
- Tests feed aggregation across multiple communities
- Validates sorting (hot, new, top) and pagination
- Tests cross-community post interleaving
5. **Rate Limiting E2E Test** (ratelimit_e2e_test.go)
- Tests 100 req/min general limit
- Tests 20 req/min comment endpoint limit
- Tests 10 posts/hour aggregator limit
- Removed fake summary test, converted to documentation
6. **Error Recovery Test** (error_recovery_test.go)
- Tests Jetstream connection retry logic
- Tests PDS unavailability handling
- Tests malformed event handling
- Renamed reconnection test to be honest about scope
- Improved SQL cleanup patterns to be more specific
**Architecture Validated:**
- atProto write-forward: writes to PDS, AppView indexes from Jetstream
- Real Docker infrastructure: PDS (port 3001), Jetstream (6008), PostgreSQL (5434)
- Graceful degradation: tests skip if infrastructure unavailable (CI-friendly)
**Security Tested:**
- Input validation at handler level
- Parameterized queries (no SQL injection)
- Authorization checks before operations
- Rate limiting enforcement
**Time Saved:** ~7-12 hours through parallel sub-agent implementation
**Test Quality:** Enhanced with database verification to catch race conditions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive Alpha launch readiness documentation and production
JWT verification testing infrastructure.
**Alpha Go-Live PRD** (docs/PRD_ALPHA_GO_LIVE.md):
- Track remaining work for alpha launch with real users
- P0 blockers: DPoP architecture fix, JWT verification (2 items)
- ✅ Validated handle resolution already implemented
- ✅ Validated comment_count reconciliation already implemented
- P1 infrastructure: Monitoring, logging, backups, load testing
- E2E testing recommendations (7 critical gaps identified)
- Timeline: 65-80 hours (~2-3 weeks)
- Go/no-go decision criteria and risk assessment
**JWT Verification Test** (tests/integration/jwt_verification_test.go):
- E2E test for JWT signature verification with real PDS
- Tests JWT parsing, JWKS fetching, and auth middleware
- Validates AUTH_SKIP_VERIFY=false production mode
- Handles both dev PDS (symmetric) and production PDS (JWKS)
- Tests tampered token rejection
Changes support alpha launch planning and production security validation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes two backlog issues:
1. Post comment_count reconciliation - Added tests proving it works
2. Handle resolution for block/unblock endpoints - Full implementation
Changes include:
- 15 new integration tests (all passing)
- Handle resolution with proper error handling (400/404/500)
- Updated documentation in PRD_BACKLOG.md
- Code formatting compliance with gofumpt
Auto-formatting comment blocks to comply with gofumpt standards.
No functional changes.
Mark the following backlog items as complete:
1. Post comment_count reconciliation (2025-11-16)
- Added comprehensive test coverage (4 test cases)
- Updated misleading FIXME comment to accurate documentation
- Verified existing reconciliation logic works correctly
2. Handle resolution for block endpoints (2025-11-16)
- Phase 3: Block/unblock endpoints now accept handles
- Added 11 test cases covering all identifier formats
- Proper error handling (400 for validation, 404 for not found)
- Phase 1 (post creation) was already complete
Both issues estimated at 2-3 hours each, completed with full test
coverage and documentation updates.
Update block and unblock handlers to accept at-identifiers (handles)
in addition to DIDs, resolving them via ResolveCommunityIdentifier().
Changes:
- Remove DID-only validation in HandleBlock and HandleUnblock
- Add ResolveCommunityIdentifier() call with proper error handling
- Map validation errors (malformed identifiers) to 400 Bad Request
- Map not-found errors to 404
- Map other errors to 500 Internal Server Error
Supported formats:
- DIDs: did:plc:xxx, did:web:xxx
- Canonical handles: gaming.community.coves.social
- @-prefixed handles: @gaming.community.coves.social
- Scoped format: !gaming@coves.social
Test coverage (11 test cases):
- Block with canonical handle
- Block with @-prefixed handle
- Block with scoped format
- Block with DID (backwards compatibility)
- Block with malformed identifiers (4 cases - returns 400)
- Block with invalid/nonexistent handle (returns 404)
- Unblock with handle
- Unblock with invalid handle
Addresses PR feedback: Validation errors now return 400 instead of 500
Fixes issue: Handle Resolution Missing
Affected: Post creation, blocking endpoints
Add comprehensive integration tests verifying that post comment_count
is correctly reconciled when comments arrive before their parent post
due to out-of-order Jetstream event delivery.
Test coverage:
- Single comment arrives before post - count reconciled to 1
- Multiple comments arrive before post - count reconciled correctly
- Mixed ordering (comments before and after post) - count accurate
- Idempotent post indexing preserves comment_count
Also update misleading FIXME comment in comment_consumer.go to accurate
NOTE explaining that reconciliation IS implemented in post_consumer.go
at lines 210-226.
Fixes issue: Post comment_count Never Reconciles
File: internal/atproto/jetstream/comment_consumer.go:362
Complete migration of comment namespace from social.coves.feed.comment
to social.coves.community.comment.
This migration aligns the comment system with the community-focused
architecture and ensures all comment records use the correct namespace.
Summary:
- ✅ Lexicon migrated to community/comment.json
- ✅ Database migration 018 applied successfully
- ✅ All backend code updated (consumer, service, validation)
- ✅ Server configuration updated for Jetstream
- ✅ All 22 integration tests passing
- ✅ Test scripts and data updated
- ✅ Documentation updated
Migration verified with:
- Unit tests: ✅ PASSING
- Integration tests: ✅ 22/22 PASSING
- Build: ✅ SUCCESS
- Database: ✅ Migration 018 applied
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update COMMENT_SYSTEM_IMPLEMENTATION.md to reflect the completed
migration from social.coves.feed.comment to
social.coves.community.comment.
Changes:
- Mark Phase 4 (Namespace Migration) as COMPLETED (2025-11-16)
- Update all namespace references throughout the document
- Add migration completion details and scope
Documentation now accurately reflects the current implementation
using the community.comment namespace for all comment records.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update all test comment generation scripts to use
social.coves.community.comment namespace in URI construction.
Scripts updated:
- generate_deep_thread.go: Creates nested comment threads
- generate_nba_comments.go: Generates NBA discussion comments
- generate_test_comments.go: Creates general test comments
All scripts now generate comment URIs with the correct namespace
pattern: at://did/social.coves.community.comment/rkey
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update all comment integration tests to use the new
social.coves.community.comment namespace.
Files updated:
- comment_consumer_test.go: 18 tests covering create/update/delete
- comment_query_test.go: 11 tests covering queries and pagination
- comment_vote_test.go: 6 tests covering voting on comments
All test data now uses the correct namespace for URI construction,
$type fields, and collection names. Tests verify that the new
namespace works correctly throughout the entire comment lifecycle.
All 22 integration tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update Jetstream comment consumer configuration to subscribe to
social.coves.community.comment collection instead of
social.coves.feed.comment.
Changes:
- Update COMMENT_JETSTREAM_URL wantedCollections parameter
- Update log message to reflect new collection name
The consumer will now listen for CREATE/UPDATE/DELETE operations
on the community.comment collection from the Jetstream firehose.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update all backend code to use social.coves.community.comment
namespace instead of social.coves.feed.comment.
Changes:
- comment_consumer.go: Update collection constant and URI construction
- vote_consumer.go: Update comment collection matching for votes
- comment.go: Update lexicon reference in documentation
- comment_service.go: Update record type in buildCommentRecord
- view_models.go: Update lexicon references in comments
- lexicon.go: Update ValidateComment to use new namespace
- record_utils.go: Update example documentation
All URI construction now uses social.coves.community.comment
for new comment records and collection filtering in Jetstream
consumers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add migration 018 to update all comment URIs from
social.coves.feed.comment to social.coves.community.comment.
Migration updates:
- comments.uri (main comment URIs)
- comments.root_uri (when root is a comment)
- comments.parent_uri (when parent is a comment)
Includes rollback support via -- +goose Down section for safe
reversibility. Since we're pre-production, only the comments table
is updated (votes table not affected).
Migration file: 018_migrate_comment_namespace.sql
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Migrate comment record lexicon from social.coves.feed.comment to
social.coves.community.comment to align with community-focused
architecture.
Changes:
- Move lexicon from feed/comment.json to community/comment.json
- Update lexicon ID: social.coves.community.comment
- Update test data lexicons (3 files) to use new namespace
This is part of the broader namespace migration to organize all
community-related records under the community namespace.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add two new scripts for generating realistic test data:
- generate_deep_thread.go: Creates deeply nested comment threads (100 levels)
for testing threading logic, depth limits, and performance
- generate_nba_comments.go: Generates NBA-themed comments with realistic
basketball discussion content for UX testing and demos
Both scripts:
- Insert directly into PostgreSQL (bypassing Jetstream for speed)
- Create realistic comment trees with varied content
- Useful for stress testing, performance validation, and demos
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove deprecated 'version' field from docker-compose.dev.yml.
Docker Compose v2 ignores this field and it's no longer needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add source_name field to Perspective model for better attribution
- Extract source name from HTML anchor tags in parser
- Display source name in rich text (e.g., "The Straits Times" vs generic "Source")
- Improve spacing in highlights, perspectives, and sources lists (double newlines)
- Better visual separation between list items
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add github.com/google/uuid for DPoP proof jti generation
- Add .cache/ to .gitignore for Go build cache
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document DPoP token binding implementation:
- Explain DPoP security model and why it's not a fallback
- Add flow diagrams for DPoP verification process
- Document replay protection with NonceCache
- Add code examples for DPoP verification
- List implemented security features and future enhancements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
JWT improvements:
- Add Confirmation field for DPoP cnf.jkt claim binding
- Reorder Claims struct fields for optimal memory alignment
Test improvements:
- Replace os.Setenv/os.Unsetenv with t.Setenv for cleaner tests
- Use t.Cleanup for automatic environment restoration
- Use UUID for DPoP proof jti to ensure test uniqueness
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance AtProtoAuthMiddleware with DPoP token binding support:
- Add Stop() method to prevent goroutine leaks on shutdown
- Require DPoP proof when token has cnf.jkt claim
- Treat DPoP-bound tokens without proof as unauthenticated in OptionalAuth
- Honor X-Forwarded-Proto header for URI verification behind proxies
Security model:
- DPoP is ADDITIONAL security, never a fallback for failed verification
- Token signature must be verified BEFORE checking DPoP binding
- Missing DPoP proof for bound tokens results in rejection
Tests added for:
- Middleware Stop() cleanup
- OptionalAuth with DPoP-bound tokens
- X-Forwarded-Proto handling
- DPoP replay protection integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add proof-of-possession verification for OAuth access tokens:
- DPoPVerifier for validating DPoP proof JWTs
- NonceCache for replay attack prevention with background cleanup
- JWK thumbprint calculation per RFC 7638
- Support for ES256 signing algorithm
- Configurable clock skew and proof age limits
Security features:
- Validates htm (HTTP method) and htu (HTTP URI) claims
- Enforces iat freshness within 5-minute window
- Tracks jti values to prevent proof reuse
- Calculates and validates JWK thumbprints for token binding
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document the dual JWT verification methods (HS256 + ES256) in environment
configuration files:
- HS256: For your own PDS (fast, shared secret, no network calls)
- ES256: For federated users (DID resolution, works with any PDS)
Updates:
- .env.dev: Add HS256_ISSUERS for local development
- .env.prod.example: Add JWT Authentication section with documentation
- docker-compose.prod.yml: Pass PDS_JWT_SECRET and HS256_ISSUERS to appview
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cache HS256_ISSUERS, PDS_JWT_SECRET, and IS_DEV_ENV at startup instead
of reading environment variables on every token verification request.
- Add jwtConfig struct with sync.Once initialization
- Use map[string]struct{} for O(1) issuer whitelist lookups
- Add InitJWTConfig() for explicit startup initialization
- Add ResetJWTConfigForTesting() for test isolation
- Update main.go to call InitJWTConfig() at startup
Before: 2-3 os.Getenv() calls + O(n) string iteration per request
After: Single pointer dereference + O(1) map lookup per request
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add support for verifying ES256 service auth tokens from federated users.
This enables users from any PDS (bsky.social, etc.) to authenticate with
Coves instances.
- DIDKeyFetcher: resolves DID documents via PLC directory to get public keys
- CombinedKeyFetcher: routes to DID or JWKS based on issuer format
- Supports did:plc: and did:web: issuers
- Converts atcrypto JWK to Go ecdsa.PublicKey for jwt-go verification
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds optional sources field to social.coves.embed.external lexicon
to support aggregator megathreads that combine multiple news sources.
Changes:
- Add #source definition with uri, title, domain, and optional sourcePost
- Add sources array (max 50) to #external for aggregated links
- Add maxLength constraints to domain (253) and provider (100) fields
- Update descriptions to clarify primary vs aggregated content
This enables LLM aggregators to create megathread posts that reference
multiple source articles, with optional strongRef to existing Coves
posts for future feed deprioritization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed camelCase NSIDs to lowercase to comply with atProto Lexicon
specification which requires NSIDs to use only lowercase letters:
- social.coves.actor.getProfile → social.coves.actor.getprofile
- social.coves.actor.updateProfile → social.coves.actor.updateprofile
Updated all code references including routes, tests, and documentation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Apply gofumpt formatting with extra-rules across all packages
- Fix mock interface signatures to match updated Service/Repository interfaces
- Fix ineffassign bugs in community_repo.go (sortColumn/sortOrder)
- Fix unchecked error returns in production code (register.go)
- Fix unchecked error returns in test files (defer closures)
- Optimize struct field alignment per govet fieldalignment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add configurable allowlist to restrict who can create communities during alpha.
Self-hosters can set their own DID in the env var.
- Add allowedCommunityCreators field to CreateHandler
- Load comma-separated DIDs from COMMUNITY_CREATORS env var
- Return 403 CommunityCreationRestricted for non-allowed users
- Empty/unset env var allows all authenticated users
- Filter empty strings from allowlist defensively
- Add comprehensive unit tests for allowlist behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update Go to 1.24 in Dockerfile
- Fix migrations path (internal/db/migrations)
- Add /xrpc/_health endpoint for Docker healthcheck
- Fix PORT env var precedence (PORT > APPVIEW_PORT)
- Add custom lexicon Jetstream URLs
- Add CURSOR_SECRET env var
- Comment out partial email config (PDS requires both or neither)
- Docker configuration (Dockerfile, docker-compose.prod.yml)
- Caddy reverse proxy with HSTS, CSP, wildcard SSL
- Deployment scripts (deploy.sh, setup-production.sh, backup.sh)
- DID key generation script
- OAuth callback with XSS protection
- Environment template (.env.prod.example)
Align social.coves.community.list endpoint to lexicon specification
with comprehensive testing and atProto compliance.
**Summary:**
- ✅ Lexicon-compliant parameter handling
- ✅ atProto-standard pagination (cursor-based)
- ✅ Input validation for all parameters
- ✅ Performance optimization (removed COUNT query)
- ✅ Comprehensive test coverage (8 new test cases)
- ✅ All tests passing
**Changes:**
- Add visibility parameter to lexicon
- Implement sort enum (popular/active/new/alphabetical)
- Fix cursor type (string vs int)
- Remove undocumented "total" field
- Add input validation for visibility and sort
- Update test suite with comprehensive coverage
Ready for alpha deployment 🚀
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive test coverage for social.coves.community.list
endpoint with all parameter combinations.
**New Test Cases:**
- List with sort=popular (default)
- List with sort=active
- List with sort=new
- List with sort=alphabetical (validates actual ordering)
- List with invalid sort value (expects 400)
- List with visibility filter
- List with default sort (no parameter)
- List with limit bounds validation
**Test Cleanup:**
- Remove deprecated "total" field from response structs
- Add "cursor" field to all list response structs
- Update repository tests for new List() signature
All tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Align social.coves.community.list handler to lexicon specification
following atProto standards.
**Changes:**
- Add visibility parameter (public/unlisted/private) to lexicon
- Implement sort enum mapping (popular→subscriber_count,
active→post_count, new→created_at, alphabetical→name)
- Add input validation for sort and visibility parameters
- Enforce limit bounds (1-100, default 50)
- Update ListCommunitiesRequest struct with new parameters
- Remove deprecated hostedBy parameter
**atProto Compliance:**
- Use string cursor type (not int)
- Remove undocumented "total" field (follows Bluesky patterns)
- Eliminate COUNT query for better performance
- Return empty cursor when pagination complete
**Performance:**
- Single query instead of COUNT + SELECT
- Proper cursor-based pagination
**Code Quality:**
- Fix magic number in GetDisplayHandle (11 → len(".community."))
- Add TODO comments for future category/language filters
Addresses lexicon contract violations and follows atProto design
patterns from bluesky-social/atproto#4245.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create starter issues for tracking Coves development:
- Complete post creation and moderation features [P1]
- Implement aggregator feed federation [P1]
- Add comprehensive API documentation [P2, blocked by features]
Issues tracked in .beads/issues.jsonl with dependency graph.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add complete Docker configuration for containerized deployment.
Files added:
- Dockerfile: Multi-stage Python 3.11 image with cron scheduler
- docker-compose.yml: Simple deployment configuration
- docker-entrypoint.sh: Startup script with validation
- .dockerignore: Build optimization
Features:
- Automated cron scheduling (daily at 1 PM UTC)
- Health checks (verifies cron is running)
- Log rotation (10MB max, 3 files)
- Auto-restart on failure
- Environment-based configuration
- Single command deployment: docker compose up -d
The container runs cron internally and streams logs to stdout,
making it production-ready and easy to monitor.
Updated README with comprehensive Docker deployment documentation
including quick start, configuration, testing, and production
deployment best practices.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add Kagi-specific automated registration script and update README.
Changes:
- Move setup-kagi-aggregator.sh to kagi-news/scripts/setup.sh
- Add comprehensive Registration section to README
- Document automated vs manual setup options
- Explain registration workflow and requirements
- Update project structure to reflect new scripts
The setup script automates all 4 registration steps:
1. PDS account creation
2. .well-known file generation
3. Coves registration via XRPC
4. Service declaration creation
This makes the Kagi aggregator self-contained and ready to be
split into its own repository.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive setup scripts and documentation for aggregator registration.
Scripts:
- 1-create-pds-account.sh: Create PDS account for aggregator
- 2-setup-wellknown.sh: Generate .well-known/atproto-did file
- 3-register-with-coves.sh: Register with Coves instance via XRPC
- 4-create-service-declaration.sh: Create service declaration record
Documentation:
- Detailed README with step-by-step instructions
- Troubleshooting guide
- Configuration examples (nginx/Apache)
- Security best practices
These scripts automate the 4-step process of:
1. Creating a DID for the aggregator
2. Proving domain ownership
3. Registering with Coves
4. Publishing service metadata
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement social.coves.aggregator.register endpoint for aggregator registration.
Features:
- Lexicon schema for registration request/response
- Domain verification via .well-known/atproto-did
- DID resolution and validation
- User table insertion for aggregators
- Comprehensive integration tests
The endpoint allows aggregators to register with a Coves instance by:
1. Providing their DID and domain
2. Verifying domain ownership via .well-known file
3. Getting indexed into the users table
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements mandatory bidirectional did:web verification matching Bluesky's
security model. This prevents domain impersonation attacks by requiring
DID documents to claim the handle domain in their alsoKnownAs field.
Security Improvements:
- MANDATORY bidirectional verification (hard-fail, not soft-fail)
- Verifies domain matching (handle domain == hostedBy domain)
- Fetches DID document from https://domain/.well-known/did.json
- Verifies DID document ID matches claimed DID
- NEW: Verifies DID document claims handle in alsoKnownAs field
- Rejects communities that fail verification (was: log warning only)
- Cache TTL increased from 1h to 24h (matches Bluesky recommendations)
Implementation:
- Location: internal/atproto/jetstream/community_consumer.go
- Verification runs in AppView Jetstream consumer (not creation API)
- Impact: Controls AppView indexing and federation trust
- Performance: Bounded LRU cache (1000 entries), rate limiting (10 req/s)
Attack Prevention:
✓ Domain impersonation (can't claim did:web:nintendo.com without owning it)
✓ DNS hijacking (bidirectional check fails even with DNS control)
✓ Reputation hijacking (can't point your domain to someone else's DID)
✓ AppView pollution (only legitimate communities indexed)
✓ Federation trust (other instances can verify instance identity)
Tests:
- Updated existing tests to handle mandatory verification
- Added comprehensive bidirectional verification tests with mock HTTP server
- All tests passing ✅
Documentation:
- PRD_BACKLOG.md: Marked did:web verification as COMPLETE
- PRD_ALPHA_GO_LIVE.md: Added production deployment requirements
- Clarified architecture: AppView (coves.social) + PDS (coves.me)
- Added PDS deployment checklist (separate domain required)
- Updated production environment checklist
- Added Jetstream configuration (Bluesky production firehose)
Production Requirements:
- Deploy .well-known/did.json to coves.social with alsoKnownAs field
- Set SKIP_DID_WEB_VERIFICATION=false (production)
- PDS must be on separate domain (coves.me, not coves.social)
- Jetstream connects to wss://jetstream2.us-east.bsky.network/subscribe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive PRD for implementing Lemmy-style federation in Beta:
**Overview:**
- Enable users on any Coves instance to post to communities on other instances
- Maintain community ownership (posts live in community repos)
- Use atProto-native service authentication pattern
**Key Features:**
- Cross-instance posting: user@instance-a posts to !community@instance-b
- atProto service auth via com.atproto.server.getServiceAuth
- Community moderation control maintained
- Allowlist-based federation (manual for Beta)
**Technical Approach:**
- Service-to-service JWT authentication
- Community credentials delegation to user's instance
- Proper record ownership in community repos
- Security: signature verification, rate limiting, moderation
**Deferred to Future:**
- Automatic instance discovery (Beta uses manual allowlist)
- Cross-instance moderation delegation
- Content mirroring/replication
- User migration between instances
**Target:** Beta Release
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated PRD to show all 6 E2E test suites are complete:
**Status Change:**
- From: "Pre-Alpha"
- To: "Pre-Alpha → **E2E Testing Complete** 🎉"
**Updates:**
- Added major progress update section at top
- Marked all E2E test sections (lines 211-312) as ✅ COMPLETE
- Added actual implementation times and test file locations
- Updated timeline estimate: 65-80 hours → 50-65 hours remaining
- Updated success criteria to show E2E test milestones achieved
- Updated next steps to show E2E tests moved to completed status
- Added celebration message for major milestone
**Test Suite Completion:**
✅ Full User Journey (40 min)
✅ Blob Upload (35 min)
✅ Multi-Community Timeline (30 min)
✅ Concurrent Scenarios (45 min - with race detection)
✅ Rate Limiting (25 min)
✅ Error Recovery (30 min)
Total: ~3 hours of E2E test implementation
**Next Phase:** P0 blockers (JWT verification, DPoP fix, did:web verification)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented all 6 critical E2E test suites required for Alpha launch:
1. **User Journey E2E Test** (user_journey_e2e_test.go)
- Tests complete user flow: signup → create community → post → comment → vote
- Uses real PDS accounts and Jetstream WebSocket subscription
- Validates full atProto write-forward architecture
- Fixed silent fallback: now fails by default if Jetstream times out
- Use ALLOW_SIMULATION_FALLBACK=true env var to enable fallback in CI
2. **Blob Upload E2E Test** (blob_upload_e2e_test.go)
- Tests image upload to PDS via com.atproto.repo.uploadBlob
- Fixed to use REAL PDS credentials instead of fake tokens
- Tests PNG, JPEG, and WebP (MIME only) format validation
- Validates blob references in post records
- Tests multiple images and external embed thumbnails
3. **Concurrent Scenarios Test** (concurrent_scenarios_test.go)
- Tests race conditions with 20-30 simultaneous users
- Added database record verification to detect duplicates/lost records
- Uses COUNT(*) and COUNT(DISTINCT) queries to catch race conditions
- Tests concurrent: voting, mixed voting, commenting, subscriptions
4. **Multi-Community Timeline Test** (timeline_test.go)
- Tests feed aggregation across multiple communities
- Validates sorting (hot, new, top) and pagination
- Tests cross-community post interleaving
5. **Rate Limiting E2E Test** (ratelimit_e2e_test.go)
- Tests 100 req/min general limit
- Tests 20 req/min comment endpoint limit
- Tests 10 posts/hour aggregator limit
- Removed fake summary test, converted to documentation
6. **Error Recovery Test** (error_recovery_test.go)
- Tests Jetstream connection retry logic
- Tests PDS unavailability handling
- Tests malformed event handling
- Renamed reconnection test to be honest about scope
- Improved SQL cleanup patterns to be more specific
**Architecture Validated:**
- atProto write-forward: writes to PDS, AppView indexes from Jetstream
- Real Docker infrastructure: PDS (port 3001), Jetstream (6008), PostgreSQL (5434)
- Graceful degradation: tests skip if infrastructure unavailable (CI-friendly)
**Security Tested:**
- Input validation at handler level
- Parameterized queries (no SQL injection)
- Authorization checks before operations
- Rate limiting enforcement
**Time Saved:** ~7-12 hours through parallel sub-agent implementation
**Test Quality:** Enhanced with database verification to catch race conditions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive Alpha launch readiness documentation and production
JWT verification testing infrastructure.
**Alpha Go-Live PRD** (docs/PRD_ALPHA_GO_LIVE.md):
- Track remaining work for alpha launch with real users
- P0 blockers: DPoP architecture fix, JWT verification (2 items)
- ✅ Validated handle resolution already implemented
- ✅ Validated comment_count reconciliation already implemented
- P1 infrastructure: Monitoring, logging, backups, load testing
- E2E testing recommendations (7 critical gaps identified)
- Timeline: 65-80 hours (~2-3 weeks)
- Go/no-go decision criteria and risk assessment
**JWT Verification Test** (tests/integration/jwt_verification_test.go):
- E2E test for JWT signature verification with real PDS
- Tests JWT parsing, JWKS fetching, and auth middleware
- Validates AUTH_SKIP_VERIFY=false production mode
- Handles both dev PDS (symmetric) and production PDS (JWKS)
- Tests tampered token rejection
Changes support alpha launch planning and production security validation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes two backlog issues:
1. Post comment_count reconciliation - Added tests proving it works
2. Handle resolution for block/unblock endpoints - Full implementation
Changes include:
- 15 new integration tests (all passing)
- Handle resolution with proper error handling (400/404/500)
- Updated documentation in PRD_BACKLOG.md
- Code formatting compliance with gofumpt
Mark the following backlog items as complete:
1. Post comment_count reconciliation (2025-11-16)
- Added comprehensive test coverage (4 test cases)
- Updated misleading FIXME comment to accurate documentation
- Verified existing reconciliation logic works correctly
2. Handle resolution for block endpoints (2025-11-16)
- Phase 3: Block/unblock endpoints now accept handles
- Added 11 test cases covering all identifier formats
- Proper error handling (400 for validation, 404 for not found)
- Phase 1 (post creation) was already complete
Both issues estimated at 2-3 hours each, completed with full test
coverage and documentation updates.
Update block and unblock handlers to accept at-identifiers (handles)
in addition to DIDs, resolving them via ResolveCommunityIdentifier().
Changes:
- Remove DID-only validation in HandleBlock and HandleUnblock
- Add ResolveCommunityIdentifier() call with proper error handling
- Map validation errors (malformed identifiers) to 400 Bad Request
- Map not-found errors to 404
- Map other errors to 500 Internal Server Error
Supported formats:
- DIDs: did:plc:xxx, did:web:xxx
- Canonical handles: gaming.community.coves.social
- @-prefixed handles: @gaming.community.coves.social
- Scoped format: !gaming@coves.social
Test coverage (11 test cases):
- Block with canonical handle
- Block with @-prefixed handle
- Block with scoped format
- Block with DID (backwards compatibility)
- Block with malformed identifiers (4 cases - returns 400)
- Block with invalid/nonexistent handle (returns 404)
- Unblock with handle
- Unblock with invalid handle
Addresses PR feedback: Validation errors now return 400 instead of 500
Fixes issue: Handle Resolution Missing
Affected: Post creation, blocking endpoints
Add comprehensive integration tests verifying that post comment_count
is correctly reconciled when comments arrive before their parent post
due to out-of-order Jetstream event delivery.
Test coverage:
- Single comment arrives before post - count reconciled to 1
- Multiple comments arrive before post - count reconciled correctly
- Mixed ordering (comments before and after post) - count accurate
- Idempotent post indexing preserves comment_count
Also update misleading FIXME comment in comment_consumer.go to accurate
NOTE explaining that reconciliation IS implemented in post_consumer.go
at lines 210-226.
Fixes issue: Post comment_count Never Reconciles
File: internal/atproto/jetstream/comment_consumer.go:362
Complete migration of comment namespace from social.coves.feed.comment
to social.coves.community.comment.
This migration aligns the comment system with the community-focused
architecture and ensures all comment records use the correct namespace.
Summary:
- ✅ Lexicon migrated to community/comment.json
- ✅ Database migration 018 applied successfully
- ✅ All backend code updated (consumer, service, validation)
- ✅ Server configuration updated for Jetstream
- ✅ All 22 integration tests passing
- ✅ Test scripts and data updated
- ✅ Documentation updated
Migration verified with:
- Unit tests: ✅ PASSING
- Integration tests: ✅ 22/22 PASSING
- Build: ✅ SUCCESS
- Database: ✅ Migration 018 applied
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update COMMENT_SYSTEM_IMPLEMENTATION.md to reflect the completed
migration from social.coves.feed.comment to
social.coves.community.comment.
Changes:
- Mark Phase 4 (Namespace Migration) as COMPLETED (2025-11-16)
- Update all namespace references throughout the document
- Add migration completion details and scope
Documentation now accurately reflects the current implementation
using the community.comment namespace for all comment records.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update all test comment generation scripts to use
social.coves.community.comment namespace in URI construction.
Scripts updated:
- generate_deep_thread.go: Creates nested comment threads
- generate_nba_comments.go: Generates NBA discussion comments
- generate_test_comments.go: Creates general test comments
All scripts now generate comment URIs with the correct namespace
pattern: at://did/social.coves.community.comment/rkey
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update all comment integration tests to use the new
social.coves.community.comment namespace.
Files updated:
- comment_consumer_test.go: 18 tests covering create/update/delete
- comment_query_test.go: 11 tests covering queries and pagination
- comment_vote_test.go: 6 tests covering voting on comments
All test data now uses the correct namespace for URI construction,
$type fields, and collection names. Tests verify that the new
namespace works correctly throughout the entire comment lifecycle.
All 22 integration tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update Jetstream comment consumer configuration to subscribe to
social.coves.community.comment collection instead of
social.coves.feed.comment.
Changes:
- Update COMMENT_JETSTREAM_URL wantedCollections parameter
- Update log message to reflect new collection name
The consumer will now listen for CREATE/UPDATE/DELETE operations
on the community.comment collection from the Jetstream firehose.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update all backend code to use social.coves.community.comment
namespace instead of social.coves.feed.comment.
Changes:
- comment_consumer.go: Update collection constant and URI construction
- vote_consumer.go: Update comment collection matching for votes
- comment.go: Update lexicon reference in documentation
- comment_service.go: Update record type in buildCommentRecord
- view_models.go: Update lexicon references in comments
- lexicon.go: Update ValidateComment to use new namespace
- record_utils.go: Update example documentation
All URI construction now uses social.coves.community.comment
for new comment records and collection filtering in Jetstream
consumers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add migration 018 to update all comment URIs from
social.coves.feed.comment to social.coves.community.comment.
Migration updates:
- comments.uri (main comment URIs)
- comments.root_uri (when root is a comment)
- comments.parent_uri (when parent is a comment)
Includes rollback support via -- +goose Down section for safe
reversibility. Since we're pre-production, only the comments table
is updated (votes table not affected).
Migration file: 018_migrate_comment_namespace.sql
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Migrate comment record lexicon from social.coves.feed.comment to
social.coves.community.comment to align with community-focused
architecture.
Changes:
- Move lexicon from feed/comment.json to community/comment.json
- Update lexicon ID: social.coves.community.comment
- Update test data lexicons (3 files) to use new namespace
This is part of the broader namespace migration to organize all
community-related records under the community namespace.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add two new scripts for generating realistic test data:
- generate_deep_thread.go: Creates deeply nested comment threads (100 levels)
for testing threading logic, depth limits, and performance
- generate_nba_comments.go: Generates NBA-themed comments with realistic
basketball discussion content for UX testing and demos
Both scripts:
- Insert directly into PostgreSQL (bypassing Jetstream for speed)
- Create realistic comment trees with varied content
- Useful for stress testing, performance validation, and demos
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add source_name field to Perspective model for better attribution
- Extract source name from HTML anchor tags in parser
- Display source name in rich text (e.g., "The Straits Times" vs generic "Source")
- Improve spacing in highlights, perspectives, and sources lists (double newlines)
- Better visual separation between list items
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>