Ankusa.BlobStore backed by S3 or any S3-compatible endpoint (MinIO,
Cloudflare R2, or the floci emulator in docker-compose.integration.yml).
Requests are signed with
aws_signature — the SigV4
implementation behind the official aws-elixir SDK — and sent with
Req. Signing is exactly the kind of code not
to hand-roll: a canonicalization bug is invisible until it fails in
production, and the failure mode is a signature mismatch on someone else's
infrastructure.
opts:
:bucket— required:region— required, e.g."us-east-1":access_key_id,:secret_access_key,:session_token— staticcredentials. Without them the chain in `Ankusa.AWS.Credentials` runs: the `AWS_*` environment, web identity (IRSA), then the EC2 instance role (IMDSv2):endpoint— default"https://s3.#{region}.amazonaws.com";point at `http://localhost:4566` for floci/MinIO:timeout_ms— default10_000, for both connect and response of onerequest that carries at most 1 MiB:upload_timeout_ms,:op_deadline_ms,:multipart_threshold_bytes,:multipart_part_bytes,:breaker_*,:pool_*— one deadline peroperation, multipart above a size, a breaker per bucket and a connection pool per endpoint. See `Ankusa.BlobStore.Options`:req_options— transport options for the HTTP client, e.g. acustom Finch pool (`finch: [name: MyFinch]`), a proxy (via `:connect_options`), or `plug:` for `Req.Test` in tests. See `Ankusa.HttpClient` — an allowlist, because a redirected or re-tuned request would no longer match its signature.:retries— default2: extra attempts after a transporterror or a `408`/`429`/`5xx`, each re-signed, jittered, `Retry-After` honoured. See `Ankusa.BlobStore.Retry`:sse—%{type: :aes256}or%{type: :kms, key_id: arn}:server-side encryption, sent (and signed) as `x-amz-server-side-encryption` on every `put/4`. See `Ankusa.BlobStore.SSE`
put_if_absent/4 sends If-None-Match: * (a 412/409 is :already_exists);
every PUT carries x-amz-checksum-sha256 and a GET verifies it
(Ankusa.BlobStore.Checksum); a body over multipart_threshold_bytes is a
multipart upload (create, parts each with Content-MD5, complete, abort on
failure).
Addressing is always path-style ({endpoint}/{bucket}/{key}) — the one
scheme every target (AWS, MinIO, R2, floci) accepts unambiguously.
Local dev
config :ankusa,
storage: %{
blob_store:
{Ankusa.BlobStore.S3,
bucket: "ankusa-segments-dev",
region: "us-east-1",
endpoint: "http://localhost:4566",
access_key_id: "test",
secret_access_key: "test"}
}docker compose -f docker-compose.integration.yml up -d floci s3-bootstrap brings up
the emulator and creates the bucket.