zapclient

package
v1.2.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2026 License: BSD-3-Clause Imports: 12 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoPeers is returned when discovery has no live peer for the
	// requested service type.
	ErrNoPeers = errors.New("zapclient: no peers discovered")

	// ErrUnknownProcedure is returned by the server when an incoming
	// opcode does not match any Register call.
	ErrUnknownProcedure = errors.New("zapclient: unknown procedure")

	// ErrPeerUnauthorized is returned when PeerVerifier refuses the
	// incoming peer. Maps to a transport-layer reject.
	ErrPeerUnauthorized = errors.New("zapclient: peer unauthorized")

	// ErrCallTimeout is returned by Call when the per-call deadline
	// elapses before the response arrives.
	ErrCallTimeout = errors.New("zapclient: call timed out")
)

Sentinel errors. Callers branch via errors.Is.

View Source
var ErrReservedOpcode = errors.New("zapclient: procedure name hashes to a reserved opcode")

ErrReservedOpcode is returned when ProcedureOpcode collides with a reserved value. The procedure must be re-named to avoid the conflict.

Functions

func MustOpcode

func MustOpcode(name string) uint16

MustOpcode is ProcedureOpcode that panics on error. Use in package-level var initialisers where a bad procedure name is a build-time bug.

func ProcedureOpcode

func ProcedureOpcode(name string) (uint16, error)

ProcedureOpcode returns the uint16 opcode for a procedure name. The lower 8 bits are zero (matching the existing MsgType<<8 shape in lux/zap); the upper 8 bits are FNV-1a(name) modulo 254 + 1.

Returns ErrReservedOpcode if the hash lands on 0x00 or 0xFF.

Types

type AllowListVerifier

type AllowListVerifier struct {
	// Allowed maps procedure name → set of NodeIDs permitted to call
	// it. Empty set = procedure disabled.
	Allowed map[string]map[string]struct{}
}

AllowListVerifier accepts only peers whose NodeID appears in Allowed[procedure]. Empty Allowed[procedure] means the procedure is not exposed.

func (AllowListVerifier) Verify

func (a AllowListVerifier) Verify(ctx context.Context, peer PeerInfo, procedure string) error

Verify accepts if NodeID is in Allowed[procedure].

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a native ZAP RPC client. Created by Connect; closed by Close (or returned stop function from MustConnect).

func Connect

func Connect(ctx context.Context, serviceType string, opts ...ClientOption) (*Client, error)

Connect resolves a service via Discovery and returns a Client. Always defer Close on the returned client.

func MustConnect

func MustConnect(ctx context.Context, serviceType string, opts ...ClientOption) *Client

MustConnect is Connect that panics on error.

func (*Client) Broadcast

func (c *Client) Broadcast(ctx context.Context, procedure string, req *zap.Message) map[string]error

Broadcast sends procedure to every current peer. Returns a map of peer NodeID → error so the caller can re-try the failures.

func (*Client) Call

func (c *Client) Call(ctx context.Context, procedure string, req *zap.Message) (*zap.Message, error)

Call invokes procedure on the picked peer and waits for a response. Respects the client-level CallTimeout in addition to ctx.

func (*Client) Close

func (c *Client) Close() error

Close releases the underlying ZAP node + discovery.

func (*Client) Peers

func (c *Client) Peers() []Peer

Peers returns the current peer snapshot.

func (*Client) Send

func (c *Client) Send(ctx context.Context, procedure string, req *zap.Message) error

Send invokes procedure as fire-and-forget — no response awaited.

type ClientOption

type ClientOption func(*ClientOptions)

ClientOption is the functional-option constructor knob.

func WithBrowseInterval

func WithBrowseInterval(d time.Duration) ClientOption

WithBrowseInterval sets the default mDNS browse interval.

func WithCallTimeout

func WithCallTimeout(d time.Duration) ClientOption

WithCallTimeout caps each Call's wait for a response.

func WithDiscoverTimeout

func WithDiscoverTimeout(d time.Duration) ClientOption

WithDiscoverTimeout caps the initial peer-discovery wait.

func WithDiscovery

func WithDiscovery(d Discovery) ClientOption

WithDiscovery overrides the peer-discovery backend (mDNS by default). Useful for tests + non-mDNS environments (Consul, etcd, static).

func WithLogger

func WithLogger(l *slog.Logger) ClientOption

WithLogger sets the structured logger.

func WithMinPeers

func WithMinPeers(n int) ClientOption

WithMinPeers blocks Connect until N peers are discovered.

func WithNodeID

func WithNodeID(id string) ClientOption

WithNodeID names the caller in mDNS announcements.

func WithPicker

func WithPicker(p Picker) ClientOption

WithPicker overrides the per-Call peer selector.

