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

# Compute permissions

> Every IAM action the compute service checks and how to scope collection permissions by region and account.

Every compute endpoint checks one IAM action before it does anything, with one
exception noted below.

<Info>
  The API reference shows the action on each endpoint's own page, so you do not
  have to come back here to look one up. Both come from the same place: the
  authorization call in the service, read at build time.
</Info>

## Resources

| Resource         | CRN                                                                                 | Keyed by                                               |
| ---------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------ |
| Instance         | `crn:compute:<region>:<account>:instance/<name>`                                    | immutable name                                         |
| Instance pool    | `crn:compute:<region>:<account>:instance-pool/<name>`                               | immutable name                                         |
| Keypair          | `crn:compute:<region>:<account>:keypair/<name>`                                     | **name**                                               |
| Image (yours)    | `crn:compute:<region>:<account>:image/<name>/architecture/<arch>/version/<version>` | name, architecture and version                         |
| Image (platform) | `crn:compute:<region>:platform:image/<name>/architecture/<arch>/version/<version>`  | name, architecture and version in the platform account |
| Flavor           | `crn:compute:<region>:platform:flavor/<name>`                                       | name, platform account slot                            |

Named resources can be addressed readably in a policy. For example, a keypair
CRN carries its immutable name:

```
crn:compute:sa-saopaulo-1:my-account:keypair/ci-*
```

Instance and pool CRNs carry immutable names. Image CRNs pin name, architecture
and version. Platform images and flavors use the `platform` account handle.

## The actions

