# Apps and identities

Grant an app, tell Seal how it proves who it is, and optionally limit where it connects from.

Source: https://seal.zone/docs/apps

## Apps and grants

An app is a name, like checkout. You choose it when you seal (`--app checkout`) and when you grant. A grant links the app to an identity, the way a running copy proves who it is. The first grant for a name creates the app.

Each grant is a signed tenant change. The CLI opens the console for your admin key's signature, or you can use the console's **Apps** page. Adding an identity widens access, so it waits out your [pending window](https://seal.zone/docs/changes).

One app can have several identities, such as a managed identity in production and a GitHub workflow for CI. Each is granted and removed on its own.

## Identity types

Prefer the identity your cloud already gives your app. Its tokens are short-lived and there's nothing to store. Use an app key only on your laptop or machines with no cloud identity.

| Identity | Grant flags | What the app needs |
|---|---|---|
| Azure managed identity | `azure --object-id --entra-tenant --audience` | `SEAL_AZURE_AUDIENCE`, plus `AZURE_CLIENT_ID` for a user-assigned identity |
| AWS IAM role | `aws --role-arn --role-id` | The usual AWS credentials. Rust crate with `--features aws` only. |
| Kubernetes service account | `k8s --service-account NS/NAME --issuer --uid` | A projected token with audience `seal:<tenant>` and `expirationSeconds: 600`. The issuer must be HTTPS your replicas can reach. |
| Google Cloud service account | `gcp --email --unique-id` | Nothing. The SDK asks the metadata server. |
| GitHub Actions workflow | `github --repo --repository-id --owner-id` plus `--environment` or `--ref` | `permissions: id-token: write` |
| App key | `key --generate --out FILE`, or `--public HEX` | `SEAL_APP_KEY` set to the file's `sealak_` line |

Grants use ids that never change, not display names. Something deleted and recreated under the same name doesn't inherit access. Each flag's `--help` says where to find its id. The SDK reads the Kubernetes token from `/var/run/secrets/seal.zone/token`, or from `SEAL_KUBERNETES_TOKEN_FILE`.

## Example: Azure managed identity

Azure issues tokens only for an audience an Entra application owns. Register one per tenant:

```shell
az ad app create --display-name seal-acme --query appId -o tsv
az ad app update --id <APP ID> --identifier-uris api://<APP ID>
az ad sp create --id <APP ID>
```

Then grant the identity. `az identity show` gives its `principalId` and `tenantId`.

```shell
seal grant app checkout azure --object-id <OID> --entra-tenant <TID> --audience api://<APP ID>
```

In the app, set `SEAL_AZURE_AUDIENCE=api://<APP ID>`.

The SDK uses an identity your code passes, or the first one it finds in the table's order. The app key comes last, so a stray `SEAL_APP_KEY` can't replace a cloud identity.

**Keep the app key away from the blob.**

If `SEAL_APP_KEY` and the blob are both in one process's environment, the SDK warns. Whoever reads that environment can use your secret as your app, from anywhere, until you revoke the key. In production, keep the blob elsewhere and add a network rule.

## Network rules

A network rule limits where an app may start sessions from.

```shell
seal grant network checkout 203.0.113.0/24 198.51.100.7/32
```

The first network applies at once. Later ones widen access and wait. `seal revoke network checkout` removes the rule. Rules stop a copied identity working from elsewhere. They don't help against whoever runs the replica's host, and on Seal's hosted replica they trust Seal's front door to report the address.

## Revoking an app

```shell
seal revoke app checkout azure --object-id <OID>   # one identity
seal revoke app checkout                           # every identity
```

Revoking applies once signed, with no wait. Replicas pick it up within about 30 seconds and cut open sessions too. Disabling the identity at your cloud is slower, up to 24 hours for cached Azure tokens. See [Rotating and revoking](https://seal.zone/docs/revoking).

## The artifact check

An allow list makes the enclave refuse sessions whose running executable hash isn't on it.

```shell
seal settings integrity allow checkout checkout-server=<SHA-256>
```

Python and Node report the interpreter's hash by default, so report your own with `endpoint_reporter`. The app reports its own hash, so root on that machine can lie.
