# 🤖 The blob MIRROR bucket (federated blobs Phase A, #992 — # docs/federated-blob-storage.md): the second copy of every store-backed # attachment, on the second provider. The PRIMARY blob store is Hetzner # Object Storage — enabled and credentialed in the Hetzner console, since # neither the hcloud provider nor this AWS one can manage it; this file is # the AWS half only. # # Versioning is deliberately OFF, unlike the hibernation bucket beside it: # blobs are write-once (an id is never rewritten, so versions can't help), # and a delete here is member-initiated GC — retaining a deleted member's # media as noncurrent versions would quietly betray the deletion. When a # blob goes, both copies actually go. resource "aws_s3_bucket" "production_blob_mirror" { bucket = "eleven-production-blob-mirror-${local.account_id}" } resource "aws_s3_bucket_public_access_block" "production_blob_mirror" { bucket = aws_s3_bucket.production_blob_mirror.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } # TLS-only, same shape as the hibernation bucket: deny-only grants nothing # public, so it attaches fine under the full access block above. data "aws_iam_policy_document" "production_blob_mirror_tls_only" { statement { sid = "DenyInsecureTransport" effect = "Deny" actions = ["s3:*"] resources = [ aws_s3_bucket.production_blob_mirror.arn, "${aws_s3_bucket.production_blob_mirror.arn}/*", ] principals { type = "*" identifiers = ["*"] } condition { test = "Bool" variable = "aws:SecureTransport" values = ["false"] } } } resource "aws_s3_bucket_policy" "production_blob_mirror" { bucket = aws_s3_bucket.production_blob_mirror.id policy = data.aws_iam_policy_document.production_blob_mirror_tls_only.json } # The instances' mirror credentials (LCHAT_BLOB_S3_MIRROR_ACCESS_KEY / # _SECRET_KEY): object rw on this bucket only — no List, because the mirror # and audit sweeps address every object by id straight from the blobs table # (HeadObject rides the s3:GetObject grant). Key minted by hand # (`aws iam create-access-key`), sealed into the vault — never in tofu state. resource "aws_iam_user" "blob_mirror_production" { name = "blob-mirror-production" } data "aws_iam_policy_document" "blob_mirror_production" { statement { sid = "ReadWriteBlobMirrorObjects" actions = [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", ] resources = ["${aws_s3_bucket.production_blob_mirror.arn}/*"] } } resource "aws_iam_user_policy" "blob_mirror_production" { name = "blob-mirror-production-bucket" user = aws_iam_user.blob_mirror_production.name policy = data.aws_iam_policy_document.blob_mirror_production.json } output "production_blob_mirror_bucket" { value = aws_s3_bucket.production_blob_mirror.bucket }