// STRYKE-GRPC — ENGINEERING REPORT

Opt-in stryke connector package · cdylib libstryke_grpc.{dylib,so} (crate-type = cdylib) · dlopen'd in-process on use Grpc · JSON-over-FFI envelope

>_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.

cdylib
Crate type
opt-in
Tier
dlopen
Load model
JSON-over-FFI
Envelope

~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.

LayerImplementation
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 modelLibrary 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
BuildCargo 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
Testszunit-style under t/ with live-service variants when applicable
CIGitHub 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.

FamilyExports
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 & trailergrpc__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 & methodgrpc__parse_target, grpc__build_target, grpc__parse_method, grpc__build_method, grpc__split_full_method, grpc__parse_authority, grpc__build_authority
Metadatagrpc__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-agentgrpc__parse_content_type, grpc__build_content_type, grpc__parse_timeout, grpc__build_timeout, grpc__parse_user_agent, grpc__build_user_agent
Compression & framinggrpc__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:

StepAction
1Open a bidi stream to ServerReflection/ServerReflectionInfo
2Send ListServices (for list) or FileContainingSymbol (for the requested symbol)
3Follow import chains via FileByFilename until the full FileDescriptorProto graph is loaded
4Build 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.

CrateVersionRole
tonic0.14gRPC transport / channel / codegen; features tls-ring, tls-native-roots, gzip, zstd, deflate
tonic-prost / prost / prost-types0.14Protobuf codec + well-known types
prost-reflect0.16Dynamic DescriptorPool / DynamicMessage (feature serde)
tokio / tokio-stream1 / 0.1Shared multi-thread runtime + stream drains
serde / serde_json1JSON-over-FFI envelope (preserve_order)
tonic-prost-build0.14build-dependency: compiles proto/reflection.proto
anyhow, bytes, http, futures-util, once_cell, parking_lotError 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.

CapabilityStatus
List servicesshipped
Describe service / method / messageshipped
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 rootshipped
ASCII + binary (-bin) metadata; per-call deadline (grpc-timeout)shipped
gzip / zstd / deflate compression; message-size caps; response-metadata captureshipped
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 offopen
Unbounded / callback-style streaming for infinite streamsopen

#PROJECT METADATA

ItemValue
LicenseMIT
AuthorMenkeTechnologies
Repositorygithub.com/MenkeTechnologies/stryke-grpc
Parent languagestrykelang
Meta umbrellaMenkeTechnologiesMeta
Issuesgithub.com/MenkeTechnologies/stryke-grpc/issues