diff --git a/pkg/client/client.go b/pkg/client/client.go index 52d05e02..3d84db20 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -28,6 +28,7 @@ import ( cs "fn-registry/functions/cybersecurity" + "github.com/enmanuel/unibus/pkg/busauth" "github.com/enmanuel/unibus/pkg/frame" "github.com/enmanuel/unibus/pkg/membership" "github.com/enmanuel/unibus/pkg/room" @@ -56,10 +57,37 @@ type Client struct { signCache map[string][]byte // sender endpoint -> sign pub (for verification) } -// New connects to NATS and records the control-plane URL. The identity holds -// the peer's long-term keypairs. +// Options configures how a client connects to the bus. The zero value is the +// legacy behavior: a plain NATS connection with no nkey and no TLS — what dev +// stacks and a not-yet-secured server expect. Secured deployments set these. +type Options struct { + // UseNkey authenticates the NATS connection with the peer's Ed25519 identity + // reused as a NATS nkey. It MUST match the server: nats.go refuses to connect + // with an nkey to a server that does not advertise nkey auth ("nkeys not + // supported by the server"), so this is opt-in rather than always-on. + UseNkey bool +} + +// New connects to NATS and records the control-plane URL with default Options +// (no nkey, no TLS). The identity holds the peer's long-term keypairs. func New(natsURL, ctrlURL string, id cs.Identity) (*Client, error) { - nc, err := nats.Connect(natsURL, nats.Name("unibus-client")) + return NewWithOptions(natsURL, ctrlURL, id, Options{}) +} + +// NewWithOptions is New with explicit connection options (nkey auth, and, from +// phase 0001d, TLS). It is the single place the data-plane connection is built, +// so every peer (worker, chat, mobile, gateway) gets identical behavior by +// passing the same Options. +func NewWithOptions(natsURL, ctrlURL string, id cs.Identity, opts Options) (*Client, error) { + natsOpts := []nats.Option{nats.Name("unibus-client")} + if opts.UseNkey { + nkeyPub, nkeySign, err := busauth.ClientNkey(id.SignPriv) + if err != nil { + return nil, fmt.Errorf("client: derive nkey: %w", err) + } + natsOpts = append(natsOpts, nats.Nkey(nkeyPub, nkeySign)) + } + nc, err := nats.Connect(natsURL, natsOpts...) if err != nil { return nil, fmt.Errorf("client: connect nats %q: %w", natsURL, err) }