// STRYKE-K8S — ENGINEERING REPORT

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

>_EXECUTIVE SUMMARY

stryke-k8s is one of the opt-in connector packages in the stryke ecosystem. Kubernetes client for stryke. Get / apply / delete / scale / logs / watch / exec against any kubeconfig-reachable cluster (kind, k3s, minikube, EKS, GKE, AKS, OpenShift, vanilla). Opt-in package, kept out of the stryke core binary so the daily-driver install stays slim; ships as a Rust cdylib that stryke dlopens in-process on first use K8s.

Built on the kube crate (kube-client + kube-runtime). Drop-in replacement for kubectl one-liners that pipe through jq / yq / awk.

cdylib
Crate type
opt-in
Tier
dlopen
Load model
JSON-over-FFI
Envelope
74
Public K8s:: fns
70
FFI exports
kube 0.95
Core dep
MIT
License

The surface splits into cluster operations (dispatched over FFI to the kube client) and pure helpers (selector/quantity/reference math computed locally, no cluster contact). Streaming-only ops (watch, logs_follow, exec) are deferred — the surface is defined but the wrappers die until the callback FFI ships.


~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 .stk wrapper (K8s); exposes typed helpers that serialize args to JSON and parse the response
cdylib (libstryke_k8s.{dylib,so})Single Rust cdylib crate; every public surface fn is an extern "C" fn k8s__<verb>(*const c_char) -> *mut c_char
Process modelLibrary is dlopen'd once per stryke session on first use K8s; a shared tokio runtime + a kube::Client cache keyed by kubeconfig context 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/k8s@<version>/ after make install; use K8s from any stryke script resolves it
TestsLive round-trip under t/ (test_k8s.stk, test_stryke_k8s_surface.stk) against a kind/k3s cluster, in a per-run stryke-test-$$ namespace torn down at exit
CIGitHub Actions .github/workflows/ci.yml — jobs: check, fmt (cargo fmt --all --check), clippy (-D warnings), test (ubuntu + macos), doc (cargo doc -D warnings), release-build (linux x86_64 + macos x86_64/aarch64), docs-gates, polish-gates

+DEPENDENCIES

The cdylib links the Kubernetes client stack; core stryke links none of it. Versions are pinned in Cargo.toml.

CrateVersionRole
kube0.95Kubernetes client + runtime (client, rustls-tls, config, runtime, derive)
k8s-openapi0.23Typed Kubernetes API objects (latest feature)
tokio1Async runtime (rt-multi-thread, macros, io-std/util, signal)
serde / serde_json / serde_yaml1 / 1 / 0.9JSON-over-FFI envelope + YAML manifest rendering (preserve_order)
futures-util0.3Stream combinators for async list/log paths
anyhow / once_cell / parking_lot1 / 1 / 0.12Error plumbing + the OnceCell runtime/client caches
either / http1 / 1Exec stream multiplexing + raw apiserver request types

Release profile: opt-level = 3, lto = "thin", codegen-units = 1, strip = true, panic = "abort".


@PERSISTENT STATE

The v1 helper rebuilt the client (full TLS+auth handshake) on every fork. The cdylib holds two process-lifetime caches instead:

CellContents
RUNTIMEOne shared tokio multi-thread runtime driving every async call — no per-call runtime spin-up
CLIENTSkube::Client cache keyed by kubeconfig context; reuses the same client + underlying HTTP connection pool across calls

Both are held in OnceCell for the life of the process. The first use K8s dlopens the library; subsequent calls hit the warm caches.


$WHY OPT-IN (NOT BUILTIN)

Built on the kube crate (kube-client + kube-runtime). Drop-in replacement for kubectl one-liners that pipe through jq / yq / awk.

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 K8s::* wrapper serializes its args to a JSON dict and calls the matching k8s__* symbol on the dlopen'd cdylib; 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)
{"verb": "<op>", "...": ...}

# response shape
{"result": ...}            # success — per-fn shape
{"error": "<msg>"}        # failure — wrapper die()s with the message

/SCOPE

74 public K8s::* functions. The package is intentionally narrower than the full Kubernetes API — the goal is "useful from a shell pipeline", not "complete API coverage". The surface groups into:

GroupFunctions
Read pathsget, get_one, get_yaml, exists, namespaces, nodes, api_resources, watch (deferred)
Write pathsapply, create, replace, patch, delete_resource, delete_collection, scale, get_scale
Rollouts + workloadset_image, rollout_restart, rollout_status, rollout_history, autoscale, label, annotate
Nodes + schedulingcordon, uncordon, taint, untaint, evict
Events / metrics / waitevents, top_pods, top_nodes, wait
Logs + execlogs; logs_follow + exec (deferred)
Connection + plumbingversion, ping, contexts, current_context, healthz, raw, pkg_version
Pure helpers (no cluster)valid_name / valid_label_value / valid_label_key; parse/build selector + field_selector + resource_ref + gvk + api_version + image_ref; selector_matches, field_selector_matches; parse/format/normalize/compare/sum/sub/scale quantity; resource_ratio, pod_status, container_images, condition, age_seconds, owner_refs, diff_merge_patch, drain_filter

Full signatures live in the Docs API reference. The README's project page carries the same authoritative scope.


!DEFERRED OPS

Streaming-only operations — watch, logs_follow, and exec — are defined on the surface but die with a clear message. They need a callback FFI shape that the current FfiSig::StrToStr JSON-in/JSON-out envelope does not model: each emits an unbounded sequence of events rather than a single response. Buffered logs (K8s::logs) ship today. The callback FFI is the gate; once it lands, these light up without surface changes.


#PROJECT METADATA

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