AWS Core Guides

The core package is a pure foundation plus explicit runtime adapters at the edge. Service packages use this foundation for signing, request construction, response metadata, and structured errors.

Credentials

Create explicit credentials with result-returning or exception-raising constructors:

let credentials =
  Awskit.Credentials.create_exn
    ~access_key_id:"AKIAIOSFODNN7EXAMPLE"
    ~secret_access_key:"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
    ()

Temporary credentials include a session token:

let credentials =
  Awskit.Credentials.create_exn
    ~access_key_id:"ASIATESTSESSIONTOKEN"
    ~secret_access_key:"secret"
    ~session_token:"FwoGZXIvYXdz..."
    ()

Credentials are opaque. Use Awskit.Credentials.access_key_id and Awskit.Credentials.session_token to read non-secret fields. The secret access key is accepted at construction time but not exposed afterward.

Environment Variables

awskit-unix and awskit-lwt-unix can read:

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_SESSION_TOKEN
AWS_REGION
AWS_DEFAULT_REGION
AWS_PROFILE
AWS_SHARED_CREDENTIALS_FILE
AWS_CONFIG_FILE
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
AWS_CONTAINER_CREDENTIALS_FULL_URI
AWS_CONTAINER_AUTHORIZATION_TOKEN
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE

awskit-unix also reads static shared AWS profiles from $HOME/.aws/credentials and $HOME/.aws/config. Named profiles use AWS_PROFILE, defaulting to "default". Environment credentials win over profile files when present.

awskit-lwt-unix extends the local static sources with refreshable ECS container credentials and EC2 instance profile credentials. The EC2 provider uses IMDSv2 when available and refreshes metadata credentials before expiration. Process providers, web identity, and STS assume-role profiles remain future work.

Regions And Endpoints

Regions are explicit values:

let region = Awskit.Region.of_string_exn "us-east-1"

Endpoints are scheme/host/port records:

let endpoint =
  Awskit.Endpoint.https
    ~host:"s3.us-east-1.amazonaws.com"
    ()

Runtime adapters can take endpoint overrides, but service-specific packages usually expose their own endpoint configuration because addressing rules are service-specific. For S3, pass endpoint and addressing options directly to the awskit-s3 adapter create functions.

Signing

Awskit.Signing implements AWS Signature Version 4 without IO.

Use payload_hash metadata instead of passing a raw body to signing:

let signed =
  Awskit.Signing.sign_request
    ~credentials
    ~region
    ~service:"s3"
    ~method_:`PUT
    ~path:"/my-key"
    ~query:""
    ~headers:[ ("host", "my-bucket.s3.us-east-1.amazonaws.com") ]
    ~payload_hash:(Awskit.Body.Payload_hash.sha256_of_string "hello")
    ~now:Ptime.epoch

For presigned or streaming cases, service packages may use explicit payload hash values such as UNSIGNED-PAYLOAD.

Useful low-level helpers:

Structured Errors

Core errors are structured values:

type t =
  | Validation of validation
  | Signing of string
  | Transport of transport
  | Service of service
  | Decode of string
  | Body of body

Use Awskit.Error.retry_class for manual retry decisions:

match Awskit.Error.retry_class err with
| `Throttled | `Retryable -> retry ()
| `Auth -> refresh_or_fail ()
| `Not_found -> handle_missing ()
| `Conflict | `Fatal | `Unknown -> fail err

Service packages preserve Awskit.Error.t and add classifier helpers. For example, awskit-s3 exposes Awskit_s3.Error.is_no_such_key and Awskit_s3.Error.is_precondition_failed.

Retry Policy

Service packages use Awskit.Retry for built-in retries. The default policy retries retryable transport failures, throttling responses, and 5xx service responses when the request body is replayable.

let retry_policy =
  Awskit.Retry.create_exn
    ~max_attempts:4
    ~base_delay:(Ptime.Span.of_float_s 0.2 |> Option.get)
    ~max_delay:(Ptime.Span.of_float_s 3.0 |> Option.get)
    ~jitter:0.5
    ()

Use Awskit.Retry.disabled when an application wants to own all retry behavior itself.

Runtime Body Model

The runtime abstraction keeps HTTP metadata separate from bodies:

This lets service packages stream bodies without forcing all adapters to buffer data in memory.

Request bodies carry a descriptor:

{
  Awskit.Body.Request.content_length = Some 5L;
  payload_hash = Awskit.Body.Payload_hash.sha256_of_string "hello";
  replayable = true;
}

The descriptor records the declared request body length, the payload hash used for signing, and whether the body can be replayed. Service packages may reject unknown lengths. For example, S3 PutObject and UploadPart currently require a known content_length; Awskit does not implement SigV4 aws-chunked streaming for unknown-length request bodies.

For custom Runtime.Request_body.of_stream values, the producer callback is part of the request operation. Errors returned by that callback are request body failures. When a known length is declared, the producer must emit exactly that number of bytes; prefer Runtime.Request_body.of_string or Runtime.Request_body.of_bytes when data is already buffered. Mark a custom stream replayable only if a retry can recreate the same body.

Response bodies must be consumed inside the with_response callback, normally through Runtime.Response_body.with_reader:

Runtime.with_response conn request body ~f:(fun response body ->
  Runtime.Response_body.with_reader body ~consume:(fun reader ->
    Runtime.Response_body.read reader bytes ~off:0 ~len:(Bytes.length bytes)))

Service packages expose higher-level helpers when buffering is intentional. For example, awskit-s3 uses Object.get_as_string ~max_bytes. These helpers keep response-size limits explicit when buffering or draining a streaming response.

Response body readers are scoped. After the consumer returns successfully, the runtime drains the remaining response body up to max_response_drain_bytes so the connection can be reused. If the consumer succeeds but draining exceeds the cap, the operation returns the drain error. If the consumer itself fails, the consumer error wins.

Choosing An Adapter

Eio

Use awskit-eio for direct-style OCaml 5 applications:

Eio.Switch.run @@ fun sw ->
let conn =
  Awskit_eio.create
    ~env
    ~sw
    ~region
    ~credentials
    ()
in
ignore conn

The monad type is type 'a t = 'a. Errors are returned as result values by service operations.

Lwt Unix

Use awskit-lwt-unix for ordinary Lwt applications on Unix:

let conn =
  Awskit_lwt_unix.create
    ~region:"us-east-1"
    ~credentials
    ()
  |> Result.get_ok

Operations in service packages return Lwt.t.

Generic Lwt

Use awskit-lwt when you need to provide a custom Cohttp_lwt.S.Client:

module Runtime = Awskit_lwt.Make (Cohttp_lwt_unix.Client)

Custom Runtime Adapters

Implement Awskit.Runtime.S when integrating another concurrency or HTTP stack. The service package functors need:

S3 adapters extend the core runtime with s3_endpoint_config so S3 endpoint resolution can stay AWS-aware without putting S3 concepts into the pure core.