> ## Documentation Index
> Fetch the complete documentation index at: https://tesser.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Your own AWS account

> Run an org's boxes in your AWS account while Tesser hosts the control plane

By default boxes run in Tesser's AWS account and the org is charged per
awake second. An org can instead run its boxes in its own account: Tesser
still hosts the control plane, the CLI, and the dashboard, but every EC2
instance is yours, in your VPC, on your bill. Owners set this up with
`tesser cloud`.

## How Tesser reaches your account

Tesser never holds a credential for your account. The control plane is an
OpenID Connect issuer, and you create an IAM role that trusts it for exactly
one org id. Each time the org cell needs EC2 it signs a token that lives
five minutes, trades it for one hour of temporary credentials with
`AssumeRoleWithWebIdentity`, and uses those. Another org's cell cannot
assume your role: the trust policy names the org in `sub`, and the token
carries the org that signed it.

`tesser cloud template` prints the trust policy with the org id filled in:

```json theme={"theme":"css-variables"}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Federated": "arn:aws:iam::<your-account-id>:oidc-provider/api.tesser.sh" },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "api.tesser.sh:aud": "sts.amazonaws.com",
          "api.tesser.sh:sub": "org:<org id>"
        }
      }
    }
  ]
}
```

## What you create

1. **An OIDC provider** for `https://api.tesser.sh` with the audience
   `sts.amazonaws.com`. AWS fetches the signing keys from
   `api.tesser.sh/.well-known/jwks.json`; no thumbprint is needed.
2. **A role** with the trust policy above and a permissions policy fenced
   to instances tagged `tesser:managed=true`: `RunInstances` only when the
   launch carries that tag, `Stop`/`Start`/`Terminate`/`CreateTags`/
   `ModifyInstanceMetadataOptions` only on instances that already have it,
   and `Describe*` for instances, subnets, and security groups. Add an
   `ec2:InstanceType` condition to the `RunInstances` statement to cap the
   sizes the org may launch. No `iam:PassRole` unless you use Session Manager
   (below): otherwise a box has no AWS identity.
3. **A subnet** for the boxes. A public subnet that assigns public IPs works
   like Tesser's own cloud. A private subnet works too: the CLI then reaches
   boxes at their private IP, which needs a VPN or subnet router of yours,
   and the subnet needs a route to the internet so a box can reach
   `api.tesser.sh`.
4. **A security group** in the same VPC: port 22 from wherever you SSH
   (sshd only accepts the per-box keys), the mesh port from the group
   itself, egress open.
5. **The box AMI.** Tesser publishes its own image publicly in each
   supported region (Ubuntu with Node, Docker, and rsync); search public
   AMIs owned by Tesser for `tesser-box-*` in your region and pass its id.
   Any image with the same tools works too.

## Connecting the org

```text theme={"theme":"css-variables"}
tesser cloud connect \
  --role arn:aws:iam::123456789012:role/tesser-boxes \
  --region us-east-1 \
  --subnet subnet-0123456789abcdef0 \
  --sg sg-0123456789abcdef0 \
  --ami ami-0123456789abcdef0 \
  --private \
  --max-boxes 10
```

`connect` assumes the role once and describes the subnet and security
group. A role that does not trust the org, an id that does not exist, a
group in a different VPC than the subnet, or `--private` disagreeing with
whether the subnet assigns public IPs all fail before anything is stored.
`--max-boxes` is the org's own cap. `tesser cloud status` shows the current
config.

From then on every `tesser make`, `dev`, and `exec` launches in your
account. Tesser creates no security group of its own, the org's compute
meter keeps counting but nothing is charged, and the beta credit gate does
not apply. Boxes sleep, wake, and are reaped exactly as before.

## No inbound port: Session Manager

With `--ssm <instance-profile>` boxes take no inbound port 22 at all. Each
box launches with that IAM instance profile, and the CLI reaches sshd by
tunneling through AWS Session Manager: every ssh, sync, and daemon tunnel
runs `aws ssm start-session --target <instance id>` as its ProxyCommand,
and the host key is filed under the instance id.

```text theme={"theme":"css-variables"}
tesser cloud connect ... --ssm tesser-box
tesser cloud aws-profile acme      # on every member's laptop
```

What it needs:

* **The instance profile** must carry `AmazonSSMManagedInstanceCore` (the
  SSM agent ships in the Tesser AMI). The role tesser assumes also needs
  `iam:PassRole` on that profile's role for `RunInstances`.
* **Every org member** needs AWS credentials for the account with
  `ssm:StartSession` on the boxes, the AWS CLI, and the
  [session-manager-plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)
  installed. `tesser cloud aws-profile <name>` tells the CLI which AWS CLI
  profile to use for this org; it lives in the laptop's tesser config, not in
  the control plane.
* The security group no longer needs port 22 open; boxes still need egress
  to reach SSM and `api.tesser.sh`.

Bulk transfers (the first sync of a large worktree) are slower through
Session Manager than over a direct connection; the steady-state edit-sync
loop is fine.

## Changing or leaving

Where boxes run cannot change while any exist. Remove them, then `connect`
again with the new ids, or `tesser cloud disconnect` to return the org to
Tesser's cloud. `--max-boxes` can change at any time.
