# ๐Ÿค– The dev mesh's object store โ€” deliberately SEPARATE from prod's buckets and # from infra/dev's proof buckets. Mesh membership is S3 heartbeats under a # prefix, so a distinct bucket is what physically stops a dev node from ever # joining the prod mesh (docs/dev-mesh.md ยง Cut-off). One bucket holds it all: # membership, the hibernation lease + replicas, and instance DBs. resource "aws_s3_bucket" "mesh" { bucket = "eleven-dev-mesh-${local.account_id}" } # Versioning is the ransomware/oops posture: an overwrite or delete with the # box's own credentials leaves recoverable versions behind. Dev data is # disposable, but the machinery must match prod so we exercise the real thing. resource "aws_s3_bucket_versioning" "mesh" { bucket = aws_s3_bucket.mesh.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_lifecycle_configuration" "mesh" { bucket = aws_s3_bucket.mesh.id rule { id = "prune-noncurrent" status = "Enabled" filter {} noncurrent_version_expiration { noncurrent_days = 5 } } } resource "aws_s3_bucket_public_access_block" "mesh" { bucket = aws_s3_bucket.mesh.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } # ---- the Helsinki (fi) node's credential -------------------------------------- # Hetzner can't assume an EC2 role, so the fi node gets a scoped IAM user whose # policy is confined to this one bucket โ€” the shape a non-AWS provider's # credential takes in the co-op model. The access key is created here but its # secret is written to tofu STATE only (S3, private); it must be pulled from # there into the vault, never committed. See outputs.tf. resource "aws_iam_user" "fi_node" { name = "eleven-dev-mesh-fi" } data "aws_iam_policy_document" "mesh_rw" { statement { sid = "ListMeshBucket" # GetBucketLocation: the hibernation restore (embedded Litestream) resolves # the bucket's region before reading replicas โ€” without it a woken instance # 503s with "cannot lookup bucket region" even though object read/write is # allowed. Bucket-level, so it sits in this ARN-of-the-bucket statement. actions = ["s3:ListBucket", "s3:ListBucketVersions", "s3:GetBucketLocation"] resources = [aws_s3_bucket.mesh.arn] } statement { sid = "ReadWriteMeshObjects" actions = [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", ] resources = ["${aws_s3_bucket.mesh.arn}/*"] } } resource "aws_iam_user_policy" "fi_node" { name = "eleven-dev-mesh-bucket" user = aws_iam_user.fi_node.name policy = data.aws_iam_policy_document.mesh_rw.json } resource "aws_iam_access_key" "fi_node" { user = aws_iam_user.fi_node.name }