A community based topic aggregation platform built on atproto

feat(server): integrate did:web verification with configuration

Integrates hostedBy verification into the server with environment-based
configuration for development and production use.

Changes:
- Added SKIP_DID_WEB_VERIFICATION env var for dev mode bypass
- Updated consumer initialization with instance DID and skip flag
- Added warning logs when verification is disabled
- Configured .env.dev with skip flag enabled for local development

Server logs will now show:
- "⚠️ WARNING: did:web verification DISABLED (dev mode)" when skipped
- "🚨 SECURITY: Rejecting community" when domain mismatch detected

Production Deployment:
- Set SKIP_DID_WEB_VERIFICATION=false or leave unset
- Ensure .well-known/did.json is properly configured

Co-Authored-By: Claude <noreply@anthropic.com>

+17 -7
+6
.env.dev
··· 137 137 # Always true for local development (use PLC_DIRECTORY_URL to control registration) 138 138 IS_DEV_ENV=true 139 139 140 + # Security: Skip did:web domain verification for local development 141 + # IMPORTANT: Set to false in production to prevent domain spoofing attacks 142 + # When true, communities can claim any hostedByDID without verification 143 + # When false, hostedByDID must match the community handle domain 144 + SKIP_DID_WEB_VERIFICATION=true 145 + 140 146 # Logging 141 147 LOG_LEVEL=debug 142 148 LOG_ENABLED=true
+11 -7
cmd/server/main.go
··· 148 148 // We cannot allow arbitrary domains to prevent impersonation attacks 149 149 // Example attack: !leagueoflegends@riotgames.com on a non-Riot instance 150 150 // 151 - // TODO (Security - V2.1): Implement did:web domain verification 152 - // Currently, any self-hoster can set INSTANCE_DID=did:web:nintendo.com without 153 - // actually owning nintendo.com. This allows domain impersonation attacks. 154 - // Solution: Verify domain ownership by fetching https://domain/.well-known/did.json 155 - // and ensuring it matches the claimed DID. See: https://atproto.com/specs/did-web 156 - // Alternatively, switch to did:plc for instance DIDs (cryptographically unique). 151 + // SECURITY: did:web domain verification is implemented in the Jetstream consumer 152 + // See: internal/atproto/jetstream/community_consumer.go - verifyHostedByClaim() 153 + // Communities with mismatched hostedBy domains are rejected during indexing 157 154 var instanceDomain string 158 155 if strings.HasPrefix(instanceDID, "did:web:") { 159 156 // Extract domain from did:web (this is the authoritative source) ··· 229 226 communityJetstreamURL = "ws://localhost:6008/subscribe?wantedCollections=social.coves.community.profile&wantedCollections=social.coves.community.subscription" 230 227 } 231 228 232 - communityEventConsumer := jetstream.NewCommunityEventConsumer(communityRepo) 229 + // Initialize community event consumer with did:web verification 230 + skipDIDWebVerification := os.Getenv("SKIP_DID_WEB_VERIFICATION") == "true" 231 + if skipDIDWebVerification { 232 + log.Println("⚠️ WARNING: did:web domain verification is DISABLED (dev mode)") 233 + log.Println(" Set SKIP_DID_WEB_VERIFICATION=false for production") 234 + } 235 + 236 + communityEventConsumer := jetstream.NewCommunityEventConsumer(communityRepo, instanceDID, skipDIDWebVerification) 233 237 communityJetstreamConnector := jetstream.NewCommunityJetstreamConnector(communityEventConsumer, communityJetstreamURL) 234 238 235 239 go func() {