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 isDevEnv bool // true = generate without registering, false = register with PLC
13 plcDirectoryURL string // PLC directory URL (only used when isDevEnv=false)
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 if !g.isDevEnv {
55 // Future: implement PLC registration here
56 // return "", fmt.Errorf("PLC registration not yet implemented")
57 }
58
59 return did, nil
60}
61
62// ValidateDID checks if a DID string is properly formatted
63// Supports did:plc, did:web (for instances)
64func ValidateDID(did string) bool {
65 if !strings.HasPrefix(did, "did:") {
66 return false
67 }
68
69 parts := strings.Split(did, ":")
70 if len(parts) < 3 {
71 return false
72 }
73
74 method := parts[1]
75 identifier := parts[2]
76
77 // Basic validation: method and identifier must not be empty
78 return method != "" && identifier != ""
79}