> For the complete documentation index, see [llms.txt](https://docs.rumi.systems/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rumi.systems/rumi-platform/concepts/aws-binding.md).

# AWS Binding

The AWS binding deploys the canonical Rumi private cloud onto Amazon Web Services. The same Rumi application, described once in DDL, runs unchanged across a developer laptop, local Docker, and AWS; the AWS binding is the component that turns that model into real VPCs, subnets, instances, and DNS.

It is built for SOC 2 grade deployments: secure by default, least privilege, and observable, with the operator supplying the sensitive pieces (IAM profiles, log destinations, certificate DNS) rather than the tool creating them.

This page is a feature catalogue. For the commands that drive each feature, see the [`rumi cloud aws`](/rumi-cli/commands/cloud/aws.md) reference.

## Two independent axes

A network is shaped by two orthogonal, opt-in choices made once at `create-network` and then read back at every later operation (both are stored as tags on the VPC):

* **Access posture** (how operators reach instances): `ssh`, `ssm-bastion`, or `ssm-all`.
* **Network topology** (where instances live): `public` or `public-private-nat`.

A bastion is always present in both.

## Network topology

| Topology             | Layout                                                                                                                                                                                                                                                                                                    |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `public` (default)   | A single public subnet holds the bastion and every instance. Instances reach the internet directly through the internet gateway when internet egress is enabled. Backward compatible with all earlier networks.                                                                                           |
| `public-private-nat` | A small public subnet holds the bastion(s) and a NAT gateway; a large private subnet holds the platform and workers. The private subnet reaches the internet outbound only through the NAT gateway, so instances there carry no public IP. This is where "internet egress" and "has a public IP" diverge. |

* **Subnet carve**: in the NAT topology the public subnet defaults to `<addr>.0.0/26` and the private subnet to `<addr>.128.0/17`. Both are operator-overridable (`--public-subnet-cidr` / `--private-subnet-cidr`), validated fail-fast (inside the VPC `/16`, non-overlapping, no smaller than the per-role floor of `/28` public and `/26` private).
* **Topology-driven placement**: at launch, a bastion lands in the public subnet and every other instance lands in the private subnet (NAT egress, no public IP). An explicit `--subnet-id` override pins placement and is validated against the network and its availability zone.
* **Single availability zone** today; multi-AZ is a later release.
* **Free S3 gateway endpoint** is attached to the route tables so instances reach S3 (for agent packages and S3 flow-log delivery) without paying NAT data charges.

## Access posture (SSH and SSM)

| Mode            | Behaviour                                                                                                                                                      |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ssh` (default) | Operators SSH to the bastion (its public SSH is opened only to whitelisted CIDRs), then on to the fleet.                                                       |
| `ssm-bastion`   | The bastion's public SSH is closed; operators reach the bastion over AWS Systems Manager (SSM), then SSH on to instances. Only the bastion runs the SSM agent. |
| `ssm-all`       | Public SSH is closed; operators SSM directly into any instance. Every instance runs the SSM agent.                                                             |

* The tool's own provisioning transport follows the posture: under SSM it tunnels to the bastion through an SSM session rather than a public IP.
* The SSM agent is installed idempotently at first boot, scoped by mode and role (only on machines that need it).
* Instance profiles granting the SSM managed policy are operator supplied and mandated (fail-fast) on the machines that run the agent.

## Bastion model

* **Single or multi-bastion**: a network has either one unnamed bastion or several named bastions (`create-network -B`). In a multi-bastion network every bastion is named and every proxying instance names the bastion that fronts it.
* **Reverse proxy fronting**: each service's public port is served through a bastion's nginx. Platform services (admin, monitor, brokers) and workers all route through a named bastion.
* **Jump-capable role**: only jump-capable bastions join the fleet SSH security group, so a proxy-only bastion has no SSH path into the fleet. The single bastion is always jump-capable.
* **Stateless primary**: the primary bastion is recomputed (first running jump-capable bastion) on every call; terminating it auto-promotes the next. Terminating the last jump-capable bastion while other instances run is refused, so the fleet is never stranded.
* **Elastic IPs**: a bastion or instance can take a stable Elastic IP that survives stop/start and replacement, is reused on relaunch, and is released at deprovision.

## Access and security

* **Record, then open on purpose**: a launch records a service's public proxy port (a tag) but opens nothing. Opening is a separate, deliberate `whitelist` step scoped to specific source CIDRs; `0.0.0.0/0` makes it public, and the `my-ip` token resolves to the operator's own public IP.
* **Bastion SSH whitelisting**: `whitelist-ssh` opens TCP 22 on a bastion for given CIDRs (ssh posture only). The bastion's default security group is otherwise minimal.
* **Internal SSH from the bastion only**: platform and broker security groups source SSH from the bastion's security group, not the VPC CIDR.
* **IMDSv2 enforced** on every instance (token-required metadata, hop limit 2 so containerized apps can still reach instance metadata).

## HTTPS

* **Let's Encrypt wildcard certificates** via certbot's DNS-01 challenge against Route 53, with autonomous renewal. One domain per bastion; the bastion's nginx terminates TLS for every service it proxies.
* Run `enable-https` right after launching a bastion and before launching services, so each proxy is created as a TLS block. The bastion needs an instance profile granting Route 53 access.
* Per-service public DNS A records (pointing at the proxy bastion's Elastic IP) are provisioner-managed at launch.

## Observability

All three are opt-in at `create-network` and lean on a consumer-supplied sink:

* **CloudWatch Logs interface endpoint** (`--log-shipping`): a private in-VPC path so private instances ship logs without traversing the internet gateway or NAT.
* **Role-keyed log shipping** (`--log-group`): every instance installs and configures the CloudWatch agent for its role's logs, with a clean `<category>/<name>` stream taxonomy (for example `app/<worker>`, `rumi/admin/app`, `rumi/monitor`, `rumi/bastion/<name>/nginx/access`). One log group per deployment.
* **VPC Flow Logs** (`--flow-logs-destination s3|cloudwatch`): all traffic, VPC-wide, delivered to an operator-supplied bucket or log group.

In the NAT topology a private worker ships CloudWatch logs through the NAT gateway without needing the Logs interface endpoint.

## Instance launch attributes

Per-launch attributes are set through a single extensible options object (so launch signatures stay stable as features are added):

* **First-boot command** run once via EC2 user-data (composed safely with the SSM and CloudWatch agent install snippets).
* **IAM instance profile** (name or ARN).
* **Boot/root volume size** and an additional data volume.
* **Per-instance tags** and an **explicit subnet override**.
* **Elastic IP** request.

## Resource tagging

* Provisioner-level base tags are applied to every created resource (instances, EBS volumes, VPC, subnets, route tables, internet gateway, S3 endpoint, security groups, Elastic IPs).
* Per-instance tags extend or override the base; the system tags `Name` and `ProvisionedBy` remain authoritative and drive idempotent discovery and teardown.

## Lifecycle and idempotency

* **Granular commands** create the network, bastions, platform services, brokers, and instances individually; **convenience commands** (`provision`, `launch-platform`) stand up a single-bastion deployment in one shot.
* **Start / stop / resize / terminate** operate per instance; `resize-instance` retypes a running instance (stop, retype, restart).
* **Idempotent teardown**: `delete-network` removes every provisioned resource (instances, NAT gateway and its Elastic IP, VPC endpoints, subnets, route tables, security groups, hosted zones, owned Elastic IPs) with no leaks. Re-running `create-network` on an existing network refuses cleanly rather than duplicating resources.

## The consumer-supplied principle

The tool never creates the sensitive or account-specific pieces. The operator supplies them, and in production they live in the customer's account:

* IAM instance profiles (SSM access, Route 53 for certificates, CloudWatch log shipping).
* The CloudWatch log group and the VPC Flow Logs delivery sink and role.
* The public DNS subdomain delegation used for certificate issuance.

## Related documentation

* [Hetzner Binding](/rumi-platform/concepts/hetzner-binding.md) — the same private cloud on Hetzner Cloud (prebaked-snapshot model, public-only topology).
* [Civo Binding](/rumi-platform/concepts/civo-binding.md) — the install-at-boot binding on Civo.
* [`rumi cloud aws`](/rumi-cli/commands/cloud/aws.md) — command reference.
