aws-s3

AWS S3 client for Kit

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/.keepPlaceholder for generated documentation output
examples/basic.kitBasic S3 usage example using environment credentials
examples/minio.kitS3-compatible storage example using MinIO
kit-lock.jsonLocked package dependency versions
kit.tomlPackage manifest with metadata, capabilities, tasks, and dependencies
src/s3.kitS3 client implementation, request helpers, XML parsing, and convenience APIs
tests/aws-s3.test.kitPublic type and record behavior tests
tests/error-types.test.kitS3 error type, Show, Error, and record tests

Dependencies

  • aws-core - AWS credentials, region helpers, URL encoding, and Signature Version 4 signing

Capabilities

This package declares the net capability in kit.toml because S3 operations send signed HTTP requests.

The type definitions, client configuration helpers, and XML parsing helpers can be type checked without making network calls. Object and bucket operations require network access at runtime.

Installation

kit add gitlab.com/kit-lang/packages/kit-aws-s3.git

Usage

Load credentials and region from the environment:

import Kit.AwsS3 as S3

main = fn =>
  match S3.client-from-env
    | Ok s3 ->
      bucket = "my-test-bucket"

      match S3.put-object s3 bucket "hello.txt" "Hello, S3!"
        | Ok _ ->
          println "Uploaded hello.txt"
        | Err err ->
          println "Upload failed: ${err}"

      match S3.get-object s3 bucket "hello.txt"
        | Ok content ->
          println "Downloaded: ${content}"
        | Err err ->
          println "Download failed: ${err}"

      match S3.list-objects s3 bucket "" 100
        | Ok result ->
          println "Found ${Int.to-string (List.length result.objects)} object(s)"
        | Err err ->
          println "List failed: ${err}"

    | Err err ->
      println "Could not create S3 client: ${err}"

main

Create a client explicitly:

import Kit.AwsCore as AWS
import Kit.AwsS3 as S3

main = fn =>
  creds = AWS.credentials "AKIAEXAMPLE" "secretkey123"
  region = AWS.region "us-east-1"
  s3 = S3.client creds region

  if S3.exists? s3 "my-bucket" "hello.txt" then
    println "hello.txt exists"
  else
    println "hello.txt does not exist"

main

Use an S3-compatible endpoint such as MinIO or LocalStack:

import Kit.AwsCore as AWS
import Kit.AwsS3 as S3

main = fn =>
  creds = AWS.credentials "minioadmin" "minioadmin"
  region = AWS.region "us-east-1"

  # MinIO normally requires path-style URLs.
  s3 = S3.client-with-endpoint creds region "http://localhost:9000" true

  match S3.create-bucket s3 "test-bucket"
    | Ok _ -> println "Bucket ready"
    | Err err -> println "Create bucket failed: ${err}"

main

Configuration

S3.client-from-env reads these environment variables:

VariableRequiredDescription
AWS_ACCESS_KEY_IDYesAWS access key ID
AWS_SECRET_ACCESS_KEYYesAWS secret access key
AWS_SESSION_TOKENNoSession token for temporary credentials
AWS_REGIONYes, unless AWS_DEFAULT_REGION is setPreferred AWS region
AWS_DEFAULT_REGIONFallbackRegion used when AWS_REGION is not set
AWS_ENDPOINT_URLNoCustom S3-compatible endpoint, such as http://localhost:9000
AWS_S3_PATH_STYLENoSet to true or 1 to use path-style bucket URLs

Path-style URLs look like https://s3.us-east-1.amazonaws.com/bucket/key.

Virtual-hosted-style URLs look like https://bucket.s3.us-east-1.amazonaws.com/key.

API Overview

Object operations:

  • get-object downloads object content.
  • put-object uploads content as application/octet-stream.
  • put-object-with-type uploads content with a custom content type.
  • delete-object deletes an object.
  • head-object checks object metadata through a HEAD request.
  • exists? returns a boolean object-existence check.
  • copy-object performs a server-side copy.

Bucket operations:

  • list-objects lists objects with optional prefix and max-key controls.
  • list-objects-continue fetches a paginated continuation page.
  • create-bucket creates a bucket.
  • delete-bucket deletes an empty bucket.

File helpers:

  • upload-file reads a local file and uploads it to S3.
  • download-file downloads an object and writes it to a local file.

Architecture

Request Flow

