# `PhoenixKit.Modules.Storage.Bucket`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.16.0/lib/modules/storage/schemas/bucket.ex#L1)

Schema for storage provider configurations.

Buckets represent storage locations where files can be stored. They can be:
- **Local** filesystem storage
- **AWS S3** buckets
- **Backblaze B2** buckets
- **Cloudflare R2** buckets

## Priority System

- `priority = 0` (default): Random selection, prefer most empty drive
- `priority > 0`: Specific priority (1 = highest, 2 = second, etc.)

## Fields

- `name` - Display name for the bucket
- `provider` - Storage provider: "local", "s3", "b2", "r2"
- `region` - AWS region or equivalent (nullable)
- `endpoint` - Custom S3-compatible endpoint (nullable)
- `bucket_name` - S3 bucket name (nullable)
- `access_key_id` - Credentials identifier (nullable, stored as-is — not a secret)
- `secret_access_key` - Encrypted at rest via `PhoenixKit.Integrations.Encryption`
  (nullable); decrypted only at the point of use (e.g. `Providers.S3`'s AWS
  config builder), never by a general accessor like `Storage.get_bucket/1`.
  A value already in this column when encryption was added is migrated
  opportunistically, not by a bulk backfill: the changeset re-derives and
  re-encrypts it on the next save of the bucket for ANY reason (see
  `encrypt_secret_access_key/1`), so it stays plaintext only until then
- `integration_uuid` - Alternative credential source: a `PhoenixKit.Integrations`
  connection uuid (nullable, no FK). Mutually exclusive with
  `access_key_id`/`secret_access_key` — a changeset may set one source or the
  other, never both
- `cdn_url` - CDN endpoint for file serving (nullable)
- `access_type` - How files are served: "public", "private", "signed" (default: "public")
- `enabled` - Whether bucket is active
- `priority` - Selection priority (0 = random/emptiest)
- `max_size_mb` - Maximum storage capacity in MB (nullable = unlimited)

## Access Types

- `public` - Redirect to public URL (default, fastest, uses CDN)
- `private` - Proxy files through server (for ACL-protected buckets)
- `signed` - Use presigned URLs (future implementation)

## Examples

    # Local storage bucket
    %Bucket{
      name: "Local SSD",
      provider: "local",
      enabled: true,
      priority: 0,
      max_size_mb: 512_000  # 500 GB
    }

    # AWS S3 bucket
    %Bucket{
      name: "Production S3",
      provider: "s3",
      region: "us-east-1",
      bucket_name: "my-app-files",
      access_key_id: "AKIA...",
      secret_access_key: "...",
      cdn_url: "https://cdn.example.com",
      enabled: true,
      priority: 1  # Highest priority
    }

    # Backblaze B2 bucket
    %Bucket{
      name: "Backup B2",
      provider: "b2",
      endpoint: "s3.us-west-002.backblazeb2.com",
      bucket_name: "my-backup-bucket",
      access_key_id: "...",
      secret_access_key: "...",
      enabled: true,
      priority: 2
    }

# `t`

```elixir
@type t() :: %PhoenixKit.Modules.Storage.Bucket{
  __meta__: term(),
  access_key_id: String.t() | nil,
  access_type: String.t(),
  bucket_name: String.t() | nil,
  cdn_url: String.t() | nil,
  enabled: boolean(),
  endpoint: String.t() | nil,
  file_locations:
    [PhoenixKit.Modules.Storage.FileLocation.t()]
    | Ecto.Association.NotLoaded.t(),
  inserted_at: DateTime.t() | nil,
  integration_uuid: UUIDv7.t() | nil,
  max_size_mb: integer() | nil,
  name: String.t(),
  priority: integer(),
  provider: String.t(),
  region: String.t() | nil,
  secret_access_key: String.t() | nil,
  updated_at: DateTime.t() | nil,
  uuid: UUIDv7.t() | nil
}
```

# `changeset`

Changeset for creating or updating a bucket.

## Required Fields

- `name`
- `provider` (must be one of: "local", "s3", "b2", "r2")

## Validation Rules

- Provider must be valid
- Priority must be >= 0
- Quality must be between 1-100 (if provided)
- S3/B2/R2 buckets require credentials — either `access_key_id`/
  `secret_access_key` directly, or an `integration_uuid`
- `integration_uuid` and `access_key_id`/`secret_access_key` are mutually
  exclusive: setting both in the same change is a validation error, not a
  silent overwrite of one by the other

`secret_access_key` is encrypted at rest (see
`PhoenixKit.Integrations.Encryption`) as part of this changeset —
already-encrypted values pass through unchanged. A row written before
encryption existed stays plaintext until the next save of any kind (no
bulk backfill — see the `secret_access_key` field doc above).

# `cloud?`

Returns whether this bucket is a cloud storage bucket (S3, B2, R2).

# `local?`

Returns whether this bucket is a local storage bucket.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
