# open-swe infra (CDK TypeScript) AWS infrastructure for the Open SWE → AWS migration. **Synth-only at this stage — nothing here is deployed yet.** All IAM is applied only after the Phase-1 security gate (T4 GPT-4.1 IAM cross-review + T5 `/sh-security-review`) clears (T6). ## Layout ``` infra/ ├── bin/ │ └── app.ts # CDK app entry — instantiates the 3 stacks, applies the naming Aspect ├── lib/ │ ├── config.ts # account/region/org constants, env type, OIDC trust subjects │ ├── open-swe-iam-stack.ts # account-level: shared OIDC deploy roles │ ├── open-swe-stack.ts # per-env stack (instance role + AMI cache wiring) │ ├── aspects/ │ │ └── kebab-naming-aspect.ts # fails synth on any non-kebab-case explicit name │ └── constructs/ │ ├── github-deploy-roles.ts # githubdeploy-open-swe-infra + githubdeploy-open-swe-app │ ├── instance-role.ts # open-swe--instance-role (least-privilege) │ └── ami-cache.ts # cached ARM64 AL2023 helper + EBS/AMI discipline docs ├── test/ │ └── kebab-naming-aspect.test.ts # jest: Aspect passes conforming names, flags bad ones ├── cdk.json ├── cdk.context.json # COMMITTED — pins the AMI (see AMI cache discipline) ├── package.json # aws-cdk-lib pinned EXACT (2.260.0) ├── tsconfig.json ├── jest.config.js └── .gitignore ``` ## Stacks | Stack name (kebab) | Construct | Contents | |---|---|---| | `open-swe-iam` | `OpenSweIamStack` | Account-level shared GitHub OIDC deploy roles (singletons). | | `open-swe-dev` | `OpenSweStack` (`envName: dev`) | `open-swe-dev-instance-role` + AMI-cache wiring. EC2/ALB/etc. land at T12. | | `open-swe-prod` | `OpenSweStack` (`envName: prod`) | `open-swe-prod-instance-role` + AMI-cache wiring. | Account `328440206208`, region `us-east-1`. Stack names are set explicitly so CDK never defaults to PascalCase; resource names follow `open-swe--*`. > The two env stacks (`open-swe-dev` / `open-swe-prod`) are the required pair. The > shared OIDC deploy roles are account-wide singletons (one `RoleName` each), so > they live in their own dedicated `open-swe-iam` stack rather than being > duplicated across the env stacks — and that stack deploys first (see ordering). ## IAM roles defined (unapplied) - **`githubdeploy-open-swe-infra`** — GitHub OIDC role for CDK/CFN infra deploys. Trust scoped to `repo:Sea-Haven-Industries/open-swe` on the `main`/`dev` branches only. Permission is the org-standard CDK pattern: `sts:AssumeRole` on the CDK bootstrap roles (`cdk-hnb659fds-*`) — the real CFN/IAM/resource scope lives in the bootstrap `cfn-exec-role`, not in this role. - **`githubdeploy-open-swe-app`** — GitHub OIDC role for app deploys. Tag-scoped `ssm:SendCommand` (instances tagged `project=open-swe` + `env in {dev,prod}`) + read-only access to the `open-swe--assets` S3 artifact buckets. - **`open-swe--instance-role`** — EC2 instance role, least-privilege: read `open-swe--assets` (S3), read `/open-swe-/*` (SSM), read `open-swe-/*` (Secrets Manager), put `/open-swe//*` CloudWatch Logs, plus `AmazonSSMManagedInstanceCore` for SSM agent registration. No admin. The GitHub OIDC provider already exists account-wide (created for seahaven-site); it is referenced by ARN, never re-created. ## Kebab-case naming Aspect `KebabNamingAspect` (applied app-wide in `bin/app.ts`) fails synth via `Annotations.addError` when a stack name or an explicit physical resource name (`RoleName`, `BucketName`, …) is not kebab-case. Path-style names (Secrets Manager `a/b`, SSM `/a/b`, log groups `/aws/.../x`) are validated per `/`-segment. CDK logical construct ids are intentionally NOT validated (they are conventionally PascalCase). Covered by `test/kebab-naming-aspect.test.ts`. ## AMI cache discipline (EBS-fix plumbing — stub for T12) `cachedArm64AmazonLinux2023()` (in `lib/constructs/ami-cache.ts`) returns an ARM64 Amazon Linux 2023 image with `cachedInContext: true`, so the resolved AMI id is pinned in the committed `cdk.context.json`. Without the pin, every deploy could pick up a newer AL2023 release → AMI change → **EC2 instance replacement** (the file-share data-loss root cause — memory `feedback_inline_ebs_volumes`). Design intent documented in code for T12 to plug into: - `userDataCausesReplacement: true` is the **deliberate** choice — user-data is provisioning-only and carries no durable state. - **No durable state on the box → no RETAIN volume.** The in-memory langgraph store is rebuilt on every boot from S3 + Secrets Manager / SSM, so there is intentionally no standalone `ec2.Volume` + `removalPolicy.RETAIN`. The goal is replacement-*tolerance*, not avoidance. - **Snapshot-before-replace** still applies operationally at T12: snapshot the root volume and wait `state=completed` before any replacing deploy, and re-verify "no local-only durable state" first. Refresh the AMI pin deliberately: ```bash cdk context --reset 'ssm:account=328440206208:parameterName=/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64:region=us-east-1' cdk synth # review the diff — it WILL show "requires replacement" ``` > The committed `cdk.context.json` ships a dummy-but-valid-shaped AMI id > (`ami-00000000000000000`) so `cdk synth` resolves the cache locally without any > live AWS call. Replace it with the real resolved id when T8/T12 build the AMI. ## Commands ```bash npm install npx cdk synth open-swe-iam npx cdk synth open-swe-dev npx cdk synth open-swe-prod npm test # jest — naming Aspect ``` ## Deploy ordering (when the gate clears — NOT yet) 1. **`open-swe-iam` first** — create `githubdeploy-open-swe-infra` + set the repo `AWS_DEPLOY_ROLE_ARN` secret before any infra/secrets CI step (BLOCK#3). 2. **Security gate** — T4 GPT-4.1 IAM cross-review + T5 `/sh-security-review` on the synth; resolve every confirmed critical/high. 3. **IAM applied** (T6) — only after the gate. 4. Env stacks (`open-swe-dev`, then `open-swe-prod`) build out at T12+, prod gated by a GitHub Environment manual approval. ## Version policy `aws-cdk-lib` is pinned EXACT (`2.260.0`) — no `^`/`~`. Dependabot keeps it current; CI (`npm ci` + `cdk synth`) + dependency review gate each bump. See `aws-infrastructure.md` "CDK Version Policy" and memory `feedback_cdk_lib_bundled_deps`.