Source: docs/conformance.md · Edit on GitHub

Conformance & interop scorecard

Target: near grpc-go / official interop parity (Phases 1–9 hard; ALTS allowlisted).

Overall estimates (judgment, not a formal audit)

Axis Implemented Tested Notes
Official gRPC standard (PROTOCOL-HTTP2 + core RPC) ~95% ~90% Core wire solid; gaps: full cacheable_unary (needs caching proxy), HTTP CONNECT out of scope, some duplex still buffered-batch
vs grpc-go app surface ~93% ~88% Strong general-app parity; ALTS/GCE allowlisted; grpclb thin; binary-log/stats/interceptors shallower than grpc-go; hedge race cautious
vs Python (grpcio) peer surface ~92% ~82% Shared wire covered both ways; no Python stress demo yet; fewer chaos/negative cases than Go
vs Rust (tonic) peer surface ~92% ~82% Same official TestService matrix as Python (rust_interop/); no Rust stress demo yet

Remaining risk is mostly peer lifecycle edges (half-close, compress+stream, cancel timing) and cloud-live paths (ADC/ALTS/xDS against real infra), not missing basic RPCs. Stress demos found two such bugs (gzip stream decode; bidi empty END_STREAM); FramingMatrix + run-stress-demos.sh now hard-gate those classes.

Metric Score
vs grpc-go app surfaces ~93% implemented / ~88% tested (see table above)
h2spec 145/146 pass, 1 skipped (full suite hard CI; skip is h2spec suite skip, not an allowlisted failure)
Official non-auth interop Lean↔︎Lean; Go↔︎Lean; Python↔︎Lean; Rust↔︎Lean (CI)
Security regressions Hard CI: securityTests + scripts/security-asan.sh (TLS verify model, gzip bomb cap, H2 flow-control/header-list, SDS paths, codegen allowlist)
Formal proofs (pure codecs) Hard CI: lake build Proofs — status/BE/framing/varint/HPACK/metadata theorems (proofs.md); does not prove TLS/FFI/e2e
Stress demos + framing matrix Hard CI (scripts/run-stress-demos.sh): VaultGauntlet, MirrorForge, SignalWeave, FramingMatrix
Compression identity/gzip/deflate/snappy; Go gzip client→Lean; Lean→Go gzip-enabled server
In-process TLS Lean→Go TLS unary (no sidecar); Lean↔︎Lean mTLS (client cert required/verified)
ADC SA JWT + GCE metadata (mock CI, not live Google); run-adc-live.sh for manual/nightly live checks
xDS ADS Full LDS→RDS→CDS→EDS chain + SDS cert stub + ACK/NACK (FakeAds CI)
HTTP CONNECT proxying N/A — this library dials backends directly (h2c/TLS); tunneling through a corporate HTTP_PROXY via HTTP/1.1 CONNECT is out of scope

Scorecard by area

Area % Notes
HTTP/2 + HPACK (h2spec) 100 Full hard gate
Core RPC + duplex + deadlines 100 Incremental FullDuplex; mid-RPC deadline RST
Peer framing / empty half-close 100 FramingMatrix + stress demos hard-gated; multi-frame gzip decodeOneIO; bidi empty DATA+END_STREAM finalize
PROTOCOL-HTTP2 wire correctness 100 HTTP 415 on bad content-type; RST→status mapping; grpc-status-details-bin/google.rpc.Status; trailers-only errors; user-agent; :scheme https on TLS
Official non-auth interop 100 Go + Python + Rust matrices in CI
Protobuf + codegen 97 Text .proto path (LEAN_GRPC_PROTO) unchanged; real protoc-plugin path now decodes CodeGeneratorRequest and emits real message structs + typed client/server/bidi signatures (scripts/run-codegen-fixture.sh)
TLS + ALPN h2 100 In-process OpenSSL (tls_ffi); sidecar optional; optional mTLS (client cert dial + server client-CA verify)
Channel/call credentials 100 JWT/OAuth/per-RPC + ADC Bearer (mock-proven)
Resolver / LB / retry / hedge 100 DNS, pick_first/RR, retry + hedgingPolicy
Health / reflection / channelz 100 opsSmoke CI gate: Check + Watch, v1/v1alpha reflection, real GetTopChannels/GetServers counters
Perf / soak / keepalive 100 benchUnary, rpc_soak/channel_soak-flag-compatible benchSoak, PING timeout→GOAWAY
GCP / ALTS / ORCA / xDS 98 ORCA unary + orca_oob streaming; full ADS LDS/RDS/CDS/EDS/SDS chain w/ ACK/NACK; ALTS allowlisted
Binary logging / stats 90 New Grpc.BinaryLog (header/message/trailer event capture) + Grpc.Stats (counters + Prometheus/OTel-stub export), both covered by opsSmoke
Load balancing extras 85 Grpc.Grpclb client shim (BalanceLoad → server list + drop handling); xDS remains the preferred/primary LB discovery path
Interceptors 90 Grpc.Interceptor client/server unary chains (logging built-ins; auth/metrics compose the same way), covered by opsSmoke