sequenceDiagram participant App participant S3Client participant AwsCore participant HTTP participant S3API App->>S3Client: put-object / get-object / list-objects S3Client->>AwsCore: Sign request with SigV4 AwsCore->>S3Client: Signed method, URL, headers, body S3Client->>HTTP: Execute signed HTTP request HTTP->>S3API: HTTPS request S3API->>HTTP: HTTP response HTTP->>S3Client: Result response S3Client->>App: Result value

Package Dependencies

graph TD A[kit-aws-s3] --> B[kit-aws-core] A --> C[HTTP] B --> D[Credentials] B --> E[Region helpers] B --> F[SigV4 signing]

Development

Running Examples

Run examples with the interpreter:

kit run examples/basic.kit
kit run examples/minio.kit

Compile an example to a native binary:

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

The examples import ../src/s3.kit so they exercise the local workspace source during development.

The MinIO example expects a MinIO-compatible service at http://localhost:9000. Without one, the example should still handle the connection failure and print S3 error messages instead of crashing.

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running Parity

Check interpreter and compiled output parity for examples:

kit parity --no-spinner --failures-only

This verifies that kit run and kit build produce matching output for the example programs.

Running kit dev

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

kit dev

This will:

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

Generating Documentation

Generate API documentation from doc comments:

kit doc src/s3.kit

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

Cleaning Build Artifacts

Remove generated files, caches, coverage reports, parity reports, 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/aws-s3/, making it available for import as Kit.AwsS3 in other projects.

When editing this package, run examples through the local examples/ files or reinstall with kit install before testing another project that imports Kit.AwsS3.

License

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

Exported Functions & Types

AWSS3Error

S3 error type with specific variants for different failure modes.

Variants

AWSS3ObjectError {message}
Object-related error (get, put, delete, head, copy operations).
AWSS3BucketError {message}
Bucket-related error (list, create, delete bucket operations).

S3Client

S3 client configuration.

Fields: - credentials: AWS credentials for authentication - region: AWS region for the S3 service - endpoint: Custom endpoint URL (empty string for AWS S3) - path-style: Whether to use path-style URLs (bucket/key) instead of virtual-hosted-style (bucket.s3.region.amazonaws.com/key)

Variants

S3Client {credentials, region, endpoint, path-style}

ObjectInfo

Metadata for an S3 object.

Fields: - key: The object's key (path) in the bucket - size: Size of the object in bytes - last-modified: ISO 8601 timestamp of last modification - etag: Entity tag (version identifier) of the object

Variants

ObjectInfo {key, size, last-modified, etag}

ListObjectsResponse

Response from listing objects in a bucket.

Fields: - objects: List of objects matching the query - is-truncated: Whether more results are available - next-continuation-token: Token for fetching next page (empty if none)

Variants

ListObjectsResponse {objects, is-truncated, next-continuation-token}

client

Create S3 client for AWS.

Creates a client configured for standard AWS S3 service using virtual-hosted-style URLs.

Parameters:

Returns:

AWS.Credentials -> AWS.Region -> S3Client

creds = AWS.credentials "AKIAIOSFODNN7EXAMPLE" "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
region = {name: "us-west-2"}
s3 = S3.client creds region

client-with-endpoint

Create S3 client with custom endpoint.

Creates a client for S3-compatible storage services like MinIO or LocalStack. Supports both path-style and virtual-hosted-style URLs.

Parameters:

Returns:

AWS.Credentials -> AWS.Region -> String -> Bool -> S3Client

creds = AWS.credentials "minioadmin" "minioadmin"
region = {name: "us-east-1"}
s3 = S3.client-with-endpoint creds region "http://localhost:9000" true

client-from-env

Create client from environment variables.

Reads credentials and configuration from: - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: AWS credentials - AWS_REGION: AWS region - AWS_ENDPOINT_URL: Custom endpoint (optional) - AWS_S3_PATH_STYLE: "true" or "1" for path-style URLs (optional)

Returns:

() -> Result S3Client String

match S3.client-from-env()
  | Ok client -> print "Client created successfully"
  | Err err -> print "Failed: ${err}"

get-object

Get object from S3.

Downloads the content of an object from an S3 bucket.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> Result String AWSS3Error

match S3.get-object client "my-bucket" "data/file.txt"
  | Ok content -> print content
  | Err err -> print "Error: ${err}"

put-object

Put object to S3.

Uploads content to an S3 bucket with default content type.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> String -> Result () AWSS3Error

match S3.put-object client "my-bucket" "data.txt" "Hello, S3!"
  | Ok _ -> print "Upload successful"
  | Err err -> print "Error: ${err}"

