infra: read-only inventory grant for the architecture page
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2 files changed,
+40
−0
docs/superpowers/specs/2026-08-19-fleet-architecture-diagram-design.md+6 −0infra/modules/region-host/main.tf+34 −0
diff --git a/docs/superpowers/specs/2026-08-19-fleet-architecture-diagram-design.md b/docs/superpowers/specs/2026-08-19-fleet-architecture-diagram-design.md| index 0db071256..eabe177ae 100644 |
| --- a/docs/superpowers/specs/2026-08-19-fleet-architecture-diagram-design.md |
| +++ b/docs/superpowers/specs/2026-08-19-fleet-architecture-diagram-design.md |
| @@ -324,6 +324,12 @@ inventory is not granted. **The page must be built and merged without |
| depending on the grant existing** — the ungranted state is a first-class |
| rendering, not a broken one. |
| +**Status (2026-08-19):** the module statement is in this repo as of this |
| +branch. The flagship box runs a role provisioned from pinfra, so the grant |
| +reaches production only when the matching pinfra change is applied — until |
| +then `/fleet/architecture` renders the CARLOS half and reports the estate |
| +as ungranted, which is the designed state and not a fault. |
| + |
| ## 9. Testing |
| - `internal/infraview`: shape round-trip, and the additive rule — an |
diff --git a/infra/modules/region-host/main.tf b/infra/modules/region-host/main.tf| index c145aac8f..407565c29 100644 |
| --- a/infra/modules/region-host/main.tf |
| +++ b/infra/modules/region-host/main.tf |
| @@ -97,6 +97,40 @@ resource "aws_iam_role_policy" "host_buckets" { |
| Action = ["ssm:GetParameter"] |
| Resource = var.ssm_parameter_arn |
| }, |
| + # Infrastructure inventory (2026-08-19-fleet-architecture-diagram-design.md |
| + # §8): what the console's hourly infra pass needs to draw |
| + # /fleet/architecture. Read-only in the strictest sense — every action |
| + # here is a List/Describe/Get of a resource's SHAPE. No write action, |
| + # no s3:GetObject, and deliberately no iam:GetPolicyVersion: the page |
| + # shows that a role exists and how many policies are attached to it, |
| + # never what those policies say. |
| + { |
| + Effect = "Allow" |
| + Action = [ |
| + "ec2:DescribeInstances", |
| + "ec2:DescribeVolumes", |
| + "ec2:DescribeAddresses", |
| + "ec2:DescribeSecurityGroups", |
| + "ec2:DescribeVpcs", |
| + "ec2:DescribeSubnets", |
| + "ec2:DescribeRegions", |
| + "tag:GetResources", |
| + "s3:ListAllMyBuckets", |
| + "s3:GetBucketLocation", |
| + "s3:GetBucketVersioning", |
| + "s3:GetEncryptionConfiguration", |
| + "s3:GetBucketPublicAccessBlock", |
| + "route53:ListHostedZones", |
| + "route53:ListResourceRecordSets", |
| + "iam:ListRoles", |
| + "iam:ListUsers", |
| + "iam:ListAttachedRolePolicies", |
| + "cloudwatch:DescribeAlarms", |
| + "acm:ListCertificates", |
| + "sts:GetCallerIdentity", |
| + ] |
| + Resource = "*" |
| + }, |
| ] |
| }) |
| } |