Mock vs live

Surface Tested how
Core / streaming / metadata Live peers: Lean, Go, Python, Rust
Compression Live Go with gzip codec registered (Tests/GoGzipServer)
TLS Live Go TLS server + Lean in-process client; Lean↔︎Lean mTLS loopback (tlsLoopback, self-signed CA via system openssl)
ADC Local HTTP mock (scripts/mock-adc-server.py) in CI; scripts/run-adc-live.sh documents a real-Google check (manual/nightly, needs live credentials)
xDS ADS Lean fakeAdsServer speaking chained LDS/RDS/CDS/EDS/SDS + NACK-triggering resource; CI sets LEAN_GRPC_XDS_INSECURE=1 (cleartext h2c FakeAds only)
Codegen (real protoc-plugin path) Hand-built binary CodeGeneratorRequest fixture piped directly into protoc-gen-lean4-grpc’s stdin (no protoc binary required/available in CI)
grpclb Unit-level (Grpc.Grpclb decode/pick helpers); no live grpclb balancer server in CI
ALTS / GCE channel creds Not tested — allowlisted
HTTP CONNECT proxying Not implemented / N/A

Interop case checklist

Case Lean↔︎Lean Go→Lean Lean→Go
empty_unary … timeout_on_sleeping_server (core)
client/server_compressed_* gzip helper ✓ gzip server
pick_first_unary
cacheable_unary ✓ (GET verb → EmptyCall) — (needs caching proxy; not run) — vs Go (GET EmptyCall → INTERNAL on recent grpc-go); — vs Python
jwt / oauth / per_rpc ✓ fixture
orca_per_rpc
xds_static_unary / xds_ads_unary / xds_ads_chain_unary / xds_ads_nack
tls_empty_unary
compute_engine_channel / ALTS allowlist allowlist allowlist

