NATS Server — Interfaces#
Interface catalog#
RaftNode#
- Package:
server - File:
server/raft.go:40 - Methods (53):
Propose,ProposeMulti,ForwardProposal,InstallSnapshot,CreateSnapshotCheckpoint,SendSnapshot,NeedSnapshot,Applied,Processed,State,Size,Progress,Leader,LeaderSince,Quorum,Current,Healthy,Term,Leaderless,GroupLeader,HadPreviousLeader,StepDown,SetObserver,IsObserver,Campaign,CampaignImmediately,ID,Group,Peers,ProposeKnownPeers,UpdateKnownPeers,ProposeAddPeer,ProposeRemovePeer,MembershipChangeInProgress,AdjustClusterSize,AdjustBootClusterSize,ClusterSize,ApplyQ,PauseApply,ResumeApply,DrainAndReplaySnapshot,LeadChangeC,QuitC,Created,Stop,WaitForStop,Delete,IsDeleted,RecreateInternalSubs,IsSystemAccount,GetTrafficAccountName,GetWriteErr - Purpose: Defines the full operational contract for a NATS Raft Group (NRG) node. Covers proposal submission, snapshot management, cluster membership, health queries, leadership control, and lifecycle management. All JetStream clustering code references this interface.
- Implementations:
raftstruct (server/raft.go), single implementation — the interface exists to allow clean testing via mock substitution and to provide a stable boundary betweenjetstream_cluster.goand the Raft engine. - Design quality: Extremely wide (53 methods) — this is a deliberate “god interface” for the Raft node. The breadth is justified by the complexity of Raft operations (consensus + membership + snapshot + observability), but it violates ISP and makes test doubles expensive to write. The 2026 addition of
RaftNodeCheckpoint(a separate 4-method interface) shows the team has begun decomposing it.
RaftNodeCheckpoint#
- Package:
server - File:
server/raft.go:99 - Methods:
LoadLastSnapshot() (snap []byte, err error),AppendEntriesSeq() iter.Seq2[*appendEntry, error],Abort(),InstallSnapshot(data []byte) (uint64, error) - Purpose: Supports asynchronous snapshot installation — a checkpoint is created from
RaftNode.CreateSnapshotCheckpointand allows installing snapshots without blocking the main Raft loop. Uses Go 1.23’siter.Seq2for lazy log iteration. - Implementations:
raftNodeCheckpointstruct (internal) - Design quality: Well-segregated single-responsibility interface extracted from the original
RaftNode. Good use ofiter.Seq2for streaming entries without materializing them.
WAL#
- Package:
server - File:
server/raft.go:106 - Methods (11):
Type() StorageType,StoreMsg(subj, hdr, msg []byte, ttl int64) (uint64, int64, error),LoadMsg(index uint64, sm *StoreMsg) (*StoreMsg, error),RemoveMsg(index uint64) (bool, error),Compact(index uint64) (uint64, error),Purge() (uint64, error),PurgeEx(subject, seq, keep) (uint64, error),Truncate(seq uint64) error,State() StreamState,FastState(*StreamState),Stop() error,Delete(inline bool) error - Purpose: Abstracts the write-ahead log used by Raft for log persistence. The WAL interface is a strict subset of
StreamStore— it intentionally reuses the stream message storage semantics for Raft log entries (each log entry is stored as a NATS message). - Implementations:
fileStoreandmemStore— the same storage backends used for JetStream streams. This dual use (JetStream storage and Raft WAL) is a key architectural decision. - Design quality: Well-sized (12 methods), focused, and a natural subset of
StreamStore. The reuse of stream storage for Raft log is architecturally elegant.
StreamStore#
- Package:
server - File:
server/store.go:93 - Methods (44):
StoreMsg,StoreRawMsg,SkipMsg,SkipMsgs,FlushAllPending,LoadMsg,LoadNextMsg,LoadNextMsgMulti,LoadLastMsg,LoadPrevMsg,LoadPrevMsgMulti,RemoveMsg,EraseMsg,Purge,PurgeEx,Compact,Truncate,GetSeqFromTime,FilteredState,SubjectsState,SubjectsTotals,AllLastSeqs,MultiLastSeqs,SubjectForSeq,NumPending,NumPendingMulti,State,FastState,EncodedStreamState,SyncDeleted,Type,RegisterStorageUpdates,RegisterStorageRemoveMsg,RegisterProcessJetStreamMsg,UpdateConfig,Delete,Stop,ConsumerStore,AddConsumer,RemoveConsumer,Snapshot,Utilization,ResetState - Purpose: The primary storage abstraction for JetStream streams. Covers the full message lifecycle: write, indexed load (by seq, subject filter, prev/next), compaction, snapshotting, state reporting, and consumer management. A stream’s storage backend is swapped via this interface at creation time.
- Implementations:
fileStore(WAL + per-message index on disk) andmemStore(in-memory circular buffer). Both are in theserverpackage. - Design quality: Very wide (44 methods) — the richest interface in the codebase. The breadth is driven by JetStream’s query semantics (subject filtering, per-subject state, multi-filter pending counts) which require a rich storage API. Not segregatable without breaking callers; this represents the full “storage port” of the JetStream layer.
FastState(*StreamState)as a performance variant ofState() StreamStateshows performance-conscious design.
ConsumerStore#
- Package:
server - File:
server/store.go:360 - Methods (13):
SetStarting(sseq uint64) error,UpdateStarting(sseq uint64),Reset(sseq uint64) error,HasState() bool,UpdateDelivered(dseq, sseq, dc uint64, ts int64) error,UpdateAcks(dseq, sseq uint64) error,UpdateConfig(cfg *ConsumerConfig) error,Update(*ConsumerState) error,ForceUpdate(*ConsumerState) error,State() (*ConsumerState, error),BorrowState() (*ConsumerState, error),EncodedState() ([]byte, error),Type() StorageType,Stop() error,Delete() error,StreamDelete() error - Purpose: Stores and retrieves per-consumer delivery progress — ack floor, pending messages, redelivery counts.
BorrowState()returns a zero-copy view to avoid allocation on the delivery hot path. - Implementations:
consumerFileStoreandconsumerMemStorewithinfilestore.goandmemstore.go. - Design quality: Well-sized (13–16 methods).
BorrowState()vsState()reveals the performance-conscious style: two methods for the same conceptual query, differentiated by allocation semantics.
AccountResolver#
- Package:
server - File:
server/accounts.go:4045 - Methods (7):
Fetch(name string) (string, error),Store(name, jwt string) error,IsReadOnly() bool,Start(server *Server) error,IsTrackingUpdate() bool,Reload() error,Close() - Purpose: Resolves NATS account public NKeys to their JWT claims. The JWT contains the account’s permissions, stream limits, import/export mappings, and signing keys. This is how decentralized multi-tenancy is managed — the server doesn’t statically configure accounts; it resolves them on demand.
- Implementations:
MemAccResolver(test/embedded use),URLAccResolver(HTTP fetch),DirAccResolver(local directory of JWT files with inotify-style update tracking). AresolverDefaultsOpsImplembedded struct provides no-op defaults, so implementors only override what they need. - Design quality: Well-designed (7 methods). The
Start(*Server)method injects the server for implementations that need to subscribe to push updates.IsReadOnly/IsTrackingUpdateare discriminator methods that let the server adapt its behavior without type assertions — a pragmatic alternative to a richer interface hierarchy.
Authentication#
- Package:
server - File:
server/auth.go:40 - Methods (1):
Check(c ClientAuthentication) bool - Purpose: The external authentication plugin interface. Allows third parties embedding the NATS server to inject a custom auth handler. The single
Checkmethod receives aClientAuthenticationview of the connecting client and returns allow/deny. - Implementations:
defaultAuthImpl(internal struct using NKey/JWT/bcrypt logic inauth.go). External consumers implement this for custom auth (LDAP, custom token, etc.). - Design quality: Exemplary ISP compliance — a single-method interface following the Go convention. The companion
ClientAuthenticationinterface provides the read-only view the auth handler needs.
ClientAuthentication#
- Package:
server - File:
server/auth.go:46 - Methods (6):
GetOpts() *ClientOpts,GetTLSConnectionState() *tls.ConnectionState,RegisterUser(*User),RemoteAddress() net.Addr,GetNonce() []byte,Kind() int - Purpose: The read-side contract that an
Authenticationimplementor sees when inspecting a connecting client. Decouples the auth handler from the fullclientstruct — the auth handler doesn’t need to know about pub-sub internals. - Implementations:
*clientstruct (which is the actual connecting client) - Design quality: Good interface segregation. The
Authentication/ClientAuthenticationpair is a textbook consumer-role interface split: the auth plugin sees only what it needs to decide on a connection.
Logger#
- Package:
server - File:
server/log.go:27 - Methods (6):
Noticef(format string, v ...any),Warnf(format string, v ...any),Fatalf(format string, v ...any),Errorf(format string, v ...any),Debugf(format string, v ...any),Tracef(format string, v ...any) - Purpose: The server’s logging abstraction. Enables embedding the NATS server with a custom logger (e.g., zap, zerolog wrapper) without coupling to the default
srvlog.Logger. The server checksio.Closerdynamically when replacing a logger to close the previous one. - Implementations:
*srvlog.Logger(file, stdout, syslog, Windows event log backends inlogger/package). Test code uses test-specific implementations. - Design quality: Good. Six clearly named severity levels. Printf-style variadic signatures match Go logger conventions. The dynamic
io.Closercheck (rather than embedding it in the interface) keeps the interface minimal.
SubjectTransformer#
- Package:
server - File:
server/subject_transform.go:74 - Methods (3):
Match(string) (string, error),TransformSubject(subject string) string,TransformTokenizedSubject(tokens []string) string - Purpose: Abstracts subject mapping transformations used in account import/export rules. A mapping rule (e.g.,
foo.* → bar.$1) is represented as aSubjectTransformerthat rewrites subjects on message delivery. - Implementations:
*subjectTransformstruct - Design quality: Minimal and focused. The
TransformTokenizedSubjectmethod accepts pre-tokenized subjects (already split on.) to avoid re-tokenizing on hot paths — performance-conscious design.
DeleteBlock#
- Package:
server - File:
server/store.go:220 - Methods (2):
State() (first, last, num uint64),Range(f func(uint64) bool) - Purpose: Abstracts three different encodings of deleted-sequence sets used in stream state replication: AVL seqsets (
avl.Dmap), run-length encoded ranges (DeleteRange), and legacy[]uint64slices. Allows the replication protocol to iterate deleted sequences without knowing the encoding format. - Implementations:
*avl.Dmap,*DeleteRange,DeleteSlice - Design quality: Clean 2-method interface following the Go “do one thing” convention.
Rangeuses a visitor/callback pattern (rather than returning a channel or iterator) to minimize allocation — though Go 1.23iter.Seqwould be a modern equivalent.
option (internal — reload visitor)#
- Package:
server - File:
server/reload.go:43 - Methods (6):
Apply(server *Server),IsLoggingChange() bool,IsTraceLevelChange() bool,IsAuthChange() bool,IsTLSChange() bool,IsClusterPermsChange() bool,IsClusterPoolSizeOrAccountsChange() bool - Purpose: Internal visitor interface for hot-reload configuration diffing. Each reloadable
Optionsfield is wrapped as anoptionimplementor. The reload machinery collects the set of changed options, queries these discriminator methods, and applies in the right order. - Implementations: ~30 unexported structs, one per reloadable field (
debugOption,tlsOption,clusterPortOption, etc.) - Design quality: Serviceable for its narrow purpose. The “is-X-change” discriminators are a code smell (open/closed violation — every new restart category needs a new method), but the pattern is simple and the file is well-contained.
Interface patterns#
Size distribution: Strongly bimodal. Most interfaces are small (1–7 methods:
Authentication,AccountResolver,Logger,DeleteBlock,SubjectTransformer,WAL). Two are very large:RaftNode(53 methods) andStreamStore(44 methods). The large interfaces represent complete “system ports” where the entire capability of a subsystem must be surfaced.Embedding: Minimal.
WALshares method signatures withStreamStorebut does not embed it — the WAL is intentionally a subset.RaftNodeCheckpointwas split out ofRaftNodewithout embedding. TheresolverDefaultsOpsImplstruct provides default method implementations for embedding intoAccountResolverimplementors (a Go idiom for partial implementation of interfaces).Implicit satisfaction: Mixed. Most interfaces are provider-defined (defined in the same file as their implementations):
StreamStore,ConsumerStore,RaftNode,WAL,Logger. Two are consumer-defined (defined where they are used, not where implemented):Authentication(defined inauth.go, implemented by callers embedding the server) andClientAuthentication(defined inauth.go, implemented by*client).Stdlib interfaces used:
io.ReadCloserinSnapshotResult.Reader;io.Closerdynamically checked inSetLoggerV2when swapping loggers (allowing Logger implementors to optionally close resources). No use offmt.Stringerorsort.Interfacein the architectural core. The newiter.Seq2(Go 1.23 iterators) appears inRaftNodeCheckpoint.AppendEntriesSeq, indicating the codebase tracks modern Go idioms.
Key abstractions#
StreamStore+ConsumerStore— The JetStream storage layer’s primary extensibility seam. By defining these interfaces at the boundary between the stream/consumer logic and the file/memory backends, NATS separates “what to store” from “how to store it.” These are the only interfaces where behavioral substitution at runtime is a documented feature (memory streams for ephemeral use, file streams for durability).RaftNode— The distributed consensus abstraction that insulatesjetstream_cluster.go(~10,900 lines) from the 5,100-line Raft implementation. Despite being excessively wide, it enables testing of the entire JetStream clustering layer with a mock Raft node and is the clearest architectural boundary in the codebase.WAL— Architecturally significant because it embodies the key decision to reuse stream storage as Raft log storage.fileStoreandmemStoreimplement bothStreamStoreandWAL, enabling a single storage engine to serve two different consumers.AccountResolver— The extensibility point for NATS’s decentralized auth model. Without this interface, the entire JWT-based multi-tenancy system would be hardcoded to a single resolution strategy. The three implementations (memory, URL, directory) cover test, cloud, and on-prem deployment scenarios respectively.Authentication/ClientAuthentication— A textbook interface pair for auth plugin extensibility.Authenticationis a single-method “check” interface;ClientAuthenticationis a read-only projection of the connecting client. This pair allows NATS Server to be embedded in applications with fully custom authentication backends without any internal coupling.
Interface-driven extensibility#
NATS Server uses interfaces for extensibility in exactly four areas:
Storage backends (
StreamStore,ConsumerStore,WAL): The file vs. memory storage decision is made at stream creation time and entirely abstracted by these interfaces. A hypothetical third backend (e.g., object storage, remote storage) would be added by implementing all three.Account resolution (
AccountResolver): The server’s multi-tenancy system is pluggable at the JWT resolution layer. Operators can implement a custom resolver for integration with identity providers, secret managers, or internal auth systems.Authentication (
Authentication): NATS Server is designed to be embeddable. The single-methodAuthenticationinterface is the primary hook: applications embedding the server can inject their own auth logic without forking.Logging (
Logger): Similarly enables embedding — the host application can inject its own structured logger while the NATS Server’s internal logging code remains unchanged.
Notably absent from this list: message routing (no Router interface — Sublist is not abstracted), protocol handling (no ProtocolHandler interface — the client struct is hardcoded for all protocol types), and clustering transport (Raft uses NATS pub-sub directly, not an abstract transport). These omissions are intentional: the hot path must avoid interface dispatch overhead, so only subsystems where runtime substitution is genuinely needed get an interface.