>_STRYKE-FLEET
Expect, but N sessions at once. Tcl/Expect automated one terminal; pexpect bolted multiprocessing on the side. stryke-fleet is the orchestration layer over stryke's native PTY builtins (pty_spawn, pty_expect, pty_expect_table, …) and its thread pool (pmap): transcripted sessions (Fleet::Session), declarative step lists (Fleet::Playbook), a recipe corpus for ssh / scp / sftp / ftp / telnet / sudo / su / passwd / psql / mysql / mongo / redis-cli / docker / npm login / gpg / openssl / installers / network gear (Fleet::Recipes), and one-playbook-N-targets fan-out (Fleet::Fanout). Pure stryke: no [ffi] table, no cdylib, no helper binary — just .stk modules loaded by stryke's normal package resolver.
The 4 sublibraries
| # | Module | File | Fns | Highlights |
|---|---|---|---|---|
| 1 | Fleet::Session | lib/Session.stk | 24 | open · send/send_line/send_lines/send_secret/send_control · expect · expect_exact (literal match) · expect_any · sendline_expect (send-then-expect atom) · branch · read · drain (collect trailing output until idle) · buffer · alive · eof · interact · close · transcript · events/matches/last_match · timed_out · transcript_text |
| 2 | Fleet::Playbook | lib/Playbook.stk | 4 | validate · run — ordered +{expect, send, branches, timeout, optional, send_matched, retries, retry_send, delay} steps; first non-optional timeout fails the run with the step name · dry_run (pure step preview) · concat (splice step lists) |
| 3 | Fleet::Recipes | lib/Recipes.stk | 49 | ssh_login · ssh_copy_id · sudo · doas · su · psql · mysql · mariadb · pg_dump · redis_cli · mongo · influx · yes_to_all · cisco_enable · telnet_login · sftp · scp · ftp_login · passwd_change · docker_login · npm_login · gpg_decrypt · openssl_pem · rsync · ssh_keygen · git_clone · kinit · ldapsearch · restic · borg · age · smbclient · vault_login · aws_configure · htpasswd · keytool · ansible_vault · sqlplus · op_signin · ssh_add · unzip_encrypted · cqlsh · cryptsetup · clickhouse_client · gcloud_auth · heroku_login · pass_insert · bw_unlock · names |
| 4 | Fleet::Fanout | lib/Fanout.stk | 18 | run (N targets via pmap) · batch (bounded concurrency) · ssh (host-list convenience) · partition (ok/failed split) · count · all_ok · failed_cmds · ok_cmds · find (first result by cmd pattern) · pluck · pluck_ok · summarize (counts + error lines) · group_by (generic bucketing) · group_by_error (bucket failures by error) · group_by_output (bucket successes by output — config-drift) · grep_transcript (which hosts printed X) · retry_failed (re-run only the failures) · retry_until (re-run failures up to N times) |
Color legend: green = module name · yellow = function count. Recipes are pure data generators — they never spawn anything, so they compose with @{...} splices and unit-test without a network.
Install
# From a release: s pkg install -g github.com/MenkeTechnologies/stryke-fleet # From a local checkout: git clone https://github.com/MenkeTechnologies/stryke-fleet cd stryke-fleet s pkg install -g . # installs into ~/.stryke/store/stryke-fleet@<version>/ # Or via Makefile: make install
No cargo step. No cdylib. The installed store directory contains only stryke.toml + lib/*.stk — no compiled artifacts, fully portable across platforms.
Quick Start
use Fleet # pulls all four sublibraries
# One session, transcripted
val $s = Fleet::Session::open("ssh user\@host")
Fleet::Session::expect($s, qr/password:/, 10)
Fleet::Session::send($s, "$pw\n")
Fleet::Session::close($s)
# Declarative playbook — first non-optional timeout fails the run
val $r = Fleet::Playbook::run("ssh user\@host", [
@{ Fleet::Recipes::ssh_login(+{ password => $pw }) },
+{ send => "uptime\n" },
+{ expect => qr/load average/, timeout => 15, name => "uptime output" },
+{ send => "exit\n" },
])
p $r->{ok} ? "done" : "failed: $r->{error}"
p "$_->{event} $_->{data}" for @{ $r->{transcript} }
# Branch tables — first match wins, optional action coderef
+{ branches => [
+{ re => qr/\(yes\/no\)\?/, do => fn { "yes\n" } },
+{ re => qr/password:/ },
],
send_matched => 1 }
# The headline: one playbook, fifty hosts, parallel PTY sessions
val $results = Fleet::Fanout::ssh([@hosts],
Fleet::Recipes::ssh_login(+{ password => $pw }))
val $part = Fleet::Fanout::partition($results)
p "ok: #{len @{$part->{ok}}} failed: #{len @{$part->{failed}}}"
Pull only what you need with explicit sub-imports: use Fleet::Playbook, use Fleet::Recipes, etc. The umbrella use Fleet is just for convenience.
Fleet::Session — full reference
A session is a hashref +{ raw => <pty handle>, cmd => CMD, log => [] } returned by open. Every send, match, timeout, and close is appended to log in order, so a finished or failed session can be replayed with transcript / transcript_text. The bare pty_* builtins stay reachable through $s->{raw}; this layer only adds bookkeeping.
| Function | Signature | Behavior |
|---|---|---|
| open | open($cmd) | Spawn $cmd under a PTY (pty_spawn). Returns the session hashref with an empty transcript. |
| send | send($s, $text) | Write $text to stdin verbatim; logs a send event with the text. |
| send_line | send_line($s, $text) | Like send but guarantees a trailing newline (added unless $text already ends in one) — the common case for answering a prompt. |
| send_lines | send_lines($s, $lines) | Send each element of the $lines arrayref newline-terminated like send_line; one send event per line. Returns the count. Dies if $lines is not an arrayref. |
| send_secret | send_secret($s, $text) | Transmit a password/token in full (trailing newline added) but record a redacted send event (data => "***", secret => 1) so credentials never leak into transcript dumps. |
| send_control | send_control($s, $char) | Send a control byte (pexpect sendcontrol): $char is a letter a–z (Ctrl-A…Ctrl-Z, e.g. "c"→0x03, "d"→0x04 EOF, "z"→0x1a) or one of @[\]^_?. Logs a send_control event with char + byte (raw byte never dumped). Unknown char dies. |
| expect | expect($s, $pat, $timeout = 30) | Wait for $pat (qr// or string) in the output. Returns the matched text or undef on timeout; both outcomes are logged (match / timeout). |
| expect_exact | expect_exact($s, $text, $timeout = 30) | Wait for the LITERAL $text (quote-meta'd with \Q…\E so regex metacharacters match as themselves) — the right primitive for prompts containing ., *, (, a $, or a literal (yes/no)?. Logged like expect. |
| expect_any | expect_any($s, $pats, $timeout = 30) | Wait for the FIRST of the $pats arrayref to appear. Returns +{ index => N, match => TEXT } for the pattern that hit, or undef on timeout. Built on branch; dies if $pats is not an arrayref. |
| sendline_expect | sendline_expect($s, $text, $pat, $timeout = 30) | Send $text as a line then immediately wait for $pat — the send-then-expect atom expect scripts repeat. Returns the matched text (undef on timeout); both the send and expect outcome are logged. |
| branch | branch($s, $branches, $timeout = 30) | First-match-wins table (pty_expect_table). $branches is [+{ re => qr/.../, do => fn {...} }, ...] (do optional). Returns the action's return value when present, else the matched text; undef on timeout. |
| read | read($s, $timeout = 5) | Non-matching read of buffered output (pty_read). |
| drain | drain($s, $idle = 1) | Collect all remaining output until the child goes idle: repeated short-timeout reads, concatenated, stopping at the first empty read or EOF. Returns the accumulated string ("" if nothing buffered). Not logged. |
| buffer | buffer($s) | Current unconsumed output buffer (pty_buffer). |
| alive | alive($s) | Is the child still running? (pty_alive) |
| eof | eof($s) | Has the child closed its side? (pty_eof) |
| interact | interact($s) | Hand the PTY to the user for manual debugging (pty_interact). |
| close | close($s) | Kill the child and release the PTY (pty_close); logs a close event with the cmd. |
| transcript | transcript($s) | The ordered event log: arrayref of +{ event, data } where event is send / match / timeout / close (or send_control). |
| events | events($s, $type) | Transcript entries of one event type, in order. |
| matches | matches($s) | The data of every match event, in order. |
| last_match | last_match($s) | The data of the most recent match event, or undef. |
| timed_out | timed_out($s) | 1 if any expect/branch in this session timed out, else 0 — catches a degraded run even when an optional step let the playbook continue. |
| transcript_text | transcript_text($s) | Render the transcript as one EVENT<TAB>DATA line per event, newlines in data escaped — the human-readable dump for a failed session. |
Fleet::Playbook — full reference
A playbook is an arrayref of step hashrefs run in order against one PTY. run spawns and closes its own session when handed a string command, or drives a caller-supplied open Fleet::Session when handed a hashref (the caller then closes it).
| Function | Signature | Behavior |
|---|---|---|
| validate | validate($steps) | Die unless $steps is a well-formed playbook (arrayref of hashrefs, each with at least one of expect/branches/send; branches must be an arrayref; retries a non-negative integer; delay ≥ 0). Returns the step count. |
| dry_run | dry_run($steps) | Validate, then return a human-readable per-step listing (index, name, action, timeout/delay/retries/optional) without spawning anything. Drives fleet.stk previews. |
| concat | concat(@parts) | Splice any number of step-list arrayrefs into one playbook (skipping undef parts), validate the combined list, and return the flat arrayref — the explicit form of the (@{ $a }, @{ $b }) recipe splice. Dies if any part is not an arrayref or any element is not a well-formed step. |
| run | run($cmd, $steps, $opts = +{}) | Run $steps against $cmd (string → session spawned here; hashref → an already-open session). Returns +{ ok, cmd, steps_run, transcript }, plus failed_step and error when ok == 0. A non-optional expect/branch timeout fails the run with the step name; the session is always closed unless the caller passed it in open. $opts->{timeout} sets the default per-step timeout. |
Step keys
| Key | Meaning |
|---|---|
expect | Pattern (qr// or string) to wait for. A non-optional timeout fails the run. |
branches | First-match-wins arrayref of +{ re, do } arms, run via Session::branch. |
send | Text written to stdin after the expect/branch (or alone, for a send-only step). |
send_matched | With branches: send the branch result back to the session. |
timeout | Seconds for the step's expect/branch (default 30, or $opts->{timeout}). |
name | Diagnostic label used in the failure error string. |
optional | A timeout on this step is not a failure — the run continues. |
retries | Re-attempt a timed-out expect/branch N more times (non-negative integer). |
retry_send | String sent before each retry — a nudge like "\n". |
delay | Seconds (fractional ok) to sleep before the step runs. |
Every step must carry at least one of expect / branches / send, or validate rejects it before anything spawns.
Fleet::Recipes — the corpus
Each recipe returns a plain playbook arrayref — pure data that composes with @{...} splices and unit-tests without a network. Options are passed as a single hashref. Where a recipe takes a prompt option it defaults to a generic shell-prompt regex; where it takes a password/passphrase it sends it with a trailing newline. Fleet::Recipes::names returns the full roster (48 entries).
| Recipe | Required opts | What it drives |
|---|---|---|
| ssh_login | (none) | Optional host-key confirmation, optional password prompt, then a shell prompt. Opts: password, prompt, timeout. |
| ssh_copy_id | password | Accept host key (optional), answer remote password, expect the "Number of key(s) added" confirmation (optional). |
| sudo | password | Answer the password prompt, then expect prompt. |
| doas | password | OpenBSD/Alpine doas: send password (its prompt, generic "Password:" fallback), then the shell prompt. |
| su | password | Switch user: answer password, then expect prompt. |
| psql | (none) | Optional password prompt, then the db=# prompt. |
| pg_dump | password | libpq "Password: " / "Password for user NAME: " prompt (pg_dump, pg_restore, psql -W). |
| mysql | (none) | Optional password prompt, then the mysql> prompt. |
| mariadb | (none) | Optional "Enter password:" then the MariaDB [...]> prompt. |
| mongo | (none) | Optional password prompt, then the > / rs0:PRIMARY> prompt. |
| redis_cli | (none) | The host:port> prompt; optional AUTH. |
| influx | password | InfluxDB v1 client auth ("username:" optional, "password:"), then the > query prompt. |
| cqlsh | (none) | Cassandra cqlsh: optional password prompt, then the cqlsh> prompt. |
| clickhouse_client | password | Answer the "Password for user (NAME):" prompt. |
| sqlplus | user, password | Oracle sqlplus: "Enter user-name:" then "Enter password:". |
| telnet_login | user, password | login: → user, Password: → password, then prompt. |
| ftp_login | user, password | Name: → user, Password: → password, then the ftp> prompt. |
| docker_login | user, password | Username: → user, Password: → password, then the success line. |
| npm_login | user, password, email | Legacy interactive npm login: Username / Password / Email prompts. |
| heroku_login | email, password | heroku login -i: email then password. |
| gcloud_auth | code | gcloud auth login --no-launch-browser: paste the authorization code. |
| op_signin | password | 1Password CLI op signin: account master-password prompt. |
| bw_unlock | password | Bitwarden CLI bw unlock: master-password prompt (outputs a session token). |
| vault_login | password | vault login (userpass/ldap): hidden password prompt, then success banner. |
| cisco_enable | enable_password | Cisco enable mode: > → enable → password → #. |
| scp | password | Optional host-key confirmation then a password prompt; the copy runs to completion. |
| sftp | (none) | Optional host-key confirmation, optional password, then the sftp> prompt. |
| rsync | password | rsync over ssh: host-key accept then password (same prompts as scp). |
| smbclient | password | SMB password then the smb: \> interactive prompt. |
| git_clone | (none) | ssh-key passphrase and/or https username+password — provide whichever the transport needs; all steps optional. |
| passwd_change | new_password | passwd chain: optional current password (root skips), new password, retype, success. Opt: old_password. |
| gpg_decrypt | passphrase | gpg --decrypt / symmetric: answer the passphrase prompt. |
| openssl_pem | passphrase | openssl PEM passphrase with verification ("Enter PEM pass phrase:" / "Verifying - ..."). |
| ssh_keygen | (none) | Passphrase entered twice (empty passphrase ⇒ none); optional overwrite answers the "Overwrite (y/n)?" prompt. |
| ssh_add | passphrase | ssh-add: decrypt a private key by passphrase. |
| age | password | age passphrase prompt (decrypt asks once; age -p asks twice — confirm step optional). |
| cryptsetup | password | LUKS passphrase prompts for luksFormat (capital "YES" confirm + verify, both optional) and luksOpen. |
| keytool | password | Java keytool keystore password, plus a re-enter prompt when confirm => 1. |
| htpasswd | (none) | htpasswd -c: New password / Re-type new password. Opt: password. |
| ansible_vault | password | ansible-vault: existing "Vault password:" prompt, or the new + confirm pair when new => 1. |
| pass_insert | password | pass insert NAME: "Enter password for NAME:" then "Retype password for NAME:" (retype optional with -e). |
| unzip_encrypted | password | unzip of an encrypted archive: the per-archive "password:" prompt. |
| kinit | password | Kerberos kinit: single "Password for principal@REALM:" prompt. |
| ldapsearch | password | OpenLDAP simple-bind: "Enter LDAP Password: " prompt (ldapsearch -W). |
| aws_configure | secret_key | aws configure: access key, secret key, region (default ""), output format (default "json"). Opt: access_key. |
| restic | password | restic repository password (init asks twice — verify step optional). |
| borg | password | BorgBackup repository passphrase (init asks twice — verify step optional). |
| yes_to_all | (none) | Answer up to $count yes/no-style prompts with $answer (default "y"), tolerating runs that ask fewer. |
| names | — | Returns all 48 recipe names — what s bin/fleet.stk recipes lists. |
Fleet::Fanout — full reference
One playbook against N targets, each on its own PTY on its own pmap thread; results come back as an arrayref of Playbook::run result hashrefs in target order. The rest of the surface is triage over that result list.
| Function | Signature | Behavior |
|---|---|---|
| run | run($cmds, $steps, $opts = +{}) | Run $steps against every command in $cmds concurrently via pmap. Validates the playbook first; dies if $cmds is not an arrayref. $opts pass through to Playbook::run. |
| batch | batch($cmds, $steps, $batch_size, $opts = +{}) | Run in consecutive batches of $batch_size — parallel within a batch, sequential between — to bound concurrency so a large fleet doesn't exhaust fds/network. Flat result list in order. $batch_size must be ≥ 1. |
| ssh | ssh($hosts, $steps, $opts = +{}) | Fan $steps out over $hosts as ssh HOST. Opts: user (login name), ssh_opts (extra flags string), plus Playbook opts. |
| partition | partition($results) | +{ ok => [...], failed => [...] } split. |
| count | count($results) | +{ total, ok, failed } tally — the numbers-only core of summarize. |
| all_ok | all_ok($results) | 1 iff every result is ok (empty set is vacuously ok), else 0 — the boolean fleet-pass gate. |
| summarize | summarize($results) | +{ total, ok, failed, errors => ["CMD: ERROR", ...] } — one line per failure. |
| failed_cmds | failed_cmds($results) | The cmd string of every failed result, in order. |
| ok_cmds | ok_cmds($results) | The cmd string of every ok result, in order — the hand-off list for "now run the next stage only against the hosts that passed". |
| find | find($results, $pattern) | First result whose cmd matches $pattern (regex string or qr//), else undef — by-target lookup over a cmd-ordered list. |
| pluck | pluck($results, $key) | Map the $key coderef over all results → one extracted value per result, order preserved. $key required. |
| pluck_ok | pluck_ok($results, $key) | Like pluck but only over ok results — the projection without the undefs a failed result would yield. |
| group_by | group_by($results, $key) | Bucket all results by a $key coderef → { "key" => [cmd, ...] }, cmd order preserved, undef key bucketed under "". The general primitive the two specialized groupers below build on. |
| group_by_error | group_by_error($results) | Bucket failures by error string → "which hosts failed the same way". Successes ignored. |
| group_by_output | group_by_output($results, $key = undef) | Bucket successes by captured output for config-drift detection (default key = last transcript data event; pass a $key for something finer). |
| grep_transcript | grep_transcript($results, $pattern) | Which targets printed $pattern anywhere in their transcript → arrayref of +{ cmd, ok, match } — catches things that didn't fail the playbook (a deprecation warning, a version banner). |
| retry_failed | retry_failed($results, $steps, $opts = +{}) | Re-run only the failed entries once, in place; ok results pass through, re-runs are tagged retried => 1. Order preserved. |
| retry_until | retry_until($results, $steps, $max_attempts, $opts = +{}) | Re-run each failure up to $max_attempts times, stopping per-target on first success; needed retries carry retried => 1 and attempts => N. $max_attempts ≥ 1. |
Examples
Four runnable scripts ship in examples/. The first three run anywhere — they drive a local sh/cat, no network — while parallel_ssh.stk needs real hosts.
| File | Run | What it shows |
|---|---|---|
local_demo.stk | s examples/local_demo.stk | A playbook drives a local sh (echo → expect → exit) and prints the transcript. |
installer_autopilot.stk | s examples/installer_autopilot.stk | yes_to_all answers a fake two-question installer simulated with sh. |
orchestrate.stk | s examples/orchestrate.stk | All four layers in one flow against local cat: recipe splice + playbook + session transcript reads + fan-out summary. |
parallel_ssh.stk | FLEET_HOSTS="web1 web2 db1" FLEET_PASS=... s examples/parallel_ssh.stk | The headline: one ssh_login recipe, N hosts, parallel sessions, partitioned ok/failed results. |
# orchestrate.stk, condensed — recipe splice into a playbook, then fan-out
val @plan = ( @{ Fleet::Recipes::yes_to_all(2) }, +{ send => "go\n" } )
p "spliced playbook: #{Fleet::Playbook::validate(\@plan)} steps"
val $res = Fleet::Fanout::run(["cat", "cat", "cat"], [
+{ send => "ping\n" },
+{ expect => qr/ping/, timeout => 5, name => "echo" },
])
val $sum = Fleet::Fanout::summarize($res)
p "fanout: #{$sum->{total}} targets, #{$sum->{ok}} ok, #{$sum->{failed}} failed"
Troubleshooting
- A run failed — what happened? The result hashref carries the full
transcriptplusfailed_stepanderror("timeout on <step name>"). Dump it withFleet::Session::transcript_textor read the+{ event, data }entries directly. - A prompt has regex metacharacters. Use
expect_exact(or quotemeta the pattern) so a literal$,., or(yes/no)?matches as itself instead of as a regex. - The prompt sometimes doesn't fire. Add
retrieswith aretry_send => "\n"nudge, or mark the stepoptionalwhen its absence is acceptable. - Credentials must not appear in logs. Use
send_secretinstead ofsend/send_line— the byte stream is transmitted in full but the transcript records a redacted***event. - A large fleet exhausts file descriptors. Use
Fleet::Fanout::batchwith a$batch_sizeinstead ofrunto bound concurrency. - Transient failures across the fleet. Wrap with
retry_failed(one re-run) orretry_until($results, $steps, $max_attempts)(per-target cap); inspectgroup_by_errorto see which hosts failed the same way. - Auditing what would be sent. Recipes are pure data —
to_jsonthe step list, or useFleet::Playbook::dry_runfor a per-step preview, before pointing it at production.
What's NOT in Here
By design — these are stryke builtins, so we don't re-wrap them. If a function in this library can be replaced with one builtin call, it's a bug.
| Category | Builtins (call directly) |
|---|---|
| PTY primitives | pty_spawn · pty_send · pty_read · pty_expect · pty_expect_table · pty_buffer · pty_alive · pty_eof · pty_close · pty_interact |
| Parallelism | pmap · pgrep · ppool and the rest of the parallel suite |
| Remote dispatch | cluster([...]) + pmap_on — persistent SSH worker pools for non-interactive remote compute; Fleet is for sessions that prompt back |
| Method-form sugar | PtyHandle class (require "perl_pty_class.stk" from strykelang examples) |
CLI
s bin/fleet.stk expect "sh -c 'sleep 1; echo ready'" ready 10 # spawn, wait, print match s bin/fleet.stk exchange cat hello hello 5 # spawn, send, wait s bin/fleet.stk recipes # list the recipe corpus s bin/fleet.stk version s bin/fleet.stk help
bin/fleet.stk covers one-shot expect/send loops from the shell. Anything with branches, retries, or more than two steps belongs in a playbook script — see examples/.
Tests
s test t/ # assertions across every public function
t/test_fleet.stk is headless-CI safe by contract: every PTY assertion drives a local cat/sh — no network, no SSH, no target hosts (enforced by tests/repo-contract.sh). Recipes are asserted structurally as data. Exits TAP-style via test_run.
Layout
stryke-fleet/ ├── stryke.toml # pure-stryke package manifest (no [ffi]) ├── Makefile # test / install / clean ├── LICENSE # MIT ├── lib/ │ ├── Fleet.stk # `use Fleet` — pulls all four sublibs │ ├── Session.stk # `use Fleet::Session` — transcripted PTY sessions │ ├── Playbook.stk # `use Fleet::Playbook` — declarative step runner │ ├── Recipes.stk # `use Fleet::Recipes` — login/prompt corpus │ └── Fanout.stk # `use Fleet::Fanout` — parallel multi-target runs ├── bin/ │ └── fleet.stk # CLI front-end (one-shot expect/exchange) ├── t/ │ └── test_fleet.stk # all-surface assertions (local processes only) ├── examples/ │ ├── local_demo.stk # runnable anywhere — drives a local sh │ ├── installer_autopilot.stk# yes_to_all against a fake installer │ ├── parallel_ssh.stk # the headline: recipe × N hosts, partitioned │ └── orchestrate.stk # all four layers: recipe + playbook + transcript + fanout ├── tests/ # shell gate scripts (CI lints) └── docs/ # this site (GitHub Pages)
Sibling packages
Part of the stryke package family. Browse the others via the MenkeTechnologiesMeta umbrella repo:
- stryke-arrow — Apache Arrow / Parquet / Feather
- stryke-aws — S3, DynamoDB, SQS, Lambda, STS
- stryke-azure — Blob, Queues, Cosmos DB, Key Vault, Entra
- stryke-clickhouse — ClickHouse
- stryke-demo — live demos for every connector
- stryke-docker — Docker daemon API
- stryke-duckdb — embedded DuckDB SQL engine
- stryke-email — email + campaign client
- stryke-gcp — Cloud Storage, Pub/Sub, Secret Manager, BigQuery, Firestore
- stryke-grpc — reflection-based gRPC client
- stryke-gui — mouse / keyboard / screen / clipboard automation
- stryke-k8s — Kubernetes
- stryke-kafka — Apache Kafka
- stryke-mcpd — MCP servers without a runtime
- stryke-mongo — MongoDB
- stryke-mssql — Microsoft SQL Server
- stryke-mysql — MySQL / MariaDB
- stryke-neo4j — Neo4j graph (Bolt)
- stryke-office — Office docs / PDF / images
- stryke-parquet — Parquet file inspector
- stryke-polars — Polars + ndarray + linalg + FFT
- stryke-postgres — PostgreSQL
- stryke-redis — Redis / Valkey
- stryke-scrape — web scraping / crawling
- stryke-scylla — ScyllaDB / Cassandra
- stryke-search — Elasticsearch / OpenSearch
- stryke-selenium — browser automation (WebDriver)
- stryke-spark — Spark Connect (no JVM)
- stryke-utils — pure-stryke utility belt
- stryke-zmq — ZeroMQ messaging