Documentation
¶
Overview ¶
Package interceptor provides the gRPC unary server interceptor chain that aligns the gRPC transport with the existing HTTP middleware stack (runtime/http/middleware): RequestID, Tracing, Metrics, Auth, and Recovery.
The interceptors are composed by NewUnaryChain into a single grpc.ServerOption. bootstrap (a later PR) installs that option on the adapters/grpc server; this package owns no server lifecycle.
Chain order ¶
NewUnaryChain composes the interceptors in this fixed order (outermost → innermost), where the first argument to grpc.ChainUnaryInterceptor is the outermost wrapper closest to the transport:
RequestID → Tracing → Metrics → Auth → Recovery → handler
Recovery is INNERMOST, not outermost. This deliberately diverges from the HTTP middleware order (where Recovery sits inside Tracing/Metrics but its placement is driven by the HTTP ResponseWriter commit-ordering constraint — Recovery must own the writer to emit a 500 body). gRPC has no such constraint: a status code is a return value, so a panic recovered by the innermost interceptor and converted to codes.Internal propagates outward as a normal (resp, err) return that the outer Metrics and Tracing interceptors observe accurately. Placing Recovery innermost is the grpc-ecosystem go-grpc-middleware consensus, which documents recovery-last so metrics and logging see the converted status.
ref: grpc-ecosystem/go-grpc-middleware interceptors/recovery/interceptors.go ref: go-kratos/kratos transport/grpc/interceptor.go
Index ¶
- func NewUnaryChain(deps Deps) grpc.ServerOption
- func UnaryAuth(verifier auth.IntentTokenVerifier, opts ...AuthOption) grpc.UnaryServerInterceptor
- func UnaryMetrics(collector metrics.GRPCCollector, clk clock.Clock) grpc.UnaryServerInterceptor
- func UnaryRecovery() grpc.UnaryServerInterceptor
- func UnaryRequestID() grpc.UnaryServerInterceptor
- func UnaryTracing(tracer wrapper.Tracer) grpc.UnaryServerInterceptor
- type AuthOption
- type Deps
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewUnaryChain ¶
func NewUnaryChain(deps Deps) grpc.ServerOption
NewUnaryChain composes the unary interceptors into a single grpc.ServerOption. This is the single authoritative composition point; the order is fixed here (RequestID outermost, Recovery innermost) and guarded by archtest GRPC-INTERCEPTOR-CHAIN-ORDER-01. See the package doc for the order rationale.
func UnaryAuth ¶
func UnaryAuth(verifier auth.IntentTokenVerifier, opts ...AuthOption) grpc.UnaryServerInterceptor
UnaryAuth returns an interceptor that extracts a bearer token from the incoming metadata, verifies it via runtime/auth.AuthenticateBearer (the shared transport-agnostic core), applies the password-reset gate, and on success forwards the principal-enriched context to the handler. Failures map directly to gRPC status codes (Unauthenticated / Unavailable / PermissionDenied), mirroring the HTTP handleAuthRequest classification.
The verifier is required: a nil verifier is a wiring bug that fails fast at construction (programmer-error panic). For a security interceptor this is correct — the server must refuse to start rather than boot and reject every request, which would be indistinguishable from an outage.
func UnaryMetrics ¶
func UnaryMetrics(collector metrics.GRPCCollector, clk clock.Clock) grpc.UnaryServerInterceptor
UnaryMetrics returns an interceptor that records grpc_server_requests_total and grpc_server_request_duration_seconds via the given collector. The metric labels are: method (info.FullMethod), code (the gRPC status code name derived from the handler's returned error), and cell. The cell label is resolved through the sealed metrics.ResolveCellLabel funnel — passed a nil closed set because gRPC cell attribution is not yet wired (#1383), so the funnel always degrades to metrics.RuntimeCellSentinel ("_runtime") for now. When attribution lands, the nil is replaced by the assembly closed set and the cell label reflects the owning cell with no other change. This mirrors runtime/http/middleware.Metrics.
collector and clk are required: a nil collector or clock is a wiring bug that fails fast at construction (programmer-error panic, like MustHaveClock) rather than nil-dereferencing on the first RPC where UnaryRecovery would mask it as a generic codes.Internal.
func UnaryRecovery ¶
func UnaryRecovery() grpc.UnaryServerInterceptor
UnaryRecovery returns the innermost interceptor: it recovers panics raised by the handler, logs the redacted panic value plus stack via slog.Error, and converts the panic into a codes.Internal status returned to the client. It does NOT re-panic — the panic is collapsed into a return value so the outer Metrics and Tracing interceptors observe a clean codes.Internal. This mirrors runtime/http/middleware.Recovery (and the go-grpc-middleware / Kratos recovery convention); because it never calls panic() itself it is outside the PANIC-REGISTERED-01 funnel.
func UnaryRequestID ¶
func UnaryRequestID() grpc.UnaryServerInterceptor
UnaryRequestID returns the outermost interceptor: it derives a request id (reused from incoming metadata when valid, otherwise freshly generated) and stores it under both ctxkeys.RequestID and ctxkeys.CorrelationID so that the downstream Tracing / Metrics / Recovery interceptors and any errcode emitted by the handler carry a stable correlation id. It mirrors runtime/http/middleware.RequestID; the ctxkeys helpers are shared and carry no funnel restriction.
func UnaryTracing ¶
func UnaryTracing(tracer wrapper.Tracer) grpc.UnaryServerInterceptor
UnaryTracing returns an interceptor that starts a wrapper.Span per RPC named by the full method (leading slash stripped), records rpc.* attributes, and on error records the error and sets the span status. Error redaction is owned by the otelSpan sink (SPAN-RECORD-ERROR-SEAL-01) — call sites pass the raw error. Span attributes go through wrapper.Attr (not raw attribute.String), so the adapters/otel safeStringAttr redaction funnel applies automatically — this interceptor does not touch the SPAN-SETATTR-REDACT-01 callsite set. It mirrors runtime/http/middleware.Tracing. A nil tracer degrades to NoopTracer.
Types ¶
type AuthOption ¶
type AuthOption func(*authConfig)
AuthOption configures the auth interceptor.
func WithPasswordResetExempt ¶
func WithPasswordResetExempt(pred func(fullMethod string) bool) AuthOption
WithPasswordResetExempt installs a predicate marking RPC methods exempt from the password-reset gate (the gRPC analog of the HTTP route-based exempt matcher). The default (nil predicate) is fail-closed — a reset-required token is rejected on every method. Passing a nil predicate is a no-op; any previously installed predicate is retained.
func WithPublicMethod ¶
func WithPublicMethod(pred func(fullMethod string) bool) AuthOption
WithPublicMethod installs a predicate marking RPC methods that bypass authentication (e.g. health checks). The wiring of concrete public-method sets is a later-PR concern (tracked in backlog); the default (nil predicate) is fail-closed — every method requires authentication. Passing a nil predicate is a no-op; any previously installed predicate is retained.
type Deps ¶
type Deps struct {
// Tracer records one span per RPC. A nil Tracer degrades to NoopTracer.
Tracer wrapper.Tracer
// Collector records grpc_server_* metrics.
Collector metrics.GRPCCollector
// Clock times request duration for the metrics interceptor (required).
Clock clock.Clock
// Verifier authenticates bearer tokens (required; nil fails closed).
Verifier auth.IntentTokenVerifier
// AuthOptions configures the auth interceptor (public-method and
// password-reset-exempt predicates).
AuthOptions []AuthOption
}
Deps holds the dependencies the unary interceptor chain needs. They are supplied by the composition root (bootstrap, a later PR); this package only composes them.