| Action                         | Call                                                                | Checked against                                  |
| ------------------------------ | ------------------------------------------------------------------- | ------------------------------------------------ |
| `compute:AttachNIC`            | `POST /v1/instances/{instance_id}/nics`                             | the instance                                     |
| `compute:AttachVolume`         | `POST /v1/instances/{instance_id}/volumes`                          | the instance                                     |
| `compute:AttachVolume`         | `PATCH /v1/instances/{instance_id}/volumes/{volume_id}`             | the instance                                     |
| `compute:CreateImage`          | `POST /v1/images`                                                   | `crn:compute:<region>:<account>:image/*`         |
| `compute:CreateInstance`       | `POST /v1/instances`                                                | `crn:compute:<region>:<account>:instance/*`      |
| `compute:CreateInstancePool`   | `POST /v1/instance-pools`                                           | `crn:compute:<region>:<account>:instance-pool/*` |
| `compute:CreateKeypair`        | `POST /v1/keypairs`                                                 | `crn:compute:<region>:<account>:keypair/*`       |
| `compute:DeleteImage`          | `DELETE /v1/images/{image_id}`                                      | the image                                        |
| `compute:DeleteInstance`       | `DELETE /v1/instances/{instance_id}`                                | the instance                                     |
| `compute:DeleteInstancePool`   | `DELETE /v1/instance-pools/{pool_id}`                               | the pool                                         |
| `compute:DeleteKeypair`        | `DELETE /v1/keypairs/{keypair_id}`                                  | the keypair                                      |
| `compute:DetachNIC`            | `DELETE /v1/instances/{instance_id}/nics/{interface_id}`            | the instance                                     |
| `compute:DetachVolume`         | `DELETE /v1/instances/{instance_id}/volumes/{volume_id}`            | the instance                                     |
| `compute:GetConsoleOutput`     | `GET /v1/instances/{instance_id}/console/output`                    | the instance                                     |
| `compute:GetConsoleScreenshot` | `GET /v1/instances/{instance_id}/console/screenshot`                | the instance                                     |
| `compute:GetFlavor`            | `GET /v1/flavors/{flavor_id}`                                       | the flavor                                       |
| `compute:GetImage`             | `GET /v1/images/{image_id}`                                         | the image                                        |
| `compute:GetInstance`          | `GET /v1/instances/{instance_id}`                                   | the instance                                     |
| `compute:GetInstance`          | `GET /v1/instances/{instance_id}/nics`                              | the instance                                     |
| `compute:GetInstance`          | `GET /v1/instances/{instance_id}/volumes`                           | the instance                                     |
| `compute:GetInstancePool`      | `GET /v1/instance-pools/{pool_id}`                                  | the pool                                         |
| `compute:GetInstancePool`      | `GET /v1/instance-pools/{pool_id}/floating-ips`                     | the pool                                         |
| `compute:GetInstancePool`      | `GET /v1/instance-pools/{pool_id}/instances`                        | the pool                                         |
| `compute:GetKeypair`           | `GET /v1/keypairs/{keypair_id}`                                     | the keypair                                      |
| `compute:ListFlavors`          | `GET /v1/flavors`                                                   | `crn:compute:<region>:platform:flavor/*`         |
| `compute:ListImages`           | `GET /v1/images`                                                    | `crn:compute:<region>:<account>:image/*`         |
| `compute:ListInstancePools`    | `GET /v1/instance-pools`                                            | `crn:compute:<region>:<account>:instance-pool/*` |
| `compute:ListInstances`        | `GET /v1/instances`                                                 | `crn:compute:<region>:<account>:instance/*`      |
| `compute:ListKeypairs`         | `GET /v1/keypairs`                                                  | `crn:compute:<region>:<account>:keypair/*`       |
| `compute:RebootInstance`       | `POST /v1/instances/{instance_id}/reboot`                           | the instance                                     |
| `compute:ReinstallInstance`    | `POST /v1/instances/{instance_id}/reinstall`                        | the instance                                     |
| `compute:ResizeInstance`       | `POST /v1/instances/{instance_id}/resize`                           | the instance                                     |
| `compute:StartInstance`        | `POST /v1/instances/{instance_id}/start`                            | the instance                                     |
| `compute:StartSerialConsole`   | `GET /v1/instances/{instance_id}/console/serial`                    | the instance                                     |
| `compute:StopInstance`         | `POST /v1/instances/{instance_id}/stop`                             | the instance                                     |
| `compute:UpdateImage`          | `PATCH /v1/images/{image_id}`                                       | the image                                        |
| `compute:UpdateInstance`       | `PATCH /v1/instances/{instance_id}`                                 | the instance                                     |
| `compute:UpdateInstancePool`   | `PATCH /v1/instance-pools/{pool_id}`                                | the pool                                         |
| `compute:UpdateInstancePool`   | `POST /v1/instance-pools/{pool_id}/floating-ips`                    | the pool                                         |
| `compute:UpdateInstancePool`   | `DELETE /v1/instance-pools/{pool_id}/floating-ips/{floating_ip_id}` | the pool                                         |
| `compute:UpdateInstancePool`   | `POST /v1/instance-pools/{pool_id}/refresh`                         | the pool                                         |
| `none`                         | `POST /v1/instances/{instance_id}/console/ticket`                   | no policy check                                  |

## Scope collection permissions

Create and list actions check a typed collection CRN with the configured region
and requesting account handle. For example,
`crn:compute:sa-saopaulo-1:my-account:instance/*` grants instance creation and
listing in that region and account; a policy naming another region or account
does not.

Flavors are a region-global catalog: `compute:ListFlavors` checks
`crn:compute:<region>:platform:flavor/*` using the `platform` account handle.

Image listing and creation check
`crn:compute:<region>:<account>:image/*` using the requesting account, including
the platform account for `compute:CreateImage`. Listing can return
platform images with `platform` in their object CRNs; those object CRNs are distinct
from the collection used to authorize the request.

Existing grants with `"resources": ["*"]` still work. Typed resource patterns
in deny statements now match these collection actions too, and an explicit
deny takes precedence over an allow.

This policy allows launching and listing instances in one account and region.
Its separate statement limits operations on existing instances by tag:

