package main import ( "fmt" "github.com/enmanuel/unibus/pkg/membership" "github.com/nats-io/nats.go/jetstream" ) // wireReplicatedNonces applies the cluster anti-replay policy to srv. It is the // single piece of wiring the binary uses to decide whether a node must share its // nonce store, extracted so a regression test exercises the EXACT decision the // running binary makes (issue 0006a, audit 0008 N3). // // Policy: // - A clustered node (clustered == true) MUST use the shared JetStream KV nonce // bucket. Every node sees the same bucket, so a request accepted on one node // cannot be replayed to another whose per-process cache never saw the nonce. // A missing JetStream context, or a failure to create the bucket, is a FATAL // configuration error returned to the caller — a clustered node running with a // per-process nonce cache is precisely the replay hole the audit flagged, so // it must refuse to start rather than serve insecurely. // - A standalone node (clustered == false) keeps the in-memory cache that // NewServer installed: there is no second node to replay to, so the shared // bucket would only add a JetStream dependency for no security gain. // // replicas is the nonce bucket's replication factor (R1..R3). Returns nil when no // action is required (standalone). func wireReplicatedNonces(srv *membership.Server, js jetstream.JetStream, clustered bool, replicas int) error { if !clustered { return nil // standalone: the in-memory nonce cache is sufficient and safe } if js == nil { return fmt.Errorf("clustered node requires JetStream for the shared nonce bucket, but none is available") } if err := srv.UseReplicatedNonces(js, replicas); err != nil { return fmt.Errorf("replicated nonces: %w", err) } return nil }