PhoenixKit.Integrations.KeyStore.S3 (phoenix_kit v2.13.17)

Copy Markdown View Source

Optional off-host copy of the encryption secret, in an S3-compatible bucket.

AWS S3, Cloudflare R2, Backblaze B2, Tigris, MinIO — anything the existing ex_aws_s3 dependency talks to. Optional in the strong sense: PhoenixKit works with no bucket, no AWS account and no network. Use it as a second member of PhoenixKit.Integrations.KeyStore.Chain, behind the local file, so the bucket is a spare copy rather than the thing everything depends on:

config :phoenix_kit,
  integrations_key_store:
    {PhoenixKit.Integrations.KeyStore.Chain,
     stores: [
       PhoenixKit.Integrations.KeyStore.File,
       {PhoenixKit.Integrations.KeyStore.S3, bucket: "my-ops-bucket"}
     ]}

Why the credentials do NOT come from an integration connection

The obvious idea is to configure this through the integrations system, the same way any other provider is configured. It cannot carry the read path, and the reason is structural rather than a matter of effort.

An integration connection's secret_key is in PhoenixKit.Integrations.Encryption.sensitive_fields/0, so it is stored encrypted with the integration key. Reading the secret out of a bucket whose credentials are themselves encrypted under that secret requires the secret you are trying to fetch. Writes work — during a rotation the old key is live, so the credentials decrypt — which is the dangerous part: it would produce a backup that is written successfully and can never be restored from. A backup you cannot restore from is worse than none, because you stop looking for a real one.

So credentials come from ExAws's own resolution: environment variables, an instance role, or an explicit :ex_aws_config. On AWS with an instance role there is no stored secret at all, which removes the circularity completely.

What the integrations system can usefully hold is the part that is not secret — which bucket, which region, which object — and that is a later, separate step; nothing here needs it.

What is stored

One object, the secret as its body, written with AES256 server-side encryption. Bucket-side access control is the operator's to set: this module refuses to pretend it can make a public bucket safe.