# 🤖 The dev box: one Graviton instance for the hibernation-activator proof # (#550 Part 7's "minimum viable" shape — the ASG comes later; one pet # instance is enough to prove wake/sleep on real AWS metal). It reads the # instances bucket via an INSTANCE ROLE — no AWS keys ever land on the box. data "aws_ami" "al2023_arm" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["al2023-ami-2023*-arm64"] } filter { name = "architecture" values = ["arm64"] } } resource "aws_key_pair" "dev_box" { key_name = "eleven-dev-box" public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILReNBCz+nwYvJ7x/RfgW/kbvSFe3pzPhOBM1SOqbjSg eleven-dev-box" } variable "ssh_ingress_cidr" { description = "Where SSH is allowed from (the operator's current IP/32)" type = string default = "109.255.206.133/32" } resource "aws_security_group" "dev_box" { name = "eleven-dev-box" description = "dev activator box: ssh from the operator, web open" ingress { description = "ssh (operator only)" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [var.ssh_ingress_cidr] } ingress { description = "http/https (autocert + serving, for when DNS points here)" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "https" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # The box's identity: object-rw on the instances bucket only, via instance # profile — the credential-free path every later box in the pool follows. resource "aws_iam_role" "dev_box" { name = "eleven-dev-box" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } }] }) } data "aws_iam_policy_document" "dev_box" { statement { sid = "ListInstancesBucket" actions = ["s3:ListBucket", "s3:ListBucketVersions"] resources = [aws_s3_bucket.state["instances"].arn] } statement { sid = "ReadWriteInstanceObjects" actions = [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", ] resources = ["${aws_s3_bucket.state["instances"].arn}/*"] } } resource "aws_iam_role_policy" "dev_box" { name = "eleven-dev-box-instances-bucket" role = aws_iam_role.dev_box.id policy = data.aws_iam_policy_document.dev_box.json } resource "aws_iam_instance_profile" "dev_box" { name = "eleven-dev-box" role = aws_iam_role.dev_box.name } resource "aws_instance" "dev_box" { ami = data.aws_ami.al2023_arm.id instance_type = "t4g.small" key_name = aws_key_pair.dev_box.key_name vpc_security_group_ids = [aws_security_group.dev_box.id] iam_instance_profile = aws_iam_instance_profile.dev_box.name # The instance role's credentials are served by IMDS, so require IMDSv2's # session tokens: an SSRF or misconfigured proxy on the box can't read the # role credentials with a bare GET. Hop limit stays at the default 1 — # nothing here runs in containers. metadata_options { http_endpoint = "enabled" http_tokens = "required" } # encrypted forces a replacement of the original (unencrypted) volume — # apply with -replace=aws_instance.dev_box; the box is a disposable pet # (state parks in S3, that's the whole point of the activator proof). root_block_device { volume_size = 20 volume_type = "gp3" encrypted = true } tags = { Name = "eleven-dev-box" } } output "dev_box_ip" { value = aws_instance.dev_box.public_ip }