Skip to content

Mitigate gRPC diamond dependency and version skew issues for downstream consumers (Debian) #931

@mhutchinson

Description

@mhutchinson

Background

The tessera repository currently depends on google.golang.org/grpc primarily through Google Cloud client libraries (for Spanner and GCS) and via OpenTelemetry OTLP gRPC exporters (used in the AWS conformance binaries).

While Go modules manage dependencies using Minimum Version Selection (MVS), gRPC does not strictly adhere to semantic versioning (semver) across minor/patch releases for its internal structures and generated Protobuf stubs. As a result, downstream consumers of tessera using Debian encounter "diamond dependency" conflicts and version skew when their application depends on a different version of gRPC or Protobuf stubs than the one pinned by tessera. See google/trillian#3870 (comment) for context in Trillian.

This problem is particularly acute in unvendored packaging ecosystems like Debian, where packages are resolved against a single system-wide installed version of a library (e.g., golang-google-grpc-dev).

If a user on Debian wants to build an application against the lightweight POSIX backend of tessera, the build chain is currently forced to resolve the entire gRPC, Protobuf, and GCP client library tree due to imports in the codebase. Incompatibilities between the system-wide gRPC package and tessera's required dependencies will break the compilation entirely, even for developers who have no intention of using Spanner, GCP, or the gRPC features.

Proposed Solutions

Option 1: Split Drivers into Independent Modules (Ideal but Heavy)

Break out individual storage implementations into separate, dedicated Go modules (e.g., github.com/transparency-dev/tessera/storage/gcp).
Pros:

  • Cleanest dependency graph for the core module.
  • Ensures that consumers of tessera-core never resolve gRPC, Spanner, or Google Cloud SDKs at all.
    Cons:
  • Higher overhead for development, maintenance, and releasing multiple synchronized Go modules.

Option 2: Pragmatic Isolation via Build Flags (Recommended)

  1. Migrate Exporters to HTTP
  2. Add Build Flags to GCP/gRPC Drivers

Switch the AWS conformance OTel exporter in cmd/conformance/aws/otel.go from otlptracegrpc/otlpmetricgrpc to their HTTP equivalents (otlp*http). Since AWS ADOT fully supports receiving over HTTP, this is functionally equivalent and drops the direct gRPC dependency at the application level.

Put the entire storage/gcp package (and any other gRPC-coupled storage components) behind explicit build flags (e.g., //go:build gcp).
Pros:

  • Lightweight to implement without refactoring repository structure.
  • Allows downstream packagers (like Debian maintainers) or developers to completely prune gRPC from the compile process by building without the -tags gcp flag.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions