feat(client): opt-in nkey NATS connection via NewWithOptions
nats.go refuses to connect with an nkey to a server that does not advertise
nkey auth, so the connection cannot blindly always present one. New keeps the
legacy plain connection; NewWithOptions(Options{UseNkey:true}) presents the
peer's identity-derived nkey. NewWithOptions is the single place the data-plane
connection is built, so every peer gets identical behavior from the same
Options (TLS fields arrive in phase 0001d).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+31
-3
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user