>_STRYKE-GCP
GCP from the pipe. Google Cloud client for stryke — Cloud Storage, Pub/Sub, Secret Manager, BigQuery, Firestore, Compute Engine, Cloud Run, Cloud Functions, Cloud Logging, Cloud Monitoring, and IAM. 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 GCP.
Scope
Twelve service families ship in v0.19.x, exposed through 84 gcp__* FFI entry points and a set of typed .stk wrappers. The package is intentionally narrower than the full GCP REST surface — the goal is “useful from a shell pipeline”, not exhaustive API coverage.
| Cloud Storage (GCS) | ls / get / put / rm / head / cp / compose / buckets / get+set IAM policy |
| Pub/Sub | publish / pull / ack / list+create+delete topics+subs / get topic / topic subscriptions / pump |
| Secret Manager | access / create / add-version / list / list-versions / delete |
| Auth identity | ADC + project resolution |
| BigQuery | query (jobs.query) + streaming insert + list/get datasets + list/get tables + list jobs |
| Firestore | get / set / delete / list / query / create (native-mode REST) |
| Compute Engine | list/get instances / start / stop / reset / list zones / regions / machine types / disks |
| Cloud Run | list/get services / list revisions (Admin API v2) |
| Cloud Functions | list / describe (API v2) |
| Cloud Logging | list entries / write entry / list logs (API v2) |
| Cloud Monitoring | list time series / list metric descriptors (API v3) |
| IAM | get policy / test permissions / list+get service accounts |
Install
# from a release (no rustc on the consumer machine)
s pkg install -g github.com/MenkeTechnologies/stryke-gcp
# from a local checkout
cd ~/projects/stryke-gcp
cargo build --release # produces target/release/libstryke_gcp.{dylib,so}
s pkg install -g . # cdylib lands in ~/.stryke/store/gcp@<version>/
# one-liner
make install
The cdylib is dlopened in-process on first use GCP — no helper-binary fork per call. Any stryke script that declares use GCP (or a submodule like use GCP::Storage) resolves the package automatically.
Auth
Uses Application Default Credentials — the same chain as gcloud:
$GOOGLE_APPLICATION_CREDENTIALSpointing at a service-account JSON.gcloud auth application-default loginon dev machines.- The GCE / Cloud Run / GKE metadata server when running on GCP.
Set the project once and forget it:
export GOOGLE_CLOUD_PROJECT=my-project-id
Per call, override the project with project => "..." on any public fn. ADC discovery, metadata-server lookup, WIF, and SA-file resolution run once per stryke session — the cached credentials live in OnceCell and are reused on every subsequent call.
GCP::Storage::ls "gs://my-bucket/", project => "other-project" GCP::PubSub::publish "my-topic", "x", project => "other-project"
Quick start: use GCP
| auth + project check | p to_json GCP::identity() |
| list GCS keys | my @entries = GCP::Storage::ls "gs://my-bucket/prefix/", delimiter => "/" |
| put an object | GCP::Storage::put "gs://my-bucket/hello.txt", data => "hello stryke", content_type => "text/plain" |
| get an object | p GCP::Storage::get "gs://my-bucket/hello.txt" |
| publish to Pub/Sub | GCP::PubSub::publish "my-topic", "event payload", attrs => { source => "stryke" } |
| pull from Pub/Sub | my @msgs = GCP::PubSub::pull "my-sub", max => 10, ack => 1 |
| pump (pull → callback → ack) | GCP::PubSub::pump "my-sub", iterations => 5, callback => sub ($m) { handle_message $m->{data} } |
Project / endpoint overrides apply on every public fn via project => "...". The complete reference is below; the README "API reference" section is the authoritative mirror.
API reference: use GCP
Plumbing fns plus the flat GCP::<service>_<op> entry points the namespaced wrappers delegate to.
| GCP::version() | cdylib package version string |
| GCP::ping(%opts) | connectivity probe |
| GCP::identity(%opts) | { ok, project, credentials_source } |
Pure helpers — credential-free string parsing / validation (no network, no auth):
| GCP::parse_gs_uri($uri) | { bucket, object } |
| GCP::build_gs_uri($b, $obj?) | bucket+object → gs:// URI (inverse of parse_gs_uri) |
| GCP::gs_uri_to_url($uri) | gs://b/o → { url, bucket, object } |
| GCP::url_to_gs_uri($url) | GCS URL → { uri, bucket, object } (inverse of gs_uri_to_url) |
| GCP::parse_resource_name($n) | { parts, pairs, trailing } for projects/p/topics/t |
| GCP::resource_name_parent($n) | { name, parent, has_parent, collection, id } |
| GCP::build_resource_name(%opts) | { parts } or { pairs, trailing } → resource name |
| GCP::parse_table_ref($ref) | BigQuery project.dataset.table / project:dataset.table → { table_ref, project, dataset, table, legacy } |
| GCP::build_table_ref(%opts) | inverse of parse_table_ref |
| GCP::valid_bucket_name($name) | { name, valid, reason } — GCS bucket rules |
| GCP::valid_project_id($id) | 6-30 lowercase/digit/hyphen, start letter, no trailing hyphen |
| GCP::valid_object_name($name) | 1-1024 UTF-8 bytes, no CR/LF, not ./.. |
| GCP::region_for_zone($zone) | us-central1-a → { zone, region, zone_letter } |
| GCP::valid_label($key, $value?) | Resource Manager label rules |
| GCP::valid_pubsub_id($id) | Pub/Sub topic/sub/schema/snapshot id rules |
| GCP::valid_service_account_id($id) | IAM accountId: 6-30 chars, RFC1035 |
| GCP::valid_secret_id($id) | Secret Manager secret ID: 1-255 chars |
| GCP::valid_dataset_id($id) | BigQuery dataset id: ≤1024, letters/digits/underscores |
| GCP::valid_table_id($id) | BigQuery table id: ≤1024 UTF-8 bytes (broader than dataset) |
Secret Manager (flat ops under use GCP):
| GCP::secret_access($secret, %opts) | → $value — opt version (default "latest") |
| GCP::secret_create($secret, %opts) | new empty secret, automatic replication |
| GCP::secret_add_version($secret, $value, %opts) | add a new version |
| GCP::secret_list(%opts) | → @secret_ids |
| GCP::secret_list_versions($secret, %opts) | → @{ {name, state, create_time} } (newest first) |
| GCP::secret_delete($secret, %opts) | deletes the secret and all versions |
API reference: use GCP::Storage
| GCP::Storage::ls $uri, %opts | → @entries (opt delimiter) |
| GCP::Storage::get $uri, %opts | → $body | $path (when output => "PATH") |
| GCP::Storage::put $uri, %opts | → \%resp — data => $bytes | input => "PATH" |
| GCP::Storage::head $uri, %opts | → \%meta (size, content_type, updated, md5_hash, …) |
| GCP::Storage::cp $src, $dst, %opts | → \%resp — server-side copy |
| GCP::Storage::rm $uri, %opts | → \%resp |
| GCP::Storage::buckets %opts | → @buckets |
| GCP::Storage::compose $bucket, $dst, \@sources | concatenate objects |
| GCP::Storage::get_iam_policy $bucket | → \%policy |
| GCP::Storage::set_iam_policy $bucket, \%policy | → \%resp |
ls entries: {type=>"object", key, size, content_type, md5, etag, updated, storage_class, generation} or {type=>"prefix", key} when delimiter is set.
API reference: use GCP::PubSub
| GCP::PubSub::publish $topic, $data, %opts | → \%resp — opts attrs, ordering_key |
| GCP::PubSub::pull $sub, %opts | → @messages — opts max, deadline, ack |
| GCP::PubSub::ack $sub, $ids_or_aref, %opts | → \%resp |
| GCP::PubSub::topics %opts | → @topic_names |
| GCP::PubSub::subs %opts | → @{ {name, topic} } |
| GCP::PubSub::create_topic $topic, %opts | → \%resp |
| GCP::PubSub::create_sub $name, $topic, %opts | → \%resp — opt ack_deadline |
| GCP::PubSub::delete_topic $topic, %opts | → \%resp |
| GCP::PubSub::delete_sub $sub, %opts | → \%resp |
| GCP::PubSub::get_topic $topic, %opts | → { topic, resource } |
| GCP::PubSub::topic_subs $topic, %opts | → @subscription_ids |
| GCP::PubSub::pump $sub, %opts | → $count — callback + auto-ack |
Topic / subscription names accept bare (my-topic) or fully qualified (projects/PROJECT/topics/my-topic) forms. Bare names expand against $opts{project} or $GOOGLE_CLOUD_PROJECT.
API reference: use GCP::BigQuery
| GCP::BigQuery::query $sql, %opts | → { columns, rows, total_rows, complete } — opts max_results, timeout_ms, project |
| GCP::BigQuery::rows $sql, %opts | → @rows — just the row hashrefs |
| GCP::BigQuery::insert $dataset, $table, \@rows, %opts | → { inserted, errors } — streaming insert |
| GCP::BigQuery::datasets %opts | → @dataset_ids |
| GCP::BigQuery::dataset $dataset, %opts | → { dataset, resource } |
| GCP::BigQuery::tables $dataset, %opts | → @table_ids |
| GCP::BigQuery::table $dataset, $table, %opts | → { dataset, table, resource } |
| GCP::BigQuery::jobs %opts | → @{ {id, state, job_type, user_email} } — opts all_users, max_results, state_filter |
API reference: use GCP::Firestore
| GCP::Firestore::get $collection, $document, %opts | → \%data | undef |
| GCP::Firestore::set $collection, $document, \%data, %opts | → \%resp — create-or-overwrite |
| GCP::Firestore::delete $collection, $document, %opts | → \%resp |
| GCP::Firestore::list $collection, %opts | → @{ {id, data} } — opt page_size |
| GCP::Firestore::query $collection, %opts | → @{ {id, data} } — opts field, op, value, limit |
| GCP::Firestore::create $collection, \%data, %opts | → { collection, id } — auto-id; opt document |
Field values cross as plain stryke data; the cdylib handles Firestore's typed encoding (stringValue / integerValue / …) in both directions. Flat forms are GCP::firestore_get / _set / _delete / _list.
API reference: use GCP::Compute
| GCP::Compute::instances $zone, %opts | → @{ {name, status, machine_type, zone} } |
| GCP::Compute::get $zone, $instance, %opts | → { zone, instance, resource } |
| GCP::Compute::start $zone, $instance, %opts | → { instance, action, operation, status } |
| GCP::Compute::stop $zone, $instance, %opts | → { instance, action, operation, status } |
| GCP::Compute::reset $zone, $instance, %opts | → { instance, action, operation, status } |
| GCP::Compute::zones %opts | → @{ {name, region, status} } |
| GCP::Compute::regions %opts | → @{ {name, status} } |
| GCP::Compute::machine_types $zone, %opts | → @{ {name, guest_cpus, memory_mb, description} } |
| GCP::Compute::disks $zone, %opts | → @{ {name, size_gb, type, status, zone} } |
API reference: Run / Functions / Logging / Monitoring / IAM
use GCP::Run
| GCP::Run::services $region, %opts | → @{ {name, uri, latest_revision} } |
| GCP::Run::get $region, $service, %opts | → { region, service, resource } |
| GCP::Run::revisions $region, $service, %opts | → @{ {name, create_time} } |
use GCP::Functions
| GCP::Functions::list %opts | → @{ {name, state, url} } — opt region (default "-" = all) |
| GCP::Functions::describe $region, $function, %opts | → { region, function, resource } |
use GCP::Logging
| GCP::Logging::entries %opts | → @{ {timestamp, severity, log_name, text_payload, json_payload} } — opts filter, page_size (50), order (asc/desc) |
| GCP::Logging::write $log, %opts | → { log, written } — opts text | json, severity |
| GCP::Logging::logs %opts | → @log_ids |
use GCP::Monitoring
| GCP::Monitoring::time_series $filter, $start, $end, %opts | → @series — interval is RFC3339 |
| GCP::Monitoring::descriptors %opts | → @{ {type, display_name, kind, value_type} } — opt filter |
use GCP::IAM
| GCP::IAM::get_policy $url, %opts | → \%policy — full :getIamPolicy endpoint |
| GCP::IAM::test_permissions $url, \@perms, %opts | → @granted — full :testIamPermissions endpoint |
| GCP::IAM::service_accounts %opts | → @{ {email, display_name, unique_id} } |
| GCP::IAM::service_account $email, %opts | → { email, resource } |
Examples
Runnable .stk scripts ship under examples/. Each is read-only or eval-guarded so it exits cleanly when ADC is unreachable.
| discover.stk | s examples/discover.stk — version + ADC source + identity + bucket count tour |
| whoami.stk | s examples/whoami.stk — ADC identity + visible GCS bucket summary |
| gcs_browse.stk | s examples/gcs_browse.stk gs://bucket/prefix/ — list a GCS prefix |
| gcs_put_get.stk | s examples/gcs_put_get.stk gs://bucket/key — round-trip a string through GCS |
| pubsub_pump.stk | s examples/pubsub_pump.stk my-sub — long-poll a Pub/Sub subscription |
# pubsub_pump.stk core — pull → callback → ack, 5 iterations
val $n = GCP::PubSub::pump($sub,
iterations => 5,
max => 10,
callback => sub ($m) {
p "got $m->{message_id}: $m->{data}"
})
p "handled $n messages"
Tests & dev workflow
cargo test # compiles, no live calls s test t/ # ADC-aware end-to-end # opt into per-service round-trips export STRYKE_GCP_TEST_BUCKET=my-test-bucket export STRYKE_GCP_TEST_TOPIC=my-test-topic export STRYKE_GCP_TEST_SUB=my-test-sub s test t/ make # release build make debug make test make install make clean
The suite skips cleanly when the cdylib isn't installed, when ADC isn't reachable, or when the per-service env vars are unset.
Why a package, not a builtin
GCP integration needs an auth chain (Application Default Credentials), a TLS HTTP client, and JSON serialization — fine to bundle once, opt-in. The cdylib talks to GCP's REST APIs directly via reqwest + google-cloud-auth.
The stryke side is a thin .stk wrapper that calls gcp__* FFI symbols on the cdylib; the heavy code lives in libstryke_gcp.{dylib,so}, dlopened in-process on first use GCP. Core stryke is never linked against this package's deps.
FFI layer
Each GCP::* wrapper builds a JSON args dict and calls a sibling gcp__* symbol resolved out of libstryke_gcp.{dylib,so}. The cdylib is dlopened in-process on first use GCP; a shared tokio runtime + reqwest::Client + cached ADC credentials are held in OnceCell — no fork-per-call, no re-running of ADC discovery / metadata-server / WIF / SA-file lookup on each call. Responses are JSON; errors come back as {"error": "<msg>"} and the wrapper dies with the message.
Layout
stryke-gcp/ ├── Cargo.toml # cdylib crate manifest (crate-type = ["cdylib"], publish = false) ├── src/ │ └── lib.rs # cdylib — gcp__* extern "C" exports ├── lib/ # stryke .stk wrapper(s) — `use GCP`, `use GCP::Storage`, … ├── stryke.toml # stryke package manifest ├── t/ # zunit-style tests ├── examples/ # runnable .stk examples ├── Makefile # `make install` builds + installs └── 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-fleet — parallel expect/PTY automation
- 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