Greetings OCaml enjoyers,
Have you been annoyed with terraform plan telling you 10 minutes later that you have a typo? Are you frustrated with how type-ambiguous your cloud is? (Abe Simpson shaking fist at cloud)
Or, maybe you wish you could have AWS libraries inside an ecosystem you trust.
Well, this release is for you!
I’m happy to announce awso, a comprehensive AWS library. This was forked from https://github.com/solvuu/awsm, which was never released to opam. awso provides typed OCaml bindings to AWS services generated from botocore’s service definitions, so the compiler catches the typos and shape mismatches.
The awso repo: GitHub - mbacarella/ocaml-awso: OCaml bindings for all of AWS · GitHub
What’s in the tin
Typed clients for the full AWS surface area, generated from botocore 1.43.9
Four I/O backends:
awso-eio- Eioawso-async— Asyncawso-lwt— Lwtawso-sync— Synchronous (blocking) over libcurl, for easy scripting and CLIs
Also
awso-cli: for fun, a kitchen-sink binary exposing every service as a composable subcommand, in the spirit of the Python aws CLI. (Ships as bytecode because linking ~400 native service libraries exceeds ARM64 executable size limits; yes really)
Note: the AWS API is big. opam install may take awhile.
An example using Eio
(* ec2_describe_instances.ml *)
module Ec2 = Awso_ec2_eio
let print_row a b c d = Printf.printf "%-16s %-15s %-39s %-20s\n" a b c d
let print_instance instance =
let name =
Option.bind instance.Ec2.Instance.tags (fun tags ->
List.find_map
(function
| { Ec2.Tag.key = Some "Name"; value = Some v } -> Some v
| _ -> None)
tags)
in
let instance_type =
match instance.instanceType with
| Some it -> Ec2.InstanceType.to_string it
| None -> ""
in
print_row
instance_type
(Option.value instance.publicIpAddress ~default:"")
(Option.value instance.ipv6Address ~default:"")
(Option.value name ~default:"")
;;
let main env =
let cfg = Awso_eio.Cfg.get_exn ~env () in
match Ec2.describe_instances ~cfg (Ec2.DescribeInstancesRequest.make ()) with
| Error e ->
failwith
(Printf.sprintf
"Ec2.describe_instances: %s"
(Yojson.Safe.to_string (Ec2.Ec2_error.to_json e)))
| Ok { reservations; _ } -> (
let instances =
reservations
|> Option.value ~default:[]
|> List.concat_map (function
| { Ec2.Reservation.instances = None; _ } -> []
| { instances = Some instances; _ } -> instances)
in
match instances with
| [] -> print_endline "no instances"
| instances ->
print_row "instance-type" "public ipv4" "public ipv6" "name";
print_row
(String.make 16 '-')
(String.make 15 '-')
(String.make 39 '-')
(String.make 20 '-');
List.iter print_instance instances)
;;
let () = Eio_main.run main
Major changes in the fork from awsm
- Replaced lightweight higher-kinded polymorphism with a functorized approach (architectural detail: end users don’t need to use functors!)
- Restructured into
aws/{eio,async,lwt,sync}/so each backend ships only what it needs - Consolidated per-service opam packages into sub-libraries under each backend, on advice from the opam-repository crew. opam install awso-async pulls in every service binding as awso-async.
- Transport errors now raise instead of polluting the Result type; only AWS-side errors stay in Result, matching Async convention
- Dropped Jane Street Core from the non-Async runtimes via a small Jane_compat shim; Yojson.Safe.t everywhere instead of ad-hoc JSON; Base is still required for some rewriters and other tooling, but still much lighter weight than all of Core
- Codegen is committed to the tree, so
opam installdoesn’t drag ~25 build-time packages into your dependency cone - Output shapes treat required as advisory, because AWS itself routinely omits fields it marks required (looking at you, AccessDeniedException with no Message). Input shapes still respect it.
- Various working examples here
Minimum OCaml 5.3.0 to install from opam, OCaml 4.14 if you bump your stack limit before building.
See CHANGES.md for the full list, and TODO.md for things known to still be rough.
A bit of history
OCaml’s AWS bindings have a lineage that predates this fork by years, with substantial development at Solvuu and collaboration from Tarides. Special thanks to Jane Street for their contributions along the way. It was open-sourced a while back and has sat quietly since.
awso is an attempt to dust it off, bring it forward to current OCaml, and give it a home where the community can utilize it. Genuine thanks to everyone whose work this builds on! There’s a lot of good engineering under the hood that deserves to keep running.
Help me get the credits right
The code in the public solvuu repo was copy/pasted over from an internal repository, so the public git history doesn’t reflect everyone who contributed. If you worked on any earlier version and want to be credited, please reach out.
Where it’s going
0.9.1 is meant as a release-candidate-quality baseline ahead of a 1.0.0. Near-term: working through TODO.md and more working examples. Issues and PRs welcome, particularly bug reports from real workloads.
It works on 4.14 but you need to increase your stack size a bit to work around some non-TCO parts of the OCaml compiler. I’m trying to see if we can chunk things up differently to get it through opam-ci for OCaml 4.14.
Repo: GitHub - mbacarella/ocaml-awso: OCaml bindings for all of AWS · GitHub
opam: opam install awso-eio (or -lwt, or -async, or -sync)
Bear with me while I spin some things in TODO.md off into GitHub issues. But I’m also curious to hear what might be missing from this release for you.
AI assistance disclosure
Most of this work is directly coded by multiple humans over several years. A bulk of my own work in the project was back in 2022 porting it to ppxlib, making it work in OCaml 5 and growing the supported services from a handful to hundreds (bugfixes, missing support, working around botocore spec errors). Most of this work happened before the age of LLMs. Remember when “generated code” meant generated by OCaml? This guy remembers.
I’ve since recruited Claude Code with Opus 4.7 for the push towards an opam release: mostly in refactoring, catching up on the latest botocore spec, and reducing the dependency cone. I hereby declare I understand and can answer for every line of code in awso.
Closing
I’m happy to hear feedback, especially on API ergonomics.
<3 Michael