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

# Installing Genesis on AWS (Tier A): guide

> Install Genesis in your own AWS account, on a new EKS cluster the install creates.

**Tier A** is the Genesis offer in which Genesis runs in **your own AWS account**, on a new
Kubernetes cluster (Amazon EKS) that the install creates for it. Genesis does not need an AWS key of
its own. The install runs with your credentials, and afterwards the Genesis management agent in the
cluster uses a read-only role in your account.

This first version of Tier A is available only for targets Genesis has approved. It is described
here with its limits stated; see [Limits of this version](#limits-of-this-version).

## Before you start

### 0. Agree the target with Genesis

Send your Genesis contact the AWS account ID, region and cluster name you intend to use. Genesis
approves that exact target in an **installer profile** and sends you:

* the profile name, for your decision sheet;
* the installer: a copy of the Genesis repository at an approved version;
* three documents: this guide, the [decision sheet](/docs/deployments/AWS/Tier-A/Decision-Sheet) and the
  [permission appendix](/docs/deployments/AWS/Tier-A/Permission-Appendix);
* two files for this install: a **registration secret** and a **registry pull credential**.

Save each file as a regular file you own, with mode exactly `600` (`chmod 600`; `400` is refused
too). The install refuses any other mode. They are not AWS keys, and you never copy them anywhere
else.

* The **registration secret** registers exactly one Genesis deployment, and expires 30 days after
  Genesis issues it. It is used up when an install reaches its last stage and registers with
  Genesis. If an install fails before that stage, you can use the same secret again; if it fails
  after it, ask Genesis for a new one (see [If the install fails](#if-the-install-fails)).
* The **registry pull credential** can be used again for a retry. Keep it: the install stores it in
  the cluster, and Kubernetes uses it again whenever it moves the Genesis application to another
  node, so it must stay valid for as long as Genesis is installed. If Genesis tells you it has
  replaced the credential, ask Genesis how to update the installed copy.

### Prerequisites on the machine that runs the install

* [uv](https://docs.astral.sh/uv/) and Python 3.12 or later. Then, in the installer directory, run
  `uv sync --locked` once.
* The AWS CLI v2, signed in as the principal that will install (see step 2).
* **Helm 3** (Helm 4 is refused) and kubectl 1.35 to 1.37, on your `PATH`. Genesis does not
  supply them. You never run Helm or kubectl yourself: the install and `open` do. The preflight
  (step 3) checks exactly the versions the install will use, and says which to install if one is
  wrong.
* OpenTofu **1.12.6**. The install looks for it at `~/.genesis/tools/tofu-1.12.6/tofu`; if it is
  elsewhere, add `--tofu <path>` to the install and teardown commands.
* The machine's public IP address must fall inside one of the sheet's `api_cidrs`. The install and
  the egress probe talk to the new cluster's Kubernetes API, which accepts only those ranges. The
  preflight does not check this; you can see your address with
  `curl -s https://checkip.amazonaws.com`.

Run every command below from the installer directory.

### Network

The new cluster needs outbound HTTPS (port 443) to:

* `enhancements.genesiscomputing.ai` and `onboarding.genesiscomputing.io` (the Genesis services);
* `ghcr.io` and `pkg-containers.githubusercontent.com` (the container registry);
* `public.ecr.aws` (the image the egress probe uses).

Inside the cluster, the Genesis management agent and sandbox manager may connect on port 443 to
anything in the cluster's control-plane subnets, not only to the Kubernetes API. The install
creates those subnets as two dedicated `/28` ranges that hold only the Kubernetes API's network
interfaces.

The install creates its own VPC with a route to the internet, so normally there is nothing to do.
You only need to act if your organization filters outbound traffic from new VPCs (an egress proxy,
a firewall or a service control policy): allow these destinations there **before** you install. If
the cluster cannot reach them, the install cannot finish: it fails and removes what it created.
Outbound access can only be tested once a cluster exists (step 5).

### Where the install keeps its record

Every command takes `--evidence <directory>`: the place on your machine where the install keeps
its record (what was decided, created and checked). Choose one directory and pass the **same** one
to every command: install, egress probe, open and teardown. The directory is created if it does not
exist, and each run gets its own subdirectory, `<directory>/<run-id>/`. The install, egress probe,
open and teardown require `--evidence`, and the install also requires `--pull-token-file`; without
them a command stops with exit code 2 before it reads anything.

The examples below use two shell variables. Set them once in your terminal; any paths work:

```sh theme={null}
SHEET=~/genesis-tier-a/decision-sheet.json
EVIDENCE=~/genesis-tier-a/evidence
mkdir -p "$EVIDENCE"
```

The sheet can have any file name and needs no special file mode: it holds no secrets.

## Install

1. **Fill in the [decision sheet](/docs/deployments/AWS/Tier-A/Decision-Sheet)** and save it at `$SHEET`.
2. **Grant the permissions.** Choose one AWS principal (a user or role) to run both the preflight
   and the install. The preflight checks the principal it runs as, so running it as anyone else
   checks the wrong permissions. Generate the ready-to-attach policy for your sheet and attach it
   to that principal:

   ```sh theme={null}
   uv run python -m scripts.aws_tier_a permission-policy --sheet "$SHEET" > "$EVIDENCE/installer-policy.json"
   ```

   The policy grants exactly the installer actions, scoped to your sheet's target, and most of the
   preflight's reads. Also allow `servicequotas:GetServiceQuota`, the one preflight read it leaves
   out (part 2 of the [permission appendix](/docs/deployments/AWS/Tier-A/Permission-Appendix) lists all of them).
3. **Run the preflight.** It only reads your account:

   ```sh theme={null}
   uv run python -m scripts.aws_tier_a preflight --sheet "$SHEET" \
     --aws-profile <your-aws-profile> --report-file "$EVIDENCE/preflight.json"
   ```

   Its first line names the cluster it checked, as
   `Tier A preflight for arn:aws:eks:<region>:<account>:cluster/<name>`; make sure that is the
   target you agreed with Genesis. It checks:

   * that your credentials are for the sheet's account;
   * every permission the install needs;
   * the VPC, Elastic IP, NAT gateway and vCPU quotas against what the install creates;
   * that the region has two available Availability Zones;
   * the tool versions.

   It prints READY, or NOT READY with **one action per failure**, for example
   "Grant eks:CreateCluster to arn:aws:iam::...:role/..." or "Request a Service Quotas increase for
   L-1216C47A in us-east-2 to at least 8". It exits with code 2 when anything needs attention. Fix
   each action and run it again until it says READY.
4. **Install:**

   ```sh theme={null}
   uv run python -m scripts.aws_tier_a install --sheet "$SHEET" \
     --run-id <a-short-name-for-this-install> --aws-profile <your-aws-profile> \
     --registration-secret-file <path-to-the-registration-secret> \
     --pull-token-file <path-to-the-registry-pull-credential> \
     --evidence "$EVIDENCE"
   ```

   The run ID is 1 to 30 lowercase letters, digits or hyphens, starting with a letter or digit. It
   is used for exactly one install: the install refuses a run ID that already has a run directory
   under `$EVIDENCE`, so to try again after a failure, use a new one.

   **It takes about 50 to 60 minutes.** It prints one line per step, each a time and a short step
   name (for example `tofu-apply`). The stages are:

   * Checks, about a minute. Before it creates anything, the install checks both files, your
     tools, the daily budget and that your profile approves the sheet's target.
   * The network and the cluster, about 15 minutes.
   * A stability check, about 10 to 15 minutes: it waits until the cluster's API addresses read
     the same three times, five minutes apart.
   * Installing Genesis, about 25 to 30 minutes, **with no output while it runs**. That is
     normal. This stage ends when the management agent sends its **first heartbeat**: a signed
     message to Genesis carrying the agent's identity, the installed release and the
     application's health.

   When it succeeds, the last line is:

   ```text theme={null}
   Install complete for run <run-id>: Genesis is running, and its management agent's first heartbeat was accepted (deployment dep_...).
   ```

   and the command exits with code 0. Keep the `dep_...` identifier: it names your deployment when
   you talk to Genesis.
5. **Optional: re-check outbound access.** You can confirm the cluster's own outbound path at any
   time after the install:

   ```sh theme={null}
   uv run python -m scripts.aws_tier_a egress-probe --sheet "$SHEET" \
     --run-id <the-same-run-id> --aws-profile <your-aws-profile> --evidence "$EVIDENCE"
   ```

   This runs one short-lived pod in the cluster to test each destination in [Network](#network),
   and always deletes the pod afterwards. Any destination it cannot reach is named, with the
   firewall or proxy change to make. Re-running it is safe.
6. **Open Genesis:**

   ```sh theme={null}
   uv run python -m scripts.aws_tier_a open --sheet "$SHEET" \
     --run-id <the-same-run-id> --aws-profile <your-aws-profile> --evidence "$EVIDENCE"
   ```

   This forwards the application to `http://localhost:18080/` on your machine (only your machine;
   nothing is exposed publicly) until you press Ctrl-C. With `sign_in` in your sheet, the page asks
   you to sign in through your Auth0 tenant.

   * **Port 18080 must be free.** The port is fixed, because your Auth0 application allows exactly
     that address, and this version has no option to change it. If another program on your machine
     already uses port 18080, the forward cannot start: the next line says `address already in
     use` and the command exits. Stop that program, then run `open` again.
   * **Installed from another machine?** The forward listens on the machine that runs `open`. To
     use a browser on your own computer, open an SSH tunnel to that machine first
     (`ssh -L 18080:localhost:18080 <install-machine>`), then browse to `http://localhost:18080/`.
   * **If the page stops loading**, the forward has dropped (this can happen after a while, and
     `open` exits with an error). Run `open` again.

### If the install fails

The install exits with a non-zero code. If it had already created resources, it removes them
before it exits, and reports the same zero-leftover check as [teardown](#removing-genesis).

* **A refusal before anything is created** (permissions, quotas, tools, the two files, the budget,
  or a target your profile does not approve) names what to fix. Fix it and run again with a new
  run ID. Your registration secret is still unused.
* **A failure in the last stage** (installing Genesis) is usually for Genesis to fix. The
  command's last line names the step and ends with `see protected run log`. That log is the file
  `<step>.log` in `$EVIDENCE/<run-id>/` (for example `installer.log`). Only you can read it,
  because it may contain secrets, so **do not send the file itself**. Send Genesis the run ID and
  the log's last sentence (for example the line that starts `installation refused:`).
* **A failure after registration** uses up the registration secret, and leaves a deployment record
  on the Genesis side. You can tell: `$EVIDENCE/<run-id>/receiver-deployments.json` lists a
  `dep_...` identifier. Ask Genesis for a new registration secret before you retry, and give
  Genesis the run ID or that `dep_...` identifier so that it retires the old record.

## After the install

* **Opening Genesis:** step 6. The application is reachable only through that local forward; it is
  not exposed publicly. If your sheet has `sign_in`, people sign in through your Auth0 tenant.
* **Connect an AI provider before you use Eve.** A new install has no AI provider, so Eve (the
  Genesis assistant) cannot answer anything yet. Some of the checks an upgrade runs also use Eve.
  Sign in, go to **Config → AI Providers**, choose a provider (for example OpenAI, Anthropic, AWS
  Bedrock, Azure OpenAI, Snowflake Cortex or Databricks), select **Connect**, and enter your API key
  or connection details for it. Until a provider is connected, that page shows "No AI provider
  configured".
* **Application upgrades:** if you consented to automatic operation in the sheet, the upgrade
  controls start unpaused and upgrades follow your policy inside your window (an approval policy
  still asks for your approval). If not, the upgrade controls start **paused** and
  nothing upgrades until they are enabled. See the
  [decision sheet](/docs/deployments/AWS/Tier-A/Decision-Sheet#what-each-decision-does).
* **Infrastructure is reported, not changed.** When infrastructure reporting is turned on (it is off
  by default; set `"infrastructure_reporting": true` in the sheet), the agent reports the cluster
  version, node-group health, instance health and add-on versions every 15 minutes. It has no AWS
  write permission. Capacity changes, node recovery and Kubernetes upgrades are still carried out
  by Genesis together with you.

## Removing Genesis

```sh theme={null}
uv run python -m scripts.aws_tier_a teardown --sheet "$SHEET" \
  --run-id <the-same-run-id> --aws-profile <your-aws-profile> --evidence "$EVIDENCE"
```

Pass the same `--evidence` directory you installed with: the teardown reads the install's record
there.

**Teardown deletes the application and its data.** It removes the application's namespaces and
storage volumes, then everything else the install created, including any volume snapshots this
run left. It then reads back 24 kinds of AWS resource, from VPCs and load balancers to IAM roles
and log groups. It reports success only when nothing the install created is left:

```text theme={null}
Zero-leftover AWS read-back passed; local OpenTofu state and kubeconfig removed.
```

The daily budget is kept; delete `genesis-tier-a-daily` if you no longer want it. Teardown does not
remove the deployment's record on the Genesis side: give Genesis the run ID (or the `dep_...`
identifier in `$EVIDENCE/<run-id>/receiver-deployments.json`), and Genesis retires it.

## Security questions

* **Where is the infrastructure state?** In the run directory on the install machine (an OpenTofu
  state file, not a remote bucket). It describes the created resources, including the cluster's
  endpoint and certificate authority. Keep that machine's home directory private.
* **Who can administer the cluster?** The principal that ran the install becomes the cluster's
  administrator (an EKS access entry). No Genesis staff identity is added. The management agent has
  only the Kubernetes permissions its chart grants inside the cluster.
* **Who can sign in to Genesis?** Anyone your Auth0 tenant authenticates for the application you
  named in `sign_in`, including through any access token the tenant's userinfo endpoint accepts.
  Accounts are created on first sign-in, and new users are administrators by default. Before
  enabling sign-in, turn off public sign-ups in the tenant and enable only the connections you
  intend on that application. Then have your intended administrator sign in first, and move
  everyone else to a non-administrator role (see the
  [decision sheet](/docs/deployments/AWS/Tier-A/Decision-Sheet#what-each-decision-does)).
* **Is the Kubernetes API private?** It is public, but it accepts connections only from your
  `api_cidrs`. It is also reachable privately from inside the VPC. A private-only endpoint is not
  offered in this version.
* **How powerful are the installer's permissions?** **Treat the installing principal as
  administrator-equivalent.** The generated policy (step 2) limits which resources each write may
  touch: roles and policies whose names start with the cluster name, this cluster, its log group,
  the `genesis-tier-a-daily` budget, and the sheet's region for network and instance writes. But
  limiting by name is not a privilege boundary. This principal can still escalate to account
  administrator: it could create a role named after the cluster, attach any policy to it, and pass
  it to an instance. Remove the principal (or its policy) after the install, or use a dedicated
  account.

## Limits of this version

* Targets are limited to those Genesis has approved in an installer profile.
* The install always creates a new VPC; it cannot use an existing one. EKS secrets encryption with
  your own KMS key is not yet offered.
* The application is opened through a local forward (`open`), on the fixed port 18080, not a public
  address or a hostname with its own certificate.
* Without consent to automatic operation, enabling the paused upgrade controls is done with you by
  Genesis.
* See the [permission appendix](/docs/deployments/AWS/Tier-A/Permission-Appendix) for the agent role's regional scope
  and the other stated limits.
