gcp-core

GCP core library - authentication, project config, and shared utilities

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and dependencies
.tool-versionsasdf tool versions (Zig, Kit)
LICENSEMIT license file
README.mdThis file
docs/.keepKeeps the generated documentation directory in the package
examples/basic.kitBasic authentication and Cloud Storage request example
examples/with-token.kitExample: using an explicit access token
kit.tomlPackage manifest with metadata, capabilities, and tasks
src/gcp.kitGCP authentication, token management, request helpers, and OAuth scopes
tests/gcp-core.test.kitTests for GCP core behavior
tests/types.test.kitTests for exported GCP types
zig/gcp_rsa.zigZig FFI implementation for RS256 service-account JWT signing
zig/kit_ffi.zigKit FFI support helpers for Zig externs

Dependencies

No Kit package dependencies.

This package uses Zig FFI and links against OpenSSL libcrypto for RS256 service-account JWT signing.

Installation

kit add gitlab.com/kit-lang/packages/kit-gcp-core.git

Usage

import Kit.GcpCore as GCP

main = fn =>
  match GCP.client-from-env
    | Err e ->
      println "Authentication failed: ${e}"
    | Ok client ->
      println "Project ID: ${client.project-id}"

      url = "https://storage.googleapis.com/storage/v1/b?project=${client.project-id}"

      match GCP.get client url
        | Ok response ->
          if response.status == 200 then
            println response.body
          else
            println "HTTP ${Int.to-string response.status}: ${response.body}"
        | Err e ->
          println "Request failed: ${e}"

main

client-from-env checks the GCP metadata server first, then falls back to environment-based credentials. For service-account credentials, set GOOGLE_APPLICATION_CREDENTIALS to a service account JSON file.

Development

Running Examples

Run examples with the interpreter:

kit run examples/basic.kit

Run the explicit token example:

GCP_PROJECT_ID=your-project-id GCP_ACCESS_TOKEN="$(gcloud auth print-access-token)" kit run examples/with-token.kit

Compile examples to a native binary:

kit build examples/basic.kit && ./basic

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow (format, check, test):

kit dev

This will:

  1. Format and check source files in src/
  2. Run tests in tests/ with coverage

Running Parity Checks

Run interpreter/native parity checks for examples:

