From 5aa5f7275b7a08015816171ba06d2cbdc2e02d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Mon, 9 Feb 2026 00:32:23 +0100 Subject: [PATCH] feat: add NATS NKey authentication support Allow authentication to NATS using NKey seed files as an alternative to credentials files. NKeys use Ed25519 key pairs for authentication. Co-Authored-By: Claude Opus 4.5 --- README.md | 2 ++ collector/flake.go | 11 +++++++++-- config/config.go | 2 ++ main.go | 3 ++- module.nix | 8 ++++++++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 07c2643..cac7871 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ nix build | `--flake.nats.url` | `nats://localhost:4222` | NATS server URL | | `--flake.nats.subject` | `nixos-exporter.remote-rev` | NATS subject for revision updates | | `--flake.nats.credentials-file` | | NATS credentials file (optional) | +| `--flake.nats.nkey-seed-file` | | NATS NKey seed file (optional) | ## NixOS Module Options @@ -68,6 +69,7 @@ services.prometheus.exporters.nixos = { url = "nats://localhost:4222"; subject = "nixos-exporter.remote-rev"; credentialsFile = null; # Optional path to credentials file + nkeySeedFile = null; # Optional path to NKey seed file }; }; }; diff --git a/collector/flake.go b/collector/flake.go index 96249b5..2df9c59 100644 --- a/collector/flake.go +++ b/collector/flake.go @@ -33,6 +33,7 @@ type FlakeCollectorConfig struct { NATSURL string NATSSubject string NATSCredentialsFile string + NATSNkeySeedFile string } // nixosVersionInfo holds the parsed output of nixos-version --json @@ -107,7 +108,7 @@ func NewFlakeCollectorWithNATS(cfg FlakeCollectorConfig) (*FlakeCollector, error c.natsEnabled = true c.natsSubject = cfg.NATSSubject - if err := c.connectNATS(cfg.NATSURL, cfg.NATSCredentialsFile); err != nil { + if err := c.connectNATS(cfg.NATSURL, cfg.NATSCredentialsFile, cfg.NATSNkeySeedFile); err != nil { // Log warning but continue without NATS slog.Warn("Failed to connect to NATS, continuing without cache sharing", "error", err) } @@ -333,7 +334,7 @@ func getNixosVersionInfo() (*nixosVersionInfo, error) { } // connectNATS establishes connection to NATS server with auto-reconnect -func (c *FlakeCollector) connectNATS(url, credentialsFile string) error { +func (c *FlakeCollector) connectNATS(url, credentialsFile, nkeySeedFile string) error { opts := []nats.Option{ nats.MaxReconnects(-1), // Infinite reconnects nats.ReconnectWait(5 * time.Second), @@ -363,6 +364,12 @@ func (c *FlakeCollector) connectNATS(url, credentialsFile string) error { if credentialsFile != "" { opts = append(opts, nats.UserCredentials(credentialsFile)) + } else if nkeySeedFile != "" { + opt, err := nats.NkeyOptionFromSeed(nkeySeedFile) + if err != nil { + return fmt.Errorf("failed to load NKey seed file: %w", err) + } + opts = append(opts, opt) } nc, err := nats.Connect(url, opts...) diff --git a/config/config.go b/config/config.go index 342cd6b..de7de55 100644 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,7 @@ type Config struct { FlakeNATSURL string FlakeNATSSubject string FlakeNATSCredentialsFile string + FlakeNATSNkeySeedFile string } func Parse() (*Config, error) { @@ -32,6 +33,7 @@ func Parse() (*Config, error) { flag.StringVar(&cfg.FlakeNATSURL, "flake.nats.url", "nats://localhost:4222", "NATS server URL") flag.StringVar(&cfg.FlakeNATSSubject, "flake.nats.subject", "nixos-exporter.remote-rev", "NATS subject for revision updates") flag.StringVar(&cfg.FlakeNATSCredentialsFile, "flake.nats.credentials-file", "", "NATS credentials file (optional)") + flag.StringVar(&cfg.FlakeNATSNkeySeedFile, "flake.nats.nkey-seed-file", "", "NATS NKey seed file (optional)") flag.Parse() diff --git a/main.go b/main.go index 2c09899..06706d2 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -const version = "0.3.0" +const version = "0.4.0" func main() { cfg, err := config.Parse() @@ -40,6 +40,7 @@ func main() { NATSURL: cfg.FlakeNATSURL, NATSSubject: cfg.FlakeNATSSubject, NATSCredentialsFile: cfg.FlakeNATSCredentialsFile, + NATSNkeySeedFile: cfg.FlakeNATSNkeySeedFile, }) if err != nil { slog.Error("Failed to create flake collector", "error", err) diff --git a/module.nix b/module.nix index 1a0e3d6..0b07e0a 100644 --- a/module.nix +++ b/module.nix @@ -58,6 +58,12 @@ in default = null; description = "Path to NATS credentials file."; }; + + nkeySeedFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to NATS NKey seed file."; + }; }; }; @@ -133,6 +139,8 @@ in "--flake.nats.subject=${cfg.flake.nats.subject}" ] ++ lib.optionals (cfg.flake.nats.enable && cfg.flake.nats.credentialsFile != null) [ "--flake.nats.credentials-file=${cfg.flake.nats.credentialsFile}" + ] ++ lib.optionals (cfg.flake.nats.enable && cfg.flake.nats.nkeySeedFile != null) [ + "--flake.nats.nkey-seed-file=${cfg.flake.nats.nkeySeedFile}" ]); Restart = "on-failure"; RestartSec = "5s";