landing-zone
Cloud substrate
The slow-moving AWS layer — VPC, base IAM, KMS, DNS, cluster vending — as OpenTofu components driven by Terragrunt, plus the generic module that provisions a tenant's datastores from its declaration.
What follows is that repo's AGENTS.md — the entry point an agent reads before working on it. It is rendered from the repo itself at build time rather than copied, so it says what the repo says today. Links in it point back at the repo they were written for.
landing-zone — agent entry point
You’re an AI client (or the author of one) about to provision cloud substrate. This file gets you running in five minutes. For the wider picture — how this repo fits into the rest of the nanohype stack — read the Platform Reference.
What this repo gives you
OpenTofu + Terragrunt monorepo for the AWS substrate every nanohype-stack app lands on:
components/aws/— VPC (network/shared-network/egress-network), base IAM, KMS keys, EKS cluster, cluster bootstrap, observability, secrets, governance,agent-iam. Shared multi-tenant components (var.tenants):druid,pipeline,governance, andtenant-substrate— the generic per-tenant datastore substrate that provisions a tenant’s declared stores from its Platform CR.
Plus:
modules/— reusable building blocks components compose:workload-identity(the EKS Pod Identity role factory) andeks-vpc-endpoints(the private endpoint set create-modenetworkandshared-networkbuild).live/— per-environment terragrunt configurations. Path islive/aws/<account>/<region>/<env>/<component>/terragrunt.hcl.
Run it
The build interface is Taskfile.yaml (Task), not raw tofu/terragrunt. Prerequisites: OpenTofu >= 1.11.0, Terragrunt, TFLint with the AWS plugin, Python 3, and the AWS CLI for anything that touches an account.
task check # everything CI runs: fmt, validate, lint, tofu test, and every gatetask fmt:check # formatting onlytask validate # tofu init -backend=false + validate, every roottask lint # tflint at --minimum-failure-severity=noticetask test # the tofu test suitestask gates # the semantic gates under scripts/, each its own CI jobtask plan ACCOUNT=workload-development REGION=us-east-1 ENVIRONMENT=development COMPONENT=networktask check needs no AWS credentials — every suite runs against mocked providers and every gate reads the tree. task plan and task apply need credentials for the target account.
Six user-invocable skills live in .claude/skills/: add-component, add-tenant, plan, drift, validate, destroy. Each names the steps and the gates its change has to satisfy.
Contract surface
Every component:
- Has its own
versions.tfdeclaringterraform >= 1.11.0+ AWS provider~> 6.0. - Has its own
variables.tf+outputs.tf+ per-resource files. - Reads from upstream component outputs via terragrunt
dependencyblocks declared inlive/_envcommon/aws/<component>.hcl. - Tags every resource with
Environment,ManagedBy,Project,CostCenter,BusinessUnit,DataClassification,Compliance,Repository(default tags emitted bylive/root.hcl). - Uses EKS Pod Identity via the shared
modules/aws/workload-identitymodule. Trust policies targetpods.eks.amazonaws.com(not an OIDC provider), and each role is bound to a specific ServiceAccount in a specific namespace through an EKS Pod Identity association.
A tenant’s stateful substrate is a declaration, not a per-app component. Each store is declared in Platform.spec.datastores (relational/keyValue/objectStore/queue/cache/stream) and the generic tenant-substrate component provisions it; the eks-agent-platform operator generates the scoped datastore-access IAM policy on the tenant role and binds the tenant-runtime ServiceAccount to it via a Pod Identity association. Non-datastore capabilities (SES, EventBridge Scheduler) are declared in Platform.spec.identity.capabilities and the operator generates their grants (minting a scheduler-invoke role for the latter). Bedrock model access comes from the agent-iam tenant baseline, clamped by the operator to Platform.spec.identity.allowedModels. Adding a tenant is a declaration in the app’s own repo — landing-zone hand-writes no per-app substrate.
Add a new component
- Create
components/aws/<name>/withversions.tf,variables.tf,main.tf,outputs.tf, plus per-resource files (rds.tf,s3.tf, etc.). - Add
live/_envcommon/aws/<name>.hcldeclaring dependencies on upstream components (typicallynetwork,cluster,cluster-bootstrap). - Add
live/aws/<account>/<region>/<env>/<name>/terragrunt.hclper environment you want to provision (workload-development,workload-staging,workload-production). - Add the component to the
## Layer Breakdowntable indocs/architecture.mdand to the layer list inCLAUDE.md.scripts/check-architecture-components.shgates both: docs/architecture.md in both directions (a documented component that is gone, and a component no table names), and CLAUDE.md in the omission direction only — it is prose rather than a table, so demanding a parseable shape would fix its formatting in place. - Declare the teardown posture: a component holding an
aws_s3_bucket,aws_rds_cluster,aws_dynamodb_tableoraws_secretsmanager_secreteither wires aforce_destroy_bucketslever through every gate attribute those types need, or is named in theEXEMPTtable inscripts/check-teardown-gates.pywith the reason. - Write
components/aws/<name>/tests/<name>.tftest.hcl. Every other root carries one; they run atcommand = planagainst amock_provider, so they need no credentials. - Run
task check. - CI auto-discovers the new component via
git ls-files— no workflow edit needed (Validate (aws/<name>)job materializes on the next PR).
Add a tenant’s substrate
A tenant’s databases, buckets, queues, caches, and streams are not a landing-zone component — they are declared in the app’s Platform.spec.datastores and provisioned by the generic tenant-substrate component from that declaration (its var.tenants map is rendered from the Platform CRs, not hand-authored). SES and EventBridge Scheduler ride Platform.spec.identity.capabilities, generated by the operator. So to add or change a tenant’s substrate you edit the Platform CR in the app’s repo — there is no per-app component to author here.
What still lands in landing-zone:
- Cloud-substrate gaps the datastore vocabulary does not cover — a shared VPC, a base IAM grant, a KMS key, a new managed service — as a new general component (not a per-app one).
- If a tenant needs a substrate service the tenant permissions boundary doesn’t cover yet, extend
agent-iam’s tenant boundary — the boundary caps every tenant role, so a grant outside it is silently clipped.
Conventions
- All TF formatted with
tofu fmt -recursive. CI’sFormat Checkjob blocks on drift. tflintruns repo-wide via the.tflint-aws.hclconfig — a hard gate at--minimum-failure-severity=notice, so undocumented or unused declarations and missing version constraints fail the build. The uniform envcommon interface inputs carry inline# tflint-ignorerationale; nothing else may.- Security scan via
checkov— a hard gate (soft_fail: false); accepted posture trade-offs are enumerated in.checkov.yaml, one line of rationale each. - Backend: per-component S3 state with native lockfile locking, generated by
live/root.hcl. - Input precedence: an ambient
TF_VAR_*beats a leaf’sinputsvalue — the opposite of the natural assumption, and terragrunt’s documented order. Two consequences: anything injectingTF_VAR_*silently overrides a deliberate per-environment value, so inject only when the value differs from the default; and noTF_VARcan rescue an unapplieddependency, because adependencyis resolved at config-parse time before tofu exists — which is why alive/aws/workload-*/leaf may not depend on another account’s state (gated byscripts/check-account-local-deps.py). Full statement indocs/inputs.md. - The
github-oidcdeploy-role trust scopes torepo:nanohype/landing-zonewith environment-gated and tag-push subject claims (allowed_subject_claims = ["environment:*", "ref:refs/tags/*"]). A bare:*— which would also trust fork PRs and arbitrary branches — is deliberately excluded; widenallowed_subject_claimsexplicitly if the CI model needs another context.
Pointers
README.md— full repo overviewdocs/— architecture, OIDC setup, drift managementCLAUDE.md— Claude Code session instructions- Platform Reference — the stack-wide view
eks-agent-platform/AGENTS.md— the operator that provisions per-tenant IAM + Pod Identity and generates the datastore-access + capability-access grants from the Platform CR