package relay import ( "crypto/rand" "encoding/hex" "errors" "os" "path/filepath" "strings" "git.smesh.lol/torly/pkg/config" "git.smesh.lol/torly/pkg/crypto/secp256k1" "git.smesh.lol/torly/pkg/nostr" ) // loadRelayKey resolves the key torly uses to sign its own events (today the // _graph result). The key comes from TORLY_SECRET_KEY when set, otherwise from // DATA_DIR/relay.key, which is created on first start. A relay identity must // survive restarts, so a key file is preferred over an ephemeral key. func loadRelayKey(cfg *config.Config) ([32]byte, error) { var sk [32]byte if raw := strings.TrimSpace(cfg.SecretKey); raw != "" { if err := nostr.Decode32(raw, sk[:]); err != nil { return sk, errors.New("secret key must be 64 lowercase hex characters") } } else { path := filepath.Join(cfg.DataDir, "relay.key") body, err := os.ReadFile(path) switch { case err == nil: if err := decodeKeyFile(body, sk[:]); err != nil { return sk, err } case os.IsNotExist(err): if err := createRelayKey(path, &sk); err != nil { return sk, err } default: return sk, err } } pub, ok := secp256k1.PubKey(sk) if !ok { return sk, errors.New("invalid relay secret key") } cfg.RelayPubKey = nostr.Hex(pub[:]) return sk, nil } // decodeKeyFile accepts either the 32 raw bytes or the 64-hex text form, so a // key generated by nak or another tool can be dropped in unchanged. func decodeKeyFile(body, dst []byte) error { if len(body) == 32 { copy(dst, body) return nil } text := strings.TrimSpace(string(body)) if err := nostr.Decode32(text, dst); err != nil { return errors.New("relay key file must contain 32 raw bytes or 64 hex characters") } return nil } func createRelayKey(path string, dst *[32]byte) error { for { if _, err := rand.Read(dst[:]); err != nil { return err } if _, ok := secp256k1.PubKey(*dst); ok { break } } if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return err } tmp := path + ".tmp" text := hex.EncodeToString(dst[:]) + "\n" if err := os.WriteFile(tmp, []byte(text), 0o600); err != nil { return err } return os.Rename(tmp, path) }