# How It Works

Learn how LeftSize scans your infrastructure, detects findings, and delivers them as GitHub Issues.

LeftSize runs as a GitHub Action in your own workflow, scans your cloud with read-only access, and turns what it finds into GitHub Issues in your repository.

## Architecture overview

```
Your Repository          LeftSize Action           LeftSize API
┌──────────────┐        ┌──────────────┐        ┌──────────────┐
│ .github/     │        │ Cloud        │        │ Template     │
│ workflows/   │───────>│ Custodian    │───────>│ Engine       │
│ leftsize.yml │  runs  │ Policies     │ submit │              │
└──────────────┘        └──────────────┘ findings└──────┬───────┘
                                                       │
                                                       │ creates
                                                       v
                                                ┌──────────────┐
                                                │ GitHub       │
                                                │ Issues       │
                                                └──────────────┘
```

## The scan cycle

### 1. Scheduled trigger

The workflow runs once a day at a fixed time that is different for each repository. You can also start a scan from the Actions tab.

### 2. Cloud authentication

The workflow signs in to your cloud with OIDC. GitHub Actions requests a short-lived token that is valid only for that run, so no cloud credentials are stored.

### 3. Policy execution

The LeftSize Action runs [Cloud Custodian](https://cloudcustodian.io/) policies against your account. They check for:

- **Cost optimization**: idle, unused and over-provisioned resources
- **Deprecations & retirements**: retired or out-of-support versions, SKUs and services
- **Security**: public exposure, unused credentials, missing MFA or logging (AWS)
- **Governance**: missing tags, encryption, backups and audit settings

The policies only read. They query resource configuration and CloudWatch or Azure Monitor metrics and never change anything. See the [rules catalog](rules.md) for every rule.

### 4. Findings submission

The action groups findings by rule and scope (for example, all idle VMs in one resource group, or all unused Elastic IPs in one account and region) and sends them to the LeftSize API with the repository token.

### 5. Issue creation

LeftSize opens one issue per group. Each issue has:

- A title with a category prefix, such as `[COST]`, `[SEC]` or `[DEPRECATION]`
- The affected resources with the relevant details
- Estimated monthly savings (for cost findings)
- The `@leftsize` commands you can use for more guidance

On the Free plan, issues are created for cost optimization rules only. The other rules still run and the scan summary shows their findings, but they don't get issues.

### 6. Issue lifecycle

- **New finding:** LeftSize opens an issue.
- **Same finding on a later scan:** LeftSize updates the issue's resource list and counts. Editing the issue body doesn't notify anyone. Labels you add are kept.
- **Problem fixed:** LeftSize doesn't close issues itself. When you've fixed the resources, close the issue.
- **Closed issue:** LeftSize doesn't reopen it or open a new one for the same rule and scope, even if the problem comes back. A deleted issue counts as closed.
- **Snoozed issue:** this is the one exception. After the snooze period, the next scan that still finds the problem reopens the issue. See [snooze](commands.md#leftsize-snooze-duration).

If GitHub Issues are turned off in the repository, findings are still stored but no issues are created.

## What runs where

| Component | Runs in | Has access to |
|-----------|---------|---------------|
| GitHub Actions workflow | Your repository's runner | Your cloud (via OIDC) |
| LeftSize Action | Your repository's runner | Cloud APIs (read-only), LeftSize API |
| LeftSize API | LeftSize infrastructure | GitHub API (to create and update issues) |

Your cloud credentials never leave your runner. For each finding, the LeftSize API receives:

- The resource ID: the Azure resource ID or AWS ARN, which includes the subscription or account ID, resource group, region and resource name
- The rule that matched, and the scope
- The resource's tags and the configuration fields the rule uses, such as size, SKU or engine version
- The estimated monthly savings

It never receives credentials or the data inside your resources. The [privacy policy](https://leftsize.com/legal/privacy/) explains how this data is stored and how long it is kept.

## Detection methods

### Metrics-based detection

For resources like VMs, databases and load balancers, LeftSize reads CloudWatch or Azure Monitor metrics over a recent window, usually 7 or 14 days. A resource is flagged when its usage stays below the rule's threshold for that whole window.

### Configuration-based detection

For things like unattached disks, unused IP addresses, missing tags or retiring versions, LeftSize checks the resource's configuration directly. These checks don't need metrics.

### Age filters

Most cost rules skip resources that were created recently, so a resource you just deployed isn't flagged before it had a chance to be used.

### Cost estimation

Where possible, LeftSize estimates monthly savings from the resource type, size and public list prices in USD (pay-as-you-go / on-demand): Azure prices from the Azure Retail Prices API, AWS prices from the Infracost Cloud Pricing API. A rule that removes a resource saves its full price; a nightly and weekend shutdown (12 hours on weekdays) saves about 64%; turning off Multi-AZ saves half. Negotiated discounts, reservations, savings plans and usage-based charges (data transfer, requests, extra IOPS) aren't taken into account. When a price can't be found reliably, the issue shows no amount rather than a guess.

## Production vs non-production

Some rules only apply to non-production resources, such as shutting down dev VMs at night, stopping test AKS clusters, or dropping Multi-AZ and Business Critical on test databases. LeftSize decides the environment of each resource, using the first of these that applies:

1. The action's `environment-name` input, if it names an environment: one value for the whole scan (`PROD`, `DEV`), or `name=environment` pairs for a subscription that holds several environments (`Shop-NG-A=acceptance, Shop-NG-P=production`). Other values, such as the `MAIN` matrix label from onboarding, are ignored.
2. An `Environment`, `Env` or `Stage` tag on the resource, then on its Azure resource group.
3. Words in the resource name or resource group name: `prod`, `prd`, `live` (production); `acc`, `uat`, `preprod` (acceptance); `stg`, `staging`; `test`, `tst`, `qa`; `dev`; `sandbox`; `nonprod`. Only whole words count, so `latest` isn't `test`.
4. A one-letter last word in the resource group name: `-P`, `-A`, `-T` or `-D` (for example `Shop-NG-A`).
5. The account the resource is in, for setups with one account per environment: the AWS account name or alias, or the Azure subscription name or its `Environment` tag (for example `shop-acc`).

Any production signal wins, and a resource without clear non-production evidence is treated as unknown, so non-production advice never reaches it. Issues show the environment of each resource and what it was based on. If one is wrong, add an `Environment` tag or set `environment-name`.

## Reports and statistics

The action also has a **stats mode** that reports findings, open issues and estimated savings for the repository. It doesn't scan your cloud; it reads from the LeftSize API. See [Reports](reports.md).
