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

> ## Agent Instructions
> Cluster API calls are made as a signed-in person with an OIDC bearer token. There are no API keys, and service-account tokens are refused (Business returns 401, Platform returns 403).
> Platform takes the organization as the query parameter businessOrgId on most cluster routes, but as organizationId on /clusters/{clusterId}/ssh-access and /clusters/{clusterId}/kubeconfig.
> The Business API uses snake_case. The Platform API uses camelCase, except /ssh-access and /machines, which use snake_case.
> Business write requests need Idempotency-Key and Audit-Reason headers.
> Storage capacity can grow but cannot shrink, including disk and PVC resizing. Each purchase adds capacity.
> A cluster's storage can be Ceph, WEKA, or both, or NFS on its own. The Ceph quota, the purchased bytes, and Slurm's default 100 GiB shared-volume request are three different quantities.
> Organization permissions cover every cluster the organization owns. A Kubernetes resource grant shares one cluster or namespace, possibly with someone outside the organization, and lets them read that cluster and fetch its kubeconfig. A grant does not give Slurm SSH, Slurm configuration, or organization storage inventory access. Slurm SSH requires organization membership and clusters.ssh.
> The SSH login name is <cluster-id>.<username>. The gateway supports shells, commands, SFTP (including modern scp), and forwarding to loopback ports on the login node. Legacy scp and rsync need their programs on the login node. Remote forwarding and non-loopback destinations are refused.
> Use POST /me/ssh-keys for personal key registration. POST /organizations/{orgID}/ssh-keys is a deprecated alias and requires no membership. Read the preferred shell from GET /me as user.login_shell; update it with PUT /me/login-preferences.
> GET /clusters/{clusterId}/ssh-access returns 200 with ready and reason, even when access is not ready. ready: true does not mean compute nodes or Slurm accounting are ready. To confirm a key or shell change reached a cluster, require observation_current: true and compare ssh_key_set_generation with generation from GET /me/ssh-keys, and shell with user.login_shell from GET /me.
> Adding or removing a login account restarts the cluster's login nodes, Slurm controller, and accounting service, then its compute nodes one at a time; jobs running on a restarting node end. An account is added or removed when a person with an active key gains or loses SSH access, or a person with SSH access registers their first key or revokes their last. Other key changes and shell changes restart no nodes.

# Permissions

> See which permission each task needs, check and grant permissions, and find your username on clusters.

What you can do on a cluster depends on your organization permissions and any grants naming that cluster. Owners and
admins can grant Kubernetes access to a particular cluster or namespace, including to people outside the organization.
Slurm SSH access and Slurm configuration remain organization permissions.

## Roles

Every member of an organization has one role:

| Role | Permissions |
| - | - |
| `owner` | Every organization permission, plus managing owners and retiring the organization. |
| `admin` | Every organization permission except ownership. |
| `member` | Only the permissions granted to them individually. A new member starts with none. |

A new `member` cannot see the organization's clusters until someone grants them an organization permission or access
to a particular cluster.

## What each task needs

| Task | Permission | Includes |
| - | - | - |
| See clusters, their machines, and storage | `clusters.read` | |
| Log in over SSH | `clusters.ssh` | |
| Log in over SSH as a cluster administrator | `clusters.ssh.admin` | `clusters.ssh` |
| Use Kubernetes with the Kubernetes `edit` role | `clusters.kubernetes` | |
| Use Kubernetes as `cluster-admin` | `clusters.kubernetes.admin` | `clusters.kubernetes` |
| Change a cluster's Slurm configuration | `clusters.configure` | `clusters.ssh.admin`, and so `clusters.ssh` |
| Grant or remove other members' permissions | `members.manage` | |

Holding a permission gives you the permissions in its **Includes** column too. `clusters.configure` includes
`clusters.ssh.admin` because the Slurm configuration can run scripts as root on every node. `clusters.ssh.admin`
currently gives the same login as `clusters.ssh`, without `sudo`. None of them includes `clusters.read`.