```json theme={null}
{
  "version": "2024-01-01",
  "statement": [
    {
      "effect": "allow",
      "actions": ["compute:CreateInstance", "compute:ListInstances"],
      "resources": ["crn:compute:sa-saopaulo-1:my-account:instance/*"]
    },
    {
      "effect": "allow",
      "actions": [
        "compute:GetInstance", "compute:StartInstance", "compute:StopInstance"
      ],
      "resources": ["crn:compute:sa-saopaulo-1:my-account:instance/*"],
      "conditions": [
        { "operator": "equals",
          "key": "basalt:ResourceTag/env", "values": ["staging"] }
      ]
    }
  ]
}
```

Create actions expose submitted tags as `basalt:RequestTag/<key>`. Keep
`basalt:ResourceTag/<key>` conditions on existing-resource operations: the
instance does not exist yet during creation, and list checks name a collection.

## Reading is not one action

Three console endpoints require three different actions, deliberately:

| Action                         | What it gives                                                 |
| ------------------------------ | ------------------------------------------------------------- |
| `compute:GetConsoleOutput`     | The boot transcript — what the kernel and cloud-init printed. |
| `compute:GetConsoleScreenshot` | A picture of the current display.                             |
| `compute:StartSerialConsole`   | A **keyboard inside the guest**.                              |

The third is not a read. It is interactive access to a running machine, and it
is the one to withhold from anyone who only needs to diagnose a boot.

<Note>
  Minting a console ticket (`POST /v1/instances/{instance_id}/console/ticket`)
  checks **no IAM action at all**. It requires authentication and org context
  and nothing else, because the ticket carries identity, not authorization:
  whether the console may actually open is decided when the socket connects,
  against `compute:StartSerialConsole` as policy stands at that moment.

  Freezing the decision into the ticket would mean a permission revoked in the
  intervening minute still let the console open. A ticket for an instance you
  cannot reach is issued and then useless.
</Note>

## Sub-resources are governed by their parent

Listing an instance's NICs or volumes needs `compute:GetInstance` — there is no
separate read action for either. The same pattern covers pools: reading a
pool's floating IPs or its instances needs `compute:GetInstancePool`, and
attaching an address, detaching one or refreshing the pool all need
`compute:UpdateInstancePool`.

<Warning>
  That means `compute:UpdateInstancePool` is one grant covering three quite
  different things: resizing the pool, rolling every member onto a new
  template, and attaching a public address to it. There is no way to allow the
  resize and withhold the roll.
</Warning>

## An action whose name does not match the call

<AccordionGroup>
  <Accordion title="PATCH a volume attachment needs compute:AttachVolume" icon="triangle-alert">
    `PATCH /v1/instances/{instance_id}/volumes/{volume_id}` changes
    `delete_on_termination` — the flag that decides whether a volume survives
    the instance being deleted. It is authorized by **`compute:AttachVolume`**.

    The audit record for it reads `compute:UpdateVolumeAttachment`, which is a
    label rather than an action: no policy can grant it, and searching for it
    finds nothing.
  </Accordion>
</AccordionGroup>

## Image ownership limits permissions

All accounts use `compute:CreateImage`, `compute:UpdateImage` and
`compute:DeleteImage`, including the platform account. There are no separate
`CreatePlatformImage`, `UpdatePlatformImage` or `DeletePlatformImage` actions.

Update and delete require the selected account to own the image. Public catalog
visibility and wildcard IAM grants do not bypass ownership. A request from a
customer account to change a platform image returns `403`; another customer's
image returns `404`. Select the owning account and grant the ordinary image
action on its CRN to manage it.

Withdrawn images remain inspectable with the ordinary read actions. An owner
can delete one after removing instance and pool references. Deleting images
remain readable during cleanup, but mutation requests return `404`.

## Next

<CardGroup cols={2}>
  <Card title="Writing policies" icon="file-text" href="/iam/policies">
    Conditions, operators, and how `deny` is evaluated.
  </Card>

  <Card title="Troubleshooting" icon="life-buoy" href="/compute/troubleshooting">
    What a status means, and why a launch failed.
  </Card>
</CardGroup>
