package main import ( "context" "encoding/base64" "errors" "flag" "fmt" "log/slog" "net" "os" "path/filepath" "runtime" "strings" "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" "koti.casa/numenor-labs/dsfx/pkg/logging" "koti.casa/numenor-labs/dsfx/pkg/network" ) var () func main() { ctx := context.Background() // --------------------------------------------------------------------------- // Logger homedir, err := os.UserHomeDir() if err != nil { logUserMsg("error: failed to get home directory\n") logUserMsg("error: are you on a real computer?\n") // Look up why this function might fail... os.Exit(1) } flagHost := flag.String("host", "localhost", "the host to listen on") flagPort := flag.Int("port", 8000, "the port to listen on") flagDir := flag.String("dir", filepath.Join(homedir, ".dsfxnode"), "the directory to store data in") flagLogFile := flag.String("log", "logs", "the file to write logs to, relative to -dir") flag.Parse() dataDir := *flagDir err = os.Mkdir(dataDir, 0777) if errors.Is(err, os.ErrExist) { // If the directory already exists, we can ignore the error. err = nil } if err != nil { logUserMsg("error: failed to create the data directory: %v\n", err) os.Exit(1) } var logFile *os.File if *flagLogFile == "stdout" { logFile = os.Stdout } else { logFilePath := filepath.Join(dataDir, *flagLogFile) logFile, err = os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { logUserMsg("warn: log file missing, making a new one: %s\n", logFilePath) logFile, err = os.Create(logFilePath) if err != nil { logUserMsg("error: failed to create log file: %v\n", err) os.Exit(1) } } } defer logFile.Close() opts := &slog.HandlerOptions{ AddSource: false, Level: slog.LevelDebug, } logger := slog.New(slog.NewTextHandler(logFile, opts)) // Everything in the application will attempt to use the logger in stored in // the context, but we also set the default with slog as a fallback. In cases // where the context is not available, or the context is not a child of the // context with the logger, the default logger will be used. slog.SetDefault(logger) ctx = logging.WithContext(ctx, logger) keyFile, err := os.Open(filepath.Join(dataDir, "key")) if err != nil { logUserMsg("warn: key file is missing, making a new one\n") logUserMsg("warn: if this is your first time running dsfxctl, you can ignore this\n") keyFile, err = os.Create(filepath.Join(dataDir, "key")) if err != nil { logUserMsg("error: failed to create key file: %v\n", err) os.Exit(1) } privkey, err := identity.Generate() if err != nil { logUserMsg("error: failed to generate key: %v\n", err) os.Exit(1) } _, err = keyFile.Write([]byte(base64.StdEncoding.EncodeToString(privkey))) if err != nil { logUserMsg("error: failed to write key: %v\n", err) os.Exit(1) } } defer keyFile.Close() // --------------------------------------------------------------------------- // Flags masterKey, err := identity.LoadSigningKeyFromFile(filepath.Join(dataDir, "key")) if err != nil { logUserMsg("error: failed to load key: %v\n", err) os.Exit(1) } var admins []string adminsFile, err := os.ReadFile(filepath.Join(dataDir, "admins")) if err != nil { logUserMsg("warn: failed to read admins file.. no one will be able to control this server..\n") logUserMsg(` If you aren't sure what this means, here's a brief explanation: The admins file is a list of public keys that are allowed to control the server. The base are base64 encoded and separated by newlines. The application looks for this file at