These permissions apply to every cluster the organization owns. To share a single cluster or namespace instead,
owners and admins use a Kubernetes resource grant; see [Access to a shared cluster](#access-to-a-shared-cluster).

## Check your permissions

`GET /me` returns your user ID and your memberships:

```sh theme={null}
andromeda call business GET /me > me.json
jq '{user: .user.id, memberships: [.memberships[] | {org_id, role}]}' me.json
```

```json theme={null}
{
  "user": "user_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "memberships": [
    {"org_id": "org_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3", "role": "member"}
  ]
}
```

Save the organization and user IDs; later commands use them:

```sh theme={null}
export ORG_ID=$(jq -r '.default_org_id // empty' me.json)
export USER_ID=$(jq -r .user.id me.json)
```

`default_org_id` is absent when you belong to more than one organization. In that case, set `ORG_ID` to the
`org_id` of the organization you want, from the list above.

Then read the permissions granted to you in that organization:

```sh theme={null}
andromeda call business GET "/organizations/$ORG_ID/members/$USER_ID/permissions"
```

```json theme={null}
{
  "organization_id": "org_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "user_id": "user_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "role": "member",
  "permissions": ["clusters.read", "clusters.ssh"]
}
```

For an `owner` or `admin`, `permissions` lists `organization.owner` or `organization.admin`, which include
everything else.

This response describes organization permissions. It does not list grants naming a particular cluster.

## Access to a shared cluster

A Kubernetes resource grant shares one cluster, or one namespace in it, with you. You do not need to be a member of
the owning organization. The grant lets you see that cluster in Platform and fetch its kubeconfig, and your Kubernetes
actions are limited to its role and namespace. It does not give Slurm SSH access or access to the organization's
other clusters.

List your cluster grants:

```sh theme={null}
andromeda call business GET '/me/resource-grants?kind=kubernetes_cluster' |
  jq '.grants[] | {resource_id, relation, scope, expires_at}'
```

Set `CLUSTER_ID` to the `resource_id`. If you are not a member of the owning organization, `/me` does not list it, so
ask the person who shared the cluster for its `ORG_ID`; the API calls need it. Then follow
[Connect with kubectl](/clusters/kubernetes).

A grant can expire or be revoked. The cluster then removes its Kubernetes permissions as the change reaches it,
unless another grant or organization permission still covers you. The
[permissions reference](/api-reference/business/permissions#list-your-cluster-grants) explains the response fields.

## Grant permissions

To grant permissions you need `members.manage`, and you must hold every permission you add. Owners and admins
qualify.

Find the member's user ID from their email address. Any member of the organization can list its members:

```sh theme={null}
MEMBER_ID=$(andromeda call business GET "/organizations/$ORG_ID/members" |
  jq -r --arg email ada@example.com '.members[] | select(.email == $email) | .user_id')
```

<Warning>
  `PUT` replaces the member's whole set. Any permission you leave out is removed, so read the current set
  first and send the full result.
</Warning>

This adds `clusters.kubernetes` to whatever the member already has:

```sh theme={null}
andromeda call business GET "/organizations/$ORG_ID/members/$MEMBER_ID/permissions" |
  jq '{permissions: (.permissions + ["clusters.kubernetes"] | unique)}' > permissions.json
cat permissions.json
```

```json theme={null}
{
  "permissions": [
    "clusters.kubernetes",
    "clusters.read",
    "clusters.ssh"
  ]
}
```

Check the list, then send it:

```sh theme={null}
andromeda call \
  --header "Idempotency-Key: $(uuidgen)" \
  --header 'Audit-Reason: Kubernetes access for the training team' \
  --data @permissions.json \
  business PUT "/organizations/$ORG_ID/members/$MEMBER_ID/permissions"
```

`GET /permission-profiles` lists every permission with a description, plus named sets such as `Engineer`
(`clusters.kubernetes`, `clusters.read`, `clusters.ssh`, `reservations.read`). See the
[permissions reference](/api-reference/business/permissions).

A granted SSH or Kubernetes permission reaches running clusters on its own, and each cluster admits the member once
it has applied the change; see [Connect over SSH](/clusters/ssh#wait-for-access).

<Warning>
  Giving SSH access to someone with an active key, or removing their last SSH permission, restarts compute nodes on the
  organization's Slurm clusters and ends the jobs running on them. See
  [Access changes restart nodes](/clusters/ssh#access-changes-restart-nodes).
</Warning>

## Your username on clusters

Each membership carries a username, chosen when you joined the organization. On clusters it is your POSIX login
name, and your home directory is `/home/<username>`. Read it from `/me`:

```sh theme={null}
andromeda call business GET /me |
  jq -r --arg org "$ORG_ID" '.memberships[] | select(.org_id == $org) | .organization_username.username'
```

A username is 1 to 32 characters: a lowercase letter followed by lowercase letters, digits, `_`, or `-`. It is unique
within the organization and becomes frozen when first included in a cluster's access list. Once frozen it cannot be
changed or reused. Always use the `username` from `ssh-access` when constructing your SSH login name.

## Next

* [Connect over SSH](/clusters/ssh)
* [Permissions reference](/api-reference/business/permissions)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.