Notes: - cacheable_unary here checks the GET-verb wire mechanism (safe/idempotent calls use :method: GET) against EmptyCall in Lean↔︎Lean CI, rather than replicating the official CacheableUnaryCall + caching-proxy + timestamp-equality assertions (infrastructure outside this repo). It is not run against recent grpc-go interop servers (GET EmptyCall → INTERNAL) or Python C-core (rejects GET). - cancel_after_begin / cancel_after_first_response now assert CANCELLED (grpc-status 1) after the client-initiated RST, using the same RST→trailers mapping (H2.Client.rstToTrailers) applied locally as soon as the client sends the reset. - Compression negotiation preference order is gzip > deflate > snappy > identity (Grpc.Compression.negotiate); deflate uses a zlib (RFC 1950) container, snappy uses literal-only (uncompressed) block framing that any conformant Snappy reader can decode. - mTLS: Grpc.Tls.Config.certPath/keyPath present a client certificate on dial; Grpc.Tls.Config.clientCaPath makes serveTls/Grpc.Tls.serveH2 require and verify a client certificate. Covered end-to-end by tlsLoopback (plain TLS, mTLS-reject-without-cert, mTLS-accept-with-cert), skipped only if openssl is unavailable in the environment. - ADC composition: Grpc.Adc.dialOptions builds Credentials.DialOptions combining TLS channel credentials with an ADC-derived per-RPC Bearer token, for calling real Google APIs. - xDS ADS now resolves the full chain a real xDS-enabled client would: Grpc.XdsAds.resolveChain does LDS(listener) → RDS(route config) → CDS(cluster) → EDS(endpoints) over one ADS session, with real ACK/NACK (Grpc.Xds.Discovery.Request.ack/.nack) on each hop; fakeAdsServer serves all five resource types (LDS/RDS/CDS/EDS/SDS) plus a deliberate nack-test resource whose inner Any.type_url is wrong, to exercise the NACK path end-to-end. - Observability additions: Grpc.Health.registerWithWatch adds a Watch (server-streaming) handler alongside Check; Grpc.Reflection decodes real ServerReflectionRequests and answers list_services/file_by_filename/file_containing_symbol against a small registry for both v1 and v1alpha; Grpc.Channelz.GetTopChannels/GetServers return real call counters keyed off a shared IO.Ref Counters; Grpc.Orca reports use IEEE-754 double utilization fields and support both per-call and out-of-band (orca_oob) streaming reports; Grpc.BinaryLog captures header/message/trailer events to an in-memory sink; Grpc.Stats exposes call counters via a Prometheus-style exposition and a stdout/OTel-stub exporter. - Codegen: the lightweight .proto-text path (LEAN_GRPC_PROTO, used by the Lake CI smoke test) is unchanged and still emits ByteArray-typed RPC stubs (it has no field-level info). The real protoc-plugin path (stdin/stdout, invoked as protoc --plugin=... --lean4_out=...) now decodes a minimal but real subset of google.protobuf.compiler.CodeGeneratorRequest (messages, fields, services, methods) and emits real message structs with encode/decode plus correctly-typed unary/client-streaming/server-streaming/bidi client stubs and a typed unary server-registration helper per RPC. scripts/run-codegen-fixture.sh hand-builds a request for a small helloworld-shaped service (no protoc binary needed) and checks the emitted code for the expected struct/stub/handler names. - grpclb: Grpc.Grpclb is a thin client-side shim for the legacy grpc.lb.v1.LoadBalancer/ BalanceLoad protocol — one-shot connect, InitialLoadBalanceRequest, decode the first ServerList response, and filter out drop entries. xDS ADS (Grpc.XdsAds) remains the preferred/primary discovery+LB path in this library; grpclb exists for interop with older balancers that only speak the plain server-list protocol. - Interceptors: Grpc.Interceptor provides composable client/server unary interceptor chains (applyClient/applyServer, callUnary/registerUnary) plus ready-made logging interceptors; additional cross-cutting concerns (auth, metrics, retries) compose the same way by adding more ClientUnary/ServerUnary elements to the chain array. - Soak: Bench/Soak.lean (benchSoak) now understands the standard --soak_* flags used by grpc’s rpc_soak/channel_soak tools (soak_iterations, soak_max_failures, soak_per_iteration_max_acceptable_latency_ms, soak_min_time_ms_between_rpcs, soak_overall_timeout_seconds, soak_request_size, soak_num_channels, soak_reset_channel_per_iteration), while remaining back-compatible with the original benchSoak host port N positional-iterations form. See scripts/run-soak.sh.

Commands

./scripts/run-go-to-lean.sh
GRPC_PORT=10001 ./scripts/interop-go-lean.sh
./scripts/run-python-to-lean.sh
GRPC_PORT=10001 ./scripts/interop-lean-python.sh
./scripts/run-rust-to-lean.sh
GRPC_PORT=10001 ./scripts/interop-lean-rust.sh
./scripts/interop-compress-go-lean.sh
./scripts/interop-tls-go-lean.sh
./scripts/run-adc-smoke.sh
./scripts/run-xds-ads-smoke.sh       # full LDS/RDS/CDS/EDS/SDS chain + NACK path
./scripts/run-codegen-fixture.sh     # real protoc-plugin descriptor decode + typed emit
./scripts/run-soak.sh                # rpc_soak + channel_soak flag surface
./scripts/h2spec.sh
./scripts/run-stress-demos.sh        # VaultGauntlet + MirrorForge + SignalWeave + FramingMatrix
./scripts/run-framing-matrix.sh      # peer framing only (Lean↔Lean + Go→Lean)
lake build tlsLoopback && ./.lake/build/bin/tlsLoopback   # mTLS loopback (needs `openssl` CLI)

# Manual / nightly only — needs live Google credentials + network egress:
GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json ./scripts/run-adc-live.sh

Source: docs/conformance.md in RileyBetts/lean-grpc. Hosted docs may lag the repository slightly.