put-object-with-type

Put object with specific content type.

Uploads content to an S3 bucket with a custom content type header.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> String -> String -> Result () AWSS3Error

json = "{\"name\": \"example\"}"
match S3.put-object-with-type client "my-bucket" "data.json" json "application/json"
  | Ok _ -> print "Upload successful"
  | Err err -> print "Error: ${err}"

delete-object

Delete object from S3.

Removes an object from an S3 bucket. Returns success even if the object doesn't exist (S3 DELETE is idempotent).

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> Result () AWSS3Error

match S3.delete-object client "my-bucket" "old-file.txt"
  | Ok _ -> print "Deleted successfully"
  | Err err -> print "Error: ${err}"

head-object

Check if object exists using HEAD request.

Performs a HEAD request to check for object existence without downloading the content.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> Result Bool AWSS3Error

match S3.head-object client "my-bucket" "test.txt"
  | Ok true -> print "Object exists"
  | Ok false -> print "Object not found"
  | Err err -> print "Error: ${err}"

exists?

Check if object exists (convenience function).

Simplified wrapper around head-object that returns a boolean.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> Bool

if S3.exists? client "my-bucket" "test.txt" then
  print "File exists"
else
  print "File not found"

list-objects

List objects in bucket.

Retrieves a list of objects from an S3 bucket with optional prefix filtering. Uses S3 ListObjectsV2 API.

Parameters:

Returns:

S3Client -> NonEmptyString -> String -> Int -> Result ListObjectsResponse AWSS3Error

match S3.list-objects client "my-bucket" "logs/" 100
  | Ok response ->
    print "Found ${List.length response.objects} objects"
    if response.is-truncated then
      print "More results available"
  | Err err -> print "Error: ${err}"

list-objects-continue

List objects with continuation token (for pagination).

Continues a previous list-objects call using a pagination token.

Parameters:

Returns:

S3Client -> NonEmptyString -> String -> Int -> String -> Result ListObjectsResponse AWSS3Error

# Get first page
match S3.list-objects client "my-bucket" "" 1000
  | Ok response ->
    if response.is-truncated then
      # Get next page
      match S3.list-objects-continue client "my-bucket" "" 1000 response.next-continuation-token
        | Ok next-page -> print "Got next page"
        | Err err -> print "Error: ${err}"
  | Err err -> print "Error: ${err}"

create-bucket

Create S3 bucket.

Creates a new S3 bucket in the configured region. For regions other than us-east-1, includes a LocationConstraint in the request body.

Parameters:

Returns:

S3Client -> NonEmptyString -> Result () AWSS3Error

match S3.create-bucket client "my-new-bucket"
  | Ok _ -> print "Bucket created"
  | Err "Bucket already exists" -> print "Bucket exists"
  | Err err -> print "Error: ${err}"

delete-bucket

Delete S3 bucket.

Deletes an S3 bucket. The bucket must be empty before deletion.

Parameters:

Returns:

S3Client -> NonEmptyString -> Result () AWSS3Error

match S3.delete-bucket client "old-bucket"
  | Ok _ -> print "Bucket deleted"
  | Err "Bucket not empty" -> print "Delete objects first"
  | Err err -> print "Error: ${err}"

upload-file

Upload file from local filesystem to S3.

Reads a file from the local filesystem and uploads it to S3 with default content type (application/octet-stream).

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> NonEmptyString -> Result () AWSS3Error

match S3.upload-file client "my-bucket" "data/report.pdf" "/tmp/report.pdf"
  | Ok _ -> print "File uploaded"
  | Err err -> print "Error: ${err}"

download-file

Download file from S3 to local filesystem.

Downloads an object from S3 and writes it to the local filesystem.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> NonEmptyString -> Result () AWSS3Error

match S3.download-file client "my-bucket" "data/report.pdf" "/tmp/report.pdf"
  | Ok _ -> print "File downloaded"
  | Err err -> print "Error: ${err}"

copy-object

Copy object within S3.

Copies an object from one location to another within S3. The source and destination can be in the same or different buckets. This is a server-side operation that doesn't download the object.

Parameters:

Returns:

S3Client -> NonEmptyString -> NonEmptyString -> NonEmptyString -> NonEmptyString -> Result () AWSS3Error

match S3.copy-object client "src-bucket" "old/file.txt" "dest-bucket" "new/file.txt"
  | Ok _ -> print "Object copied"
  | Err err -> print "Error: ${err}"