config.go (2933B)
1 // config.go — global configuration, account definitions, and age key initialisation. 2 package main 3 4 import ( 5 "fmt" 6 "log/slog" 7 "os" 8 "strings" 9 "time" 10 11 "filippo.io/age" 12 "maunium.net/go/mautrix/id" 13 ) 14 15 // ourServerName is used to decide whose media and avatars to download. 16 // Override with S3_SERVER_NAME if needed. 17 var ourServerName = envOrDefault("SERVER_NAME", "mtrnord.blog") 18 19 type accountCfg struct { 20 UserID id.UserID 21 Password string 22 SSSSKey string 23 Prefix string 24 StoreDir string 25 } 26 27 var ( 28 homeserver = mustEnv("HOMESERVER") 29 s3Endpoint = mustEnv("S3_ENDPOINT") 30 s3BucketName = mustEnv("S3_BUCKET") 31 s3AccessKey = mustEnv("S3_ACCESS_KEY") 32 s3SecretKey = mustEnv("S3_SECRET_KEY") 33 s3Region = envOrDefault("S3_REGION", "hel1") 34 keyExportPass = mustEnv("KEY_EXPORT_PASSPHRASE") 35 dateStr = time.Now().UTC().Format("20060102") 36 runStr = time.Now().UTC().Format("20060102-150405") 37 38 accounts = []accountCfg{ 39 { 40 UserID: "@mtrnord:mtrnord.blog", 41 Password: os.Getenv("MTRNORD_PASSWORD"), 42 SSSSKey: os.Getenv("MTRNORD_SSSS_KEY"), 43 Prefix: "mtrnord", 44 StoreDir: "/data/crypto/mtrnord", 45 }, 46 { 47 UserID: "@lexi:mtrnord.blog", 48 Password: os.Getenv("LEXI_PASSWORD"), 49 SSSSKey: os.Getenv("LEXI_SSSS_KEY"), 50 Prefix: "lexi", 51 StoreDir: "/data/crypto/lexi", 52 }, 53 } 54 55 // ageRecipients holds the parsed public keys used to encrypt history files. 56 ageRecipients []age.Recipient 57 58 // ageIdentity is the optional private key used to decrypt files written by 59 // this tool (loaded from AGE_PRIVATE_KEY). Allows reading back previously 60 // encrypted objects without storing plain copies in S3. 61 ageIdentity age.Identity 62 ) 63 64 func mustEnv(key string) string { 65 v := os.Getenv(key) 66 if v == "" { 67 slog.Error("Required environment variable not set", "key", key) 68 os.Exit(1) 69 } 70 return v 71 } 72 73 func envOrDefault(key, def string) string { 74 if v := os.Getenv(key); v != "" { 75 return v 76 } 77 return def 78 } 79 80 // initAgeRecipients parses a comma-separated list of age public keys. 81 func initAgeRecipients(s string) error { 82 for _, raw := range strings.Split(s, ",") { 83 raw = strings.TrimSpace(raw) 84 if raw == "" { 85 continue 86 } 87 r, err := age.ParseX25519Recipient(raw) 88 if err != nil { 89 return fmt.Errorf("invalid age recipient %q: %w", raw, err) 90 } 91 ageRecipients = append(ageRecipients, r) 92 } 93 if len(ageRecipients) == 0 { 94 return fmt.Errorf("AGE_RECIPIENTS is empty — at least one public key is required") 95 } 96 return nil 97 } 98 99 // initAgeIdentity parses an AGE-SECRET-KEY-1… line for decrypting previously 100 // written .age objects (e.g. the room list, session token). 101 func initAgeIdentity(privKey string) error { 102 privKey = strings.TrimSpace(privKey) 103 if privKey == "" { 104 return nil 105 } 106 ids, err := age.ParseIdentities(strings.NewReader(privKey)) 107 if err != nil { 108 return fmt.Errorf("parse age identity: %w", err) 109 } 110 if len(ids) > 0 { 111 ageIdentity = ids[0] 112 } 113 return nil 114 }