agentsclimarketplace

Go grpc

Skill muratmirgun/gophers/skills/go-grpc

26 production-grade Go skills for Claude Code, Gemini CLI, and opencode.

Install
npx -y skills add muratmirgun/gophers --skill go-grpc

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when implementing or reviewing gRPC servers/clients in Go. Covers .proto organisation, code generation with protoc/buf, server bootstrap (interceptors, health, graceful shutdown), client patterns (reuse, deadlines, retries), status.Code error handling, streaming, TLS/mTLS, and bufconn testing. Apply when writing .proto files, adding interceptors, or auditing a service for production readiness.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.7 KB, as published. Nobody here has run it

Go gRPC

Treat gRPC as a transport. Keep .proto-generated code and business logic separated. The official Go implementation is google.golang.org/grpc; pair it with protoc-gen-go + protoc-gen-go-grpc (or buf generate).

Core Rules

  1. One concern per layer. .proto defines the contract; generated code lives in gen/; service implementation lives in internal/. Never edit generated files.
  2. Always wrap RPC arguments in Request/Response messages. Bare scalars (string, int32) cannot be evolved without breaking callers.
  3. Return typed status codes, never raw errors. A fmt.Errorf becomes codes.Unknown on the wire — the client cannot decide whether to retry.
  4. Every client call has a deadline. No context.Background() to a remote service. Set context.WithTimeout per call.
  5. Reuse connections. HTTP/2 multiplexes; creating a new grpc.ClientConn per request is a TLS handshake leak.
  6. Disable reflection in production. Reflection is a developer convenience that doubles as an API enumeration tool for attackers.

When to Use What

NeedUse
Define service.proto file in proto/<service>/v1/
Generate stubsbuf generate or protoc --go_out --go-grpc_out
Cross-cutting (auth, logging, recovery)grpc.ChainUnaryInterceptor / ChainStreamInterceptor
Health probes (Kubernetes)grpc_health_v1 from google.golang.org/grpc/health
Errors with detailsstatus.Errorf(codes.X, ...) + WithDetails(errdetails.BadRequest{...})
Testsgoogle.golang.org/grpc/test/bufconn
Service mesh / mTLScredentials.NewTLS or delegate to Istio/Linkerd

Read references/proto-and-codegen.md when organizing .proto packages or wiring buf. Read references/status-and-errors.md when mapping domain errors to gRPC codes.

Server Bootstrap

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/health"
    healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

srv := grpc.NewServer(
    grpc.ChainUnaryInterceptor(recoveryUnary, loggingUnary, authUnary),
    grpc.ChainStreamInterceptor(recoveryStream, loggingStream),
)
pb.RegisterUserServiceServer(srv, &userService{...})
healthpb.RegisterHealthServer(srv, health.NewServer())

go func() { _ = srv.Serve(lis) }()

// Graceful shutdown bounded by a hard timeout.
<-shutdownSignal
stopped := make(chan struct{})
go func() { srv.GracefulStop(); close(stopped) }()
select {
case <-stopped:
case <-time.After(15 * time.Second):
    srv.Stop()
}

Three pieces are non-negotiable: interceptors for cross-cutting concerns, health service for Kubernetes probes, and a bounded graceful shutdown.

Client Bootstrap

conn, _ := grpc.NewClient("dns:///user-service:50051",
    grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg)),
    grpc.WithDefaultServiceConfig(`{
      "loadBalancingPolicy": "round_robin",
      "methodConfig": [{
        "name": [{"service": "user.v1.UserService"}],
        "timeout": "5s",
        "retryPolicy": {
          "maxAttempts": 3, "initialBackoff": "0.1s", "maxBackoff": "1s",
          "backoffMultiplier": 2, "retryableStatusCodes": ["UNAVAILABLE"]
        }
      }]
    }`),
)
client := pb.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second); defer cancel()
resp, err := client.GetUser(ctx, &pb.GetUserRequest{Id: id})

The service config is the right place for retries — let the library handle the loop, backoff, and UNAVAILABLE-only filter.

Errors

A raw Go error returned from an RPC becomes codes.Unknown. The client cannot tell a 404 from a 500. Always use status.Errorf:

if errors.Is(err, ErrNotFound) {
    return nil, status.Errorf(codes.NotFound, "user %q not found", req.Id)
}
if errors.As(err, &validationErr) {
    st, _ := status.New(codes.InvalidArgument, "validation").WithDetails(
        &errdetails.BadRequest{FieldViolations: violations(validationErr)},
    )
    return nil, st.Err()
}
return nil, status.Errorf(codes.Internal, "lookup: %v", err)

Quick map:

DomainCode
Missing/invalid fieldInvalidArgument
Not foundNotFound
Already existsAlreadyExists
UnauthenticatedUnauthenticated
Authenticated but forbiddenPermissionDenied
Rate-limitedResourceExhausted
Dependency down, retriableUnavailable
Bug, unexpectedInternal

Streaming

PatternUse case
Server streamingLog tailing, paginated result sets, server-sent events
Client streamingFile upload, batch ingest
BidirectionalChat, real-time sync

Streams must respect ctx.Done(). A goroutine reading from a stream after the client disconnects is a slow leak.

func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
    for _, u := range s.repo.All(stream.Context()) {
        if err := stream.Send(toProto(u)); err != nil {
            return err // includes ctx canceled
        }
    }
    return nil
}

Testing with bufconn

bufconn is an in-memory net.Listener. It exercises the real gRPC stack — interceptors, marshaling, metadata — without binding a TCP port. See references/testing.md for the full harness plus table-driven status-code assertions, metadata injection, and stream testing.

Security Notes

  • TLS in production. Plaintext is only acceptable behind a confirmed-private network (and even then mTLS is preferable).
  • For service-to-service auth, prefer a mesh (Istio/Linkerd) over hand-rolled token validation.
  • For user auth, implement credentials.PerRPCCredentials to attach a token and validate inside an auth interceptor.
  • Reflection: enable in dev, disable in prod via build tag or env flag.

Anti-Patterns

Anti-patternWhy it hurtsDo this instead
return fmt.Errorf("not found")Wire code is Unknown, clients can't retry-discriminatestatus.Errorf(codes.NotFound, ...)
context.Background() to a client callNo deadline → goroutines pile up on a slow dependencycontext.WithTimeout(parent, 5s)
New ClientConn per requestTLS handshake every call; sockets exhaustOne grpc.NewClient at startup, reuse
Bare string as RPC argumentCannot add fields without breaking callersAlways Request/Response messages
Reflection on in productionLets attackers enumerate every methodCompile-out with build tag in prod
codes.Internal for all errorsClient retry config can't distinguish bugs from outagesMap domain → specific codes
No health serviceKubernetes can't gate traffic; rolling deploys breakRegister grpc_health_v1
Ignoring stream.Context().Done()Goroutines run after client disconnectSelect on ctx.Done() in stream loops

Verification Checklist

  • .proto packages are versioned (pkg/v1, not pkg)
  • All RPCs take Request and return Response messages
  • Generated code is in a separate directory, never edited
  • Every error return uses status.Errorf with a specific code
  • Every client call has a deadline via context.WithTimeout
  • Server registers grpc_health_v1
  • GracefulStop is bounded by a time.After fallback
  • Reflection is gated to non-production builds
  • Tests use bufconn and assert status.Code(err)

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.