func WithStaticPeers added in v0.8.6

func WithStaticPeers(addrs ...string) ClientOption

WithStaticPeers sets explicit "host:port" peer addresses, bypassing mDNS. The cloud/K8s path: pass Service DNS names (e.g. "ta.ns.svc.cluster.local:7801"). An explicit WithDiscovery wins over this.

func WithTLS

func WithTLS(cfg *tls.Config) ClientOption

WithTLS configures mutual TLS for the underlying ZAP connection. Required for the local-trust authorisation model on the server side (the server's PeerVerifier reads the peer cert).

type ClientOptions

type ClientOptions struct {
	// NodeID is the caller's identity in mDNS announcements. Empty
	// auto-generates a short suffix.
	NodeID string
	// MinPeers blocks Connect until at least N peers are discovered
	// (or DiscoverTimeout elapses). Default 1.
	MinPeers int
	// DiscoverTimeout caps the initial peer-discovery wait. Default
	// 10 * BrowseInterval.
	DiscoverTimeout time.Duration
	// BrowseInterval is how often the default mDNS Discovery
	// re-browses. Default 5 seconds.
	BrowseInterval time.Duration
	// CallTimeout caps each Call. Zero = no timeout. Default 30s.
	CallTimeout time.Duration
	// TLS configures mutual TLS. Nil = plaintext (dev only — local-
	// trust mode requires mTLS against the cluster CA).
	TLS *tls.Config
	// Logger for ZAP node + client. Defaults to slog.Default().
	Logger *slog.Logger

	// Pluggable wiring. nil means "use default for this concern."
	Discovery Discovery
	Picker    Picker

	// StaticPeers is an explicit "host:port" peer set, bypassing mDNS.
	// The cloud/K8s path — pass the Service DNS name(s). When set (and
	// Discovery is nil) Connect builds a static Discovery over them.
	StaticPeers []string
}

ClientOptions configure Connect. Construct via WithX options.

type Discovery

type Discovery interface {
	// Peers returns the current peer snapshot. The returned slice is
	// freshly allocated; callers may retain it.
	Peers() []Peer

	// PeerCount returns len(Peers()) cheaply.
	PeerCount() int

	// ServiceType reports the service the Discovery is browsing.
	ServiceType() string

	// Start begins the discovery loop. Calling Start twice on the
	// same Discovery is a programmer error.
	Start() error

	// Stop releases all resources. Idempotent.
	Stop()
}

Discovery is the peer-enumeration contract.

Implementations MUST be goroutine-safe; the client may call Peers() from every in-flight Call and Send.

type LocalTrustVerifier

type LocalTrustVerifier struct{}

LocalTrustVerifier accepts any peer presenting a valid mTLS cert from the cluster CA. The underlying TLS handshake already verified the chain; this verifier returns nil for every authenticated peer.

Use when:

  • the peer is on the cluster-private network
  • the listener was constructed with the cluster CA in ClientCAs
  • all in-cluster services should be allowed to call all procedures (apply per-procedure RBAC in the handler if needed)

func (LocalTrustVerifier) Verify

func (LocalTrustVerifier) Verify(ctx context.Context, peer PeerInfo, procedure string) error

Verify accepts any authenticated peer.

type Peer

type Peer struct {
	NodeID      string
	ServiceType string
	Address     string
	Metadata    map[string]string
	LastSeen    time.Time
}

Peer is the discovery view of one reachable instance of a service.

NodeID uniquely identifies the peer within ServiceType across discovery cycles. Address is the dial target (host:port). For in-cluster peers under mTLS, the authenticated peer identity comes from the TLS session — NodeID is a hint, not a trust anchor.

type PeerInfo

type PeerInfo struct {
	NodeID      string
	ServiceType string
	// TLSCertSubject is the Subject CN of the mTLS peer cert, when
	// the listener was configured with TLS. Empty under plaintext
	// (local-dev) transport.
	TLSCertSubject string
	// TLSCertSANs are the Subject Alternative Names from the peer
	// cert. Authorisation should prefer SANs over the legacy CN.
	TLSCertSANs []string
}

PeerInfo is the authenticated peer identity surfaced to server-side handlers and PeerVerifier.

type PeerVerifier

type PeerVerifier interface {
	// Verify returns nil on accept, ErrPeerUnauthorized (or wrapping
	// error) on reject. Implementations MUST NOT block — refer to
	// pre-built decision tables, not network calls.
	Verify(ctx context.Context, peer PeerInfo, procedure string) error
}

PeerVerifier is the server-side authorisation hook. Runs on every inbound message before procedure dispatch — return non-nil to reject the call.

The PeerInfo carries the authenticated peer identity (NodeID + mTLS cert chain if present). Procedure is the procedure name the caller requested. PeerVerifier should compare the (peer, procedure) pair against the service's authorisation policy.

type Picker

type Picker interface {
	// Pick returns the chosen peer. ErrNoPeers if peers is empty.
	Pick(peers []Peer) (Peer, error)
}

Picker selects one peer from a Discovery snapshot for a single Call or Send. Implementations MUST be cheap (sub-microsecond) and goroutine-safe; the client invokes Pick from every concurrent op.

type ProcedureHandler

type ProcedureHandler func(ctx context.Context, peer PeerInfo, req *zap.Message) (*zap.Message, error)

ProcedureHandler runs server-side for one inbound procedure call. Return nil + nil-err to ack a fire-and-forget Send. Return a response + nil-err to reply to a Call. Return nil + non-nil err to signal failure; the client side observes the err on its Call.

type RoundRobinPicker

type RoundRobinPicker struct {
	// contains filtered or unexported fields
}

RoundRobinPicker rotates through peers in registration order. Zero-value is ready to use; goroutine-safe.

func (*RoundRobinPicker) Pick

func (p *RoundRobinPicker) Pick(peers []Peer) (Peer, error)

Pick returns the next peer via round-robin.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is the native ZAP server side: procedure-name dispatch on top of *zap.Node. Construct with NewServer, Register procedures, Start to begin accepting, Stop to release.

func NewServer

func NewServer(serviceType string, opts ...ServerOption) (*Server, error)

NewServer constructs a server for the given service type + port. The returned Server is not yet listening — call Start.

func (*Server) Dispatch added in v0.8.3

func (s *Server) Dispatch(ctx context.Context, from string, msg *zap.Message) (*zap.Message, error)

Dispatch routes one inbound message to its registered procedure handler, running the PeerVerifier first. It is the transport-agnostic entry point: the native ZAP node reaches it via Handle, and alternate transports — e.g. zapweb over WebSocket for browser clients — call it directly. `from` is the authenticated peer identity passed to the verifier and PeerInfo. Returns the handler's response, or an error for unknown-procedure / verifier-reject / handler failure.

func (*Server) NodeID

func (s *Server) NodeID() string

NodeID returns the server's NodeID — useful for tests + ops.

func (*Server) Register

func (s *Server) Register(procedure string, h ProcedureHandler) error

Register binds a procedure name to a handler.

Refuses to register if the procedure's opcode collides with an already-registered procedure — the caller MUST rename. With the 8-bit codespace, occasional collisions are expected; the build fails clearly rather than silently routing to the wrong handler.

func (*Server) Start

func (s *Server) Start() error

Start binds the listener and begins accepting. Call Stop on shutdown.

func (*Server) Stop

func (s *Server) Stop()

Stop releases the underlying node + listener. Idempotent.

type ServerOption

type ServerOption func(*ServerOptions)

ServerOption is a functional-option knob.

func WithNoDiscovery

func WithNoDiscovery() ServerOption

WithNoDiscovery disables mDNS advertisement (test-only).

func WithServerLogger

func WithServerLogger(l *slog.Logger) ServerOption

WithServerLogger sets the structured logger.

func WithServerMetadata

func WithServerMetadata(m map[string]string) ServerOption

WithServerMetadata sets the mDNS TXT-record metadata.

func WithServerNodeID

func WithServerNodeID(id string) ServerOption

WithServerNodeID names this server in mDNS announcements.

func WithServerPort added in v0.8.2

func WithServerPort(p int) ServerOption

WithServerPort sets the fixed TCP port the server listens on. Use this whenever peers reach the server by a stable address — Kubernetes Service DNS, a docker-compose service name, or localhost — rather than by mDNS discovery. Port 0 (the default) binds an ephemeral port, which is only reachable via mDNS; mDNS multicast is not forwarded across most Kubernetes pod overlays, so a fixed port + WithNoDiscovery is the correct production configuration.

func WithServerTLS

func WithServerTLS(cfg *tls.Config) ServerOption

WithServerTLS configures mutual TLS on the listener.

func WithVerifier

func WithVerifier(v PeerVerifier) ServerOption

WithVerifier installs the server-side PeerVerifier. Default is LocalTrustVerifier, which accepts any authenticated peer. Set to AllowListVerifier or a custom impl for stricter policy.

type ServerOptions

type ServerOptions struct {
	NodeID   string
	Port     int
	TLS      *tls.Config
	Verifier PeerVerifier
	Logger   *slog.Logger
	// Metadata is the mDNS TXT-record metadata advertised to peers.
	Metadata map[string]string
	// NoDiscovery disables mDNS advertisement entirely. Use for
	// stdio / unix-socket tests.
	NoDiscovery bool
}

ServerOptions configure NewServer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL