A community based topic aggregation platform built on atproto
1package did
2
3import (
4 "crypto/rand"
5 "encoding/base32"
6 "fmt"
7 "strings"
8)
9
10// Generator creates DIDs for Coves entities
11type Generator struct {
12 plcDirectoryURL string
13 isDevEnv bool
14}
15
16// NewGenerator creates a new DID generator
17// isDevEnv: true for local development (no PLC registration), false for production (register with PLC)
18// plcDirectoryURL: URL for PLC directory (e.g., "https://plc.directory")
19func NewGenerator(isDevEnv bool, plcDirectoryURL string) *Generator {
20 return &Generator{
21 isDevEnv: isDevEnv,
22 plcDirectoryURL: plcDirectoryURL,
23 }
24}
25
26// GenerateCommunityDID creates a new random DID for a community
27// Format: did:plc:{base32-random}
28//
29// Dev mode (isDevEnv=true): Generates did:plc:xxx without registering to PLC
30// Prod mode (isDevEnv=false): Generates did:plc:xxx AND registers with PLC directory
31//
32// See: https://github.com/bluesky-social/did-method-plc
33func (g *Generator) GenerateCommunityDID() (string, error) {
34 // Generate 16 random bytes for the DID identifier
35 randomBytes := make([]byte, 16)
36 if _, err := rand.Read(randomBytes); err != nil {
37 return "", fmt.Errorf("failed to generate random DID: %w", err)
38 }
39
40 // Encode as base32 (lowercase, no padding) - matches PLC format
41 encoded := base32.StdEncoding.EncodeToString(randomBytes)
42 encoded = strings.ToLower(strings.TrimRight(encoded, "="))
43
44 did := fmt.Sprintf("did:plc:%s", encoded)
45
46 // TODO: In production (isDevEnv=false), register this DID with PLC directory
47 // This would involve:
48 // 1. Generate signing keypair for the DID
49 // 2. Create DID document with service endpoints
50 // 3. POST to plcDirectoryURL to register
51 // 4. Store keypair securely for future DID updates
52 //
53 // For now, we just generate the identifier (works fine for local dev)
54 // Production PLC registration is not yet implemented - DIDs are generated
55 // locally but not registered with the PLC directory. This is acceptable
56 // for development and private instances, but production deployments should
57 // implement full PLC registration to ensure DIDs are globally resolvable.
58 _ = g.isDevEnv // Acknowledge that isDevEnv will be used when PLC registration is implemented
59
60 return did, nil
61}
62
63// ValidateDID checks if a DID string is properly formatted
64// Supports did:plc, did:web (for instances)
65func ValidateDID(did string) bool {
66 if !strings.HasPrefix(did, "did:") {
67 return false
68 }
69
70 parts := strings.Split(did, ":")
71 if len(parts) < 3 {
72 return false
73 }
74
75 method := parts[1]
76 identifier := parts[2]
77
78 // Basic validation: method and identifier must not be empty
79 return method != "" && identifier != ""
80}