package main import ( "log" "os" "path/filepath" "strconv" "fiatjaf.com/nostr" ) func envOr(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } // envIntOr parses a non-negative integer env var, falling back on empty/invalid. func envIntOr(key string, fallback int) int { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil && n >= 0 { return n } log.Printf("invalid %s=%q; using %d", key, v, fallback) } return fallback } // loadOrGenerateKey resolves the relay's signing identity, which signs the group // metadata/admins/members events. A STABLE identity is required so those signed // lists remain valid across restarts and re-deploys. // // - RELAY_SECRET_KEY set → use it (production path). // - RELAY_ENV=production, unset → fatal. A fresh key each boot silently // corrupts signed group rosters, so we refuse to start. // - otherwise (dev) → persist a generated key under the data dir // and reuse it, so even dev keeps a stable identity across restarts. func loadOrGenerateKey(dataDir string) nostr.SecretKey { if hex := os.Getenv("RELAY_SECRET_KEY"); hex != "" { sk, err := nostr.SecretKeyFromHex(hex) if err != nil { log.Fatalf("invalid RELAY_SECRET_KEY: %v", err) } return sk } if isProduction() { log.Fatal("RELAY_SECRET_KEY is required when RELAY_ENV=production " + "(a fresh key each boot would corrupt signed group rosters). " + "Generate one with: openssl rand -hex 32") } // Dev convenience: persist a generated key so restarts keep one identity. keyPath := filepath.Join(dataDir, "relay.key") if data, err := os.ReadFile(keyPath); err == nil { if sk, err := nostr.SecretKeyFromHex(string(data)); err == nil { log.Printf("using persisted dev relay identity from %s", keyPath) return sk } log.Printf("ignoring unreadable %s; generating a new dev key", keyPath) } sk := nostr.Generate() if err := os.WriteFile(keyPath, []byte(sk.Hex()), 0o600); err != nil { log.Printf("could not persist dev relay key (%v); identity is ephemeral this run", err) } else { log.Printf("generated and persisted a dev relay identity at %s (set RELAY_SECRET_KEY for production)", keyPath) } return sk } func isProduction() bool { return os.Getenv("RELAY_ENV") == "production" }