Ankusa.BlobStore.S3 (ankusa v0.7.0)

Copy Markdown View Source

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 — static
                        credentials. 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 — default 10_000, for both connect and response of one
                        request that carries at most 1 MiB
  • :upload_timeout_ms, :op_deadline_ms, :multipart_threshold_bytes, :multipart_part_bytes, :breaker_*, :pool_* — one deadline per
                        operation, 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. a
                        custom 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 — default 2: extra attempts after a transport
                        error 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.