>_EXECUTIVE SUMMARY
stryke-grpc is one of the opt-in connector packages in the stryke ecosystem. Generic, reflection-based gRPC client for stryke — list services, describe methods, call unary and streaming RPCs with JSON in/out. Like grpcurl but as a stryke package: it ships as a Rust cdylib that stryke dlopens in-process on first use Grpc.
Reflection-driven means no .proto files needed on the client side. Targets that expose the gRPC reflection service work out of the box.
~ARCHITECTURE
In-process cdylib design: the stryke side is a thin .stk wrapper that calls FFI symbols on the dlopen'd library. No subprocess, no pipe — calls return on the same thread that made them.
| Layer | Implementation |
|---|---|
stryke wrappers (lib/*.stk) | Thin Grpc.stk wrapper (use Grpc); each public fn serializes args to JSON and parses the response |
cdylib (libstryke_grpc.{dylib,so}) | Single Rust cdylib crate; 46 entry points (8 RPC verbs + 38 connection-free wire helpers) exported as extern "C" fn grpc__<verb>(*const c_char) -> *mut c_char — the authoritative roster is [ffi].exports in stryke.toml |
| Process model | Library is dlopen'd once per stryke session on first use Grpc; a shared tokio runtime + tonic::Channel cache per endpoint + DescriptorPool cache per (endpoint, symbol) held in OnceCell; lives until the runtime exits |
| Build | Cargo with [lib] crate-type = ["cdylib"]; publish = false; the package itself ships via s pkg install -g . |
| Install path | ~/.stryke/store/grpc@<version>/ after make install; use Grpc from any stryke script resolves it |
| Tests | zunit-style under t/ with live-service variants when applicable |
| CI | GitHub Actions .github/workflows/ci.yml — cargo fmt + clippy + test, plus stryke pkg install verification |
$WHY OPT-IN (NOT BUILTIN)
Reflection-driven means no .proto files needed on the client side. Targets that expose the gRPC reflection service work out of the box.
The trade-off is intentional. The core stryke binary stays under ~40 MB precisely because each connector ships separately. Daily-driver work (one-liners, awk replacement, data scripting) doesn't need MongoDB drivers, AWS SDKs, or Spark Connect bindings linked in.
&FFI ENVELOPE
JSON in / JSON out across the FFI boundary. Each Grpc::* wrapper serializes its args to a JSON dict and calls the matching grpc__* symbol; the cdylib returns a JSON CString the wrapper parses. catch_unwind on every call converts panics to JSON errors. The returned CString is freed via the cdylib-exported stryke_free_cstring, wired automatically by rust_ffi::load_cdylib.
# request shape (built by the .stk wrapper)
{"...": ...}
# response shape
{"result": ...} # success — per-fn shape
{"error": "<msg>"} # failure — wrapper die()s with Grpc::<op>: <reason>
@FFI EXPORT INVENTORY
The cdylib exports 46 grpc__* symbols (the [ffi].exports array in stryke.toml). 8 are RPC verbs that open a channel; the other 38 are pure, connection-free wire helpers that parse / build / validate gRPC HTTP/2 metadata, status, target, timeout, content-type, user-agent, compression, and framing primitives. The Grpc.stk wrapper exposes a matching 46 public Grpc::* functions one-to-one.
| Family | Exports |
|---|---|
| RPC verbs (open a channel) | grpc__pkg_version, grpc__ping, grpc__list, grpc__describe, grpc__call, grpc__server_stream, grpc__client_stream, grpc__bidi_stream |
| Status & trailer | grpc__status_code, grpc__status_description, grpc__status_codes, grpc__http_status_for, grpc__grpc_status_for_http, grpc__parse_grpc_status, grpc__build_grpc_trailer, grpc__encode_status_message, grpc__decode_status_message, grpc__retriable_status, grpc__health_status |
| Target & method | grpc__parse_target, grpc__build_target, grpc__parse_method, grpc__build_method, grpc__split_full_method, grpc__parse_authority, grpc__build_authority |
| Metadata | grpc__is_binary_key, grpc__is_reserved_key, grpc__is_pseudo_header, grpc__valid_metadata_key, grpc__normalize_metadata_key, grpc__valid_metadata_value, grpc__encode_bin_value, grpc__decode_bin_value |
| Content-type, timeout & user-agent | grpc__parse_content_type, grpc__build_content_type, grpc__parse_timeout, grpc__build_timeout, grpc__parse_user_agent, grpc__build_user_agent |
| Compression & framing | grpc__compression_codecs, grpc__valid_compression, grpc__parse_accept_encoding, grpc__build_accept_encoding, grpc__frame_message, grpc__unframe_message |
%REFLECTION PIPELINE
No .proto files live on the client. On every list / describe / call, src/reflection.rs talks to grpc.reflection.v1alpha.ServerReflection to build the descriptor graph at call time:
| Step | Action |
|---|---|
| 1 | Open a bidi stream to ServerReflection/ServerReflectionInfo |
| 2 | Send ListServices (for list) or FileContainingSymbol (for the requested symbol) |
| 3 | Follow import chains via FileByFilename until the full FileDescriptorProto graph is loaded |
| 4 | Build a prost-reflect DescriptorPool, look up the method, decode the JSON request into a DynamicMessage, encode it, fire the call via tonic's low-level Grpc::unary with a passthrough BytesCodec (src/codec.rs), then decode the response back to JSON |
The grpc.reflection.v1alpha client is generated at build time by build.rs (tonic-prost-build compiling proto/reflection.proto, client-only).
+DEPENDENCIES
The connector pulls in the tonic / prost / tokio gRPC stack — the reason it is opt-in rather than linked into core stryke.
| Crate | Version | Role |
|---|---|---|
tonic | 0.14 | gRPC transport / channel / codegen; features tls-ring, tls-native-roots, gzip, zstd, deflate |
tonic-prost / prost / prost-types | 0.14 | Protobuf codec + well-known types |
prost-reflect | 0.16 | Dynamic DescriptorPool / DynamicMessage (feature serde) |
tokio / tokio-stream | 1 / 0.1 | Shared multi-thread runtime + stream drains |
serde / serde_json | 1 | JSON-over-FFI envelope (preserve_order) |
tonic-prost-build | 0.14 | build-dependency: compiles proto/reflection.proto |
anyhow, bytes, http, futures-util, once_cell, parking_lot | — | Error context, byte buffers, header types, async combinators, cached OnceCell state, locks |
Release profile: opt-level = 3, lto = "thin", codegen-units = 1, strip = true, panic = "abort".
/SCOPE
The package is intentionally narrower than the underlying tonic SDK — the goal is "useful from a shell pipeline", not "complete API coverage". See the README's "Why this is a package" + "API reference" sections for the authoritative scope.
| Capability | Status |
|---|---|
| List services | shipped |
| Describe service / method / message | shipped |
| Unary call (JSON in / out) | shipped |
| Server-, client-, and bidi-streaming (bounded → JSON arrays) | shipped |
| TLS + native-roots cert chain; mTLS (client cert) + custom CA root | shipped |
ASCII + binary (-bin) metadata; per-call deadline (grpc-timeout) | shipped |
| gzip / zstd / deflate compression; message-size caps; response-metadata capture | shipped |
| Connection-free wire helpers (status / trailer / timeout / target / method / metadata / content-type / user-agent / authority / compression / health / framing) | shipped |
--proto FILE fallback when reflection is off | open |
| Unbounded / callback-style streaming for infinite streams | open |
#PROJECT METADATA
| Item | Value |
|---|---|
| License | MIT |
| Author | MenkeTechnologies |
| Repository | github.com/MenkeTechnologies/stryke-grpc |
| Parent language | strykelang |
| Meta umbrella | MenkeTechnologiesMeta |
| Issues | github.com/MenkeTechnologies/stryke-grpc/issues |