kit parity --failures-only

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in docs/*.html

Cleaning Build Artifacts

Remove generated files, caches, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/gcp-core/, making it available for import as Kit.GcpCore in other projects.

License

This package is released under the MIT License - see LICENSE for details.

Exported Functions & Types

GCPError

GCP error type with specific variants for different failure modes.

Variants

GCPAuthError {message}
Authentication/authorization failed (token issues).
GCPCredentialError {message}
Problem loading or parsing credentials.
GCPNotFoundError {message, resource}
Resource not found (HTTP 404).
GCPPermissionDeniedError {message, resource}
Permission denied (HTTP 403).
GCPRateLimitError {message, retry-after-ms}
Rate limit exceeded (HTTP 429).
GCPQuotaExceededError {message, quota-metric}
Quota exceeded (HTTP 429 with quota reason).
GCPInvalidRequestError {message, field}
Invalid request parameters (HTTP 400).
GCPConflictError {message, resource}
Resource conflict (HTTP 409).
GCPPreconditionFailedError {message}
Precondition failed (HTTP 412).
GCPServerError {message, status}
Server error (HTTP 5xx).
GCPNetworkError {message}
Network/connection error.
GCPTimeoutError {message}
Request timeout.
GCPGenericError {message, status}
Generic error for other cases.

is-retryable?

Check if an error is retryable.

Returns true for transient errors that may succeed on retry: - Rate limit errors (with backoff) - Server errors (5xx) - Network errors - Timeout errors

GCPError -> Bool

error-from-status

Create appropriate error from HTTP status code.

Parameters:

Returns:

Int -> String -> String -> GCPError

with-retry

Retry a function with exponential backoff for retryable errors.

Executes the given function and retries if it fails with a retryable error. Uses exponential backoff starting at 100ms, doubling each retry up to max-delay-ms. For rate limit errors, uses the retry-after-ms hint if available.

Parameters:

Returns:

(() -> Result a GCPError) -> Int -> Result a GCPError

# Retry up to 3 times
match GCP.with-retry (fn() => GCP.get client url) 3
  | Ok response -> println "Success!"
  | Err e -> println "Failed after retries: ${show e}"

with-retry-backoff

Retry with custom backoff configuration.

Parameters:

(() -> Result a GCPError) -> Int -> Int -> Int -> Result a GCPError

# Start with 500ms delay, max 30 seconds
GCP.with-retry-backoff (fn() => GCP.get client url) 5 500 30000

load-service-account

Load service account credentials from JSON key file content.

Parses a Google Cloud service account JSON key file and extracts the credentials needed for authentication. The JSON should be in the format downloaded from the GCP Console (IAM & Admin > Service Accounts).

Parameters:

Returns:

String -> Result ServiceAccount GCPError

json = File.read "service-account.json"
match load-service-account json
  | Ok sa -> println "Loaded account: ${sa.client-email}"
  | Err err -> println "Error: ${err}"

Errors:
- Returns Err if required fields (project_id, private_key, client_email, token_uri) are missing

load-service-account-file

Load service account from file path.

NonEmptyString -> Result ServiceAccount GCPError

load-service-account-from-env

Load from GOOGLE_APPLICATION_CREDENTIALS environment variable.

() -> Result ServiceAccount GCPError

get-project-id-from-env

Get project ID from environment variables.

Checks the following environment variables in order: 1. GOOGLE_CLOUD_PROJECT (standard for Google Cloud SDK) 2. CLOUDSDK_CORE_PROJECT (gcloud CLI config) 3. GCP_PROJECT (common alternative) 4. GCLOUD_PROJECT (legacy)

Returns:

() -> Option String

match GCP.get-project-id-from-env()
  | Some project -> println "Project: ${project}"
  | None -> println "No project ID set"

get-access-token

Get access token for a service account with specific scopes.

ServiceAccount -> [String] -> Result AccessToken GCPError

client-with-token

Create GCP client with explicit access token.

NonEmptyString -> NonEmptyString -> GcpClient

client-from-metadata

Create client from metadata server (GCE, Cloud Run, GKE).

() -> Result GcpClient GCPError

client-from-env

Create client from environment. If GOOGLE_APPLICATION_CREDENTIALS is set, use it directly (local development). Otherwise try metadata server first (GCE, Cloud Run, GKE), then fall back to env var.

() -> Result GcpClient GCPError

client-from-service-account

Create a GCP client from service account credentials with specified scopes.

This authenticates using the service account's private key to sign a JWT, then exchanges the JWT for an access token via the OAuth 2.0 token endpoint.

Parameters:

Returns:

ServiceAccount -> [String] -> Result GcpClient GCPError

json = File.read "service-account.json"
match load-service-account json
  | Ok sa ->
    match client-from-service-account sa [scope-storage-full]
      | Ok client -> # Use client for storage operations
      | Err e -> println "Auth failed: ${Show.show e}"
  | Err e -> println "Load failed: ${Show.show e}"

is-token-expired?

Check if token is expired (with 5 minute buffer).

Returns true if the token will expire within 5 minutes, giving time to refresh before actual expiration.

Parameters:

Returns:

GcpClient -> Bool

if GCP.is-token-expired? client then
  match GCP.refresh-token client
    | Ok new-client -> # use new-client
    | Err e -> println "Refresh failed: ${e}"

refresh-token

Refresh access token using the appropriate method.

Automatically detects how to refresh based on the client configuration: - If the client has service account credentials (private key), uses those to generate a new token with the cloud-platform scope. - Otherwise, attempts to use the metadata server (for GCE/Cloud Run/GKE).

Parameters:

Returns:

GcpClient -> Result GcpClient GCPError

if GCP.is-token-expired? client then
  match GCP.refresh-token client
    | Ok refreshed -> do-something refreshed
    | Err e -> println "Refresh failed: ${e}"

refresh-token-with-scopes

Refresh access token with specific OAuth scopes.

Use this when you need specific scopes instead of the default cloud-platform scope.

Parameters:

Returns:

GcpClient -> [String] -> Result GcpClient GCPError

scopes = [GCP.scope-storage-full, GCP.scope-bigquery]
match GCP.refresh-token-with-scopes client scopes
  | Ok refreshed -> do-something refreshed
  | Err e -> println "Refresh failed: ${e}"

ensure-valid-token

Ensures the client has a valid (non-expired) token.

If the token is expired or about to expire, automatically refreshes it. If the token is still valid, returns the client unchanged.

This is useful as a wrapper before making API calls to ensure the token is always valid.

Parameters:

Returns:

GcpClient -> Result GcpClient GCPError

match GCP.ensure-valid-token client
  | Ok valid-client ->
    match GCP.get valid-client some-url
      | Ok response -> # handle response
      | Err e -> println "Request failed: ${e}"
  | Err e -> println "Token refresh failed: ${e}"

ensure-valid-token-with-scopes

Ensures the client has a valid token with specific scopes.

Like ensure-valid-token, but allows specifying the scopes for refresh.

Parameters:

Returns:

GcpClient -> [String] -> Result GcpClient GCPError

get

Make authenticated GET request.

GcpClient -> NonEmptyString -> Result HTTP.Response HTTP.Error

post

Make authenticated POST request.

GcpClient -> NonEmptyString -> String -> Result HTTP.Response HTTP.Error

put

Make authenticated PUT request.

GcpClient -> NonEmptyString -> String -> Result HTTP.Response HTTP.Error

delete

Make authenticated DELETE request.

GcpClient -> NonEmptyString -> Result HTTP.Response HTTP.Error

request

Make authenticated request with custom headers.

GcpClient -> String -> NonEmptyString -> String -> [HTTP.Header] -> Result HTTP.Response HTTP.Error

scope-cloud-platform

Cloud Platform (broad access - use when you need multiple services).

String

scope-cloud-platform-readonly

String

scope-storage-read

Cloud Storage.

String

scope-storage-write

String

scope-storage-full

String

scope-bigquery

BigQuery.

String

scope-bigquery-readonly

String

scope-bigquery-insert

String

scope-compute

Compute Engine.

String

scope-compute-readonly

String

scope-pubsub

Pub/Sub.

String

scope-datastore

Firestore / Datastore.

String

scope-firestore

String

scope-sqlservice-admin

Cloud SQL.

String

scope-spanner-admin

Spanner.

String

scope-spanner-data

String

scope-secretmanager

Secret Manager.

String

scope-cloudfunctions

Cloud Functions.

String

scope-run

Cloud Run.

String

scope-run-admin

String

scope-tasks

Cloud Tasks.

String

scope-scheduler

Cloud Scheduler.

String

scope-iam

IAM.

String

scope-logging-read

Logging.

String

scope-logging-write

String

scope-logging-admin

String

scope-monitoring

Monitoring.

String

scope-monitoring-read

String

scope-monitoring-write

String

scope-cloudkms

Cloud KMS (Key Management Service).

String

scope-dns

Cloud DNS.

String

scope-dns-readonly

String

scope-containerregistry

Container Registry / Artifact Registry.

String

scope-artifactregistry

String

scope-container

Kubernetes Engine (GKE).

String

scope-aiplatform

Vertex AI / AI Platform.

String

scope-service-management

Service Management.

String

scope-service-management-readonly

String

scope-servicecontrol

Service Control.

String

scope-trace-append

Trace.

String

scope-trace-readonly

String

scope-source-read

Source Repositories.

String

scope-source-write

String

scope-sheets

Sheets (for data pipelines that use Sheets).

String

scope-sheets-readonly

String

scope-drive

Drive (for data pipelines that use Drive).

String

scope-drive-readonly

String

scope-drive-file

String

scope-gmail-send

Gmail (for automated email workflows).

String

scope-gmail-readonly

String

scope-calendar

Calendar.

String

scope-calendar-readonly

String

scope-userinfo-email

User info.

String

scope-userinfo-profile

String

scope-openid

String

url-encode

URL encode a string.

String -> String