# The CLI

Every seal command: signing in, sealing, granting, changing settings, deploying and reading telemetry.

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

Version 0.2.7. The CLI checks what it reads against the transparency log, with trust built into the binary, so it never takes the console's word for anything.

## Install and sign in

```shell
# With the Python SDK (Linux and macOS)
pip install sealzone
# Or the CLI alone
cargo install sealzone-cli

seal login
seal use acme
```

Both installs give you the same `seal` binary. There are no Windows builds. `seal login` keeps your login in the OS keychain, or under `$SEAL_HOME/logins/` on machines without one. `seal use` saves the tenant the other commands work on, after checking its admin keys and settings against the log.

## Commands

Every command takes `--help`. For identity flags, the help names the cloud command that prints each id.

### Everyday

| Command | What it does |
|---|---|
| `seal login` | Signs in with a code you approve in the browser. `--no-browser` prints the link instead. |
| `seal logout` | Signs out and revokes this machine's console login. |
| `seal whoami` | Shows who you're signed in as and your workspaces. |
| `seal use [TENANT]` | Picks and verifies the tenant the other commands work on. Without an id, lists the tenants in your workspaces. |
| `seal status` | Root id, admin keys, state in force, apps, grants and pending changes. `--log` also scans the log for anything that shouldn't be there, such as a second root claiming your tenant's name, and exits 1 if it finds one. |
| `seal secret` | Seals a secret for one or more apps and prints the blob. Reads the secret from the terminal, or from stdin, so it never lands in your shell history. |

```shell
seal status --log
seal secret --to https://api.openai.com/v1 --app checkout < key.txt > openai.blob
seal secret --to postgres://app@db.example.com:5432/prod --app checkout
```

`seal secret` takes these flags. For a database, it reads only the password, and the rest goes in `--to`. [Sealing a secret](https://seal.zone/docs/sealing) explains each option.

| Flag | What it does |
|---|---|
| `--to URL` | Where the secret may be used: `https://`, `postgres://` or `rediss://`. Required. |
| `--app NAME` | An app that may use it. Repeat, or separate with commas. Required. |
| `--header NAME` | The HTTP header that carries the secret. Default `authorization`. |
| `--format TEMPLATE` | The header value, with `{key}` where the secret goes. Default `Bearer {key}` for `authorization`, else `{key}`. |
| `--methods M,…` | The only HTTP methods allowed. Default: any. |
| `--deny PATH` | Refuses requests under this path prefix, such as an API's key-management endpoints. |
| `--expires DATE` | The blob stops working at this time: RFC 3339, `YYYY-MM-DD` in UTC, or Unix seconds. |
| `--pin sha256/B64` | Requires this key in the upstream's certificate chain. Up to 4. Pin a CA key so leaf renewals don't break it. |
| `--sealed-only` | Only enclaves on real TEE hardware may open the blob. |
| `--receipt-file FILE` | Appends a telemetry receipt here, so you can later prove the seal was logged. |
| `--tenant T@ROOT` | The tenant, as `seal status` prints it. Default: the one from `seal use`. With `--tenant` and `--endpoint`, anyone can seal without signing in. |
| `--endpoint URL` | A replica to seal on. Default: the replicas Seal runs for your tenant. |

### Who may do what

Every grant and revoke goes through the [change flow](#how-changes-work).

| Command | What it does |
|---|---|
| `seal grant app NAME TYPE` | Lets an app start sessions as an identity. The first grant creates the app. |
| `seal grant network APP CIDR…` | Limits an app to sessions from these networks. The first network turns the rule on. |
| `seal grant enclave azure` | Lets an enclave deployment serve the tenant, named by its managed identity with `--object-id`, `--entra-tenant` and `--audience`. `seal deploy azure` runs it for you. `aws` and `confidential-space` are listed for platforms Seal doesn't run on yet. |
| `seal revoke app APP [TYPE …]` | Takes back one identity of an app, or the whole app. Applies at once, and replicas cut open sessions within about 30 seconds. |
| `seal revoke enclave azure` | Takes back a deployment. |
| `seal revoke network APP [CIDR…]` | Removes some networks, or the whole rule when none are named. |
| `seal changes` | Lists proposals waiting for signatures, and logged changes still waiting out the pending window. |
| `seal changes show ID` | One proposal: its differences from the state in force, and its signatures. |
| `seal changes sign ID` | Adds your signatures with `--key-file` or `--passkey` (a USB security key). Without either, opens the console. |
| `seal changes submit ID` | Submits a proposal that already has its signatures. |
| `seal changes cancel ID` | Deletes a proposal. |
| `seal changes --watch` | Follows pending changes as they're logged, take effect or get replaced. `--interval` sets how often it checks, default 60 seconds. |
| `seal changes --watch --once` | Prints the state in force and every pending change, then exits 0 if nothing is pending, 3 if something is. |

```shell
seal grant app checkout azure --object-id OID --entra-tenant TID --audience api://seal-acme
seal grant app worker key --generate --out worker.key
seal grant network checkout 203.0.113.0/24
seal revoke app checkout key --public HEX   # one identity
seal revoke app checkout                    # the whole app
seal changes sign 3f9a1c --key-file admin.key
```

Each identity type names the identity by ids that can't be reused after it's deleted. [Apps and identities](https://seal.zone/docs/apps) helps you pick one.

| Identity | Flags |
|---|---|
| `azure` | `--object-id`, `--entra-tenant`, `--audience` |
| `aws` | `--role-arn`, `--role-id` |
| `k8s` | `--service-account NS/NAME`, `--issuer URL`, `--uid` |
| `gcp` | `--email`, `--unique-id` |
| `github` | `--repo OWNER/REPO`, `--repository-id`, `--owner-id`, and `--environment` or `--ref` |
| `key` | `--generate [--out FILE]` for a new app key, or `--public HEX` for an existing one |

A proposal ID can be its first few characters. Run `seal changes --watch --once` from cron or CI. It reads the log directly, so it catches a widening change even if the console never told you.

### Tenant settings

With no arguments, each command prints the current value. All take `--json`. [Admins and changes](https://seal.zone/docs/changes) explains windows and thresholds.

| Command | What it changes |
|---|---|
| `seal settings admins add KEY…` | Adds admin key files' public keys, from `seal key create`. Passkeys are added in the console. `--threshold N` changes the threshold in the same step. |
| `seal settings admins rm KEY…` | Removes admin keys, by public key or key id. |
| `seal settings admins threshold N` | How many admin keys must sign each change, from 1 up to the number of keys. |
| `seal settings window DURATION` | The pending window. Shortening it waits out the current window. |
| `seal settings platforms add\|rm PLATFORM` | The platforms new blobs are wrapped for. `azure` is the one Seal runs on today. |
| `seal settings integrity allow APP NAME=SHA256` | Allows an executable's hash for an app, whose sessions then need a matching hash. `--remove` takes it out. |
| `seal settings integrity on SEVERITY ACTION` | What happens when a signal of this severity or higher arrives: `log-only`, `deny`, `suspend`, `suspend=SECS`, or `none` to remove the rule. |
| `seal settings integrity clear IDENTITY` | Ignores an identity's signals up to now, which lifts a suspension. |
| `seal settings telemetry-readers add [KEY]` | Encrypts telemetry to a reader key. `--generate FILE` makes a new one. |
| `seal settings telemetry-readers rm KEY` | Stops encrypting to that reader. |
| `seal settings freshness key add\|rm` | Your own freshness signers, by public key or `--file`. |
| `seal settings freshness sign` | Signs one statement that the latest state is current. Run it from cron, well inside the freshness window. |
| `seal settings freshness window WINDOW` | How long a freshness statement keeps the state usable, from `5m` to `24h`, or `default` (24 hours). |
| `seal settings tenant-key bump` | New blobs use the next tenant key version. `--retire-older` also stops older versions, and their blobs stop working. See [Rotating and revoking](https://seal.zone/docs/revoking). |

```shell
seal settings window 24h
seal settings admins add 9c41e0… --threshold 2
seal settings integrity on high suspend
seal settings tenant-key bump --retire-older
```

### Deploy

`seal deploy` runs a replica in your own Azure subscription, inside the virtual network your apps run in. It works today. Running your own replica in production is part of the Business plan, coming soon; write to harvey@seal.zone. [Where the enclave runs](https://seal.zone/docs/hosting) compares it with a Seal-hosted replica.

| Command | What it does |
|---|---|
| `seal deploy azure` | Verifies the newest release, then creates a managed identity, the container group, and a `seal` subnet with a NAT gateway if the network has none. Signs the deployment grant through the change flow. |
| `seal deploy upgrade` | Moves a replica to the newest verified release, keeping its identity and network. Takes `-g`, `--name`, `--release` and `--dry-run`. |

```shell
seal deploy azure --vnet apps-vnet
seal deploy upgrade -g seal-rg
```

| Flag for `seal deploy azure` | What it does |
|---|---|
| `--vnet NAME\|ID` | The virtual network your apps run in. Asked for at a terminal if you leave it out. |
| `--subnet SUBNET\|ID` | A subnet you prepared, delegated to Azure Container Instances, with a NAT gateway. |
| `-g`, `--resource-group RG` | Where the identity, the container group and a new NAT gateway go. Default: the network's. |
| `--location REGION` | The Azure region. Default: the network's. |
| `--name NAME` | The container group. Default `seal-TENANT`. Use one per replica. |
| `--identity NAME` | The managed identity, created if missing. Default `seal-TENANT`. Replicas can share it. |
| `--audience URI` | The Entra app ID URI the replica's token is for. Default: the one your other Azure deployments use, or a new one. |
| `--skip-grant` | Doesn't sign the grant. Prints the `seal grant enclave` command for an admin instead. |
| `--release MEASUREMENT` | Deploys this release instead of the newest verified one. |
| `--registry-user USER` | Pulls the image as this registry user, with the password in `SEAL_REGISTRY_PASSWORD`. |
| `--dry-run` | Verifies the release and prints the template and commands. Changes nothing. |

### Keys, telemetry and the rest

The CLI makes admin key files but never stores one. Keep key files offline, and keep a backup admin key. If you lose your only one, nobody can change the tenant again.

| Command | What it does |
|---|---|
| `seal key create` | Writes a new Ed25519 private key with owner-only permissions and prints its public key. Never overwrites a file. Without `--out`, writes to stdout, which must not be a terminal. |
| `seal key public FILE` | Prints the public key of an existing key file. |
| `seal telemetry` | Downloads the tenant's telemetry and checks every batch. Reports gaps, streams that stop without a last batch, and receipts with no matching entry. See [Telemetry](https://seal.zone/docs/telemetry). |
| `seal release approve ID` | Co-signs an enclave release with your admin keys. Only for tenants whose root requires their own signature on releases. Repeat `--key-file` up to your threshold. |
| `seal completion SHELL` | Prints completions for `bash`, `zsh`, `fish`, `elvish` or `powershell`. |

```shell
seal key create --out admin.key
seal telemetry --since 2h
seal telemetry --receipts receipts.jsonl --reader-key reader.key
seal release approve 5be1c0… --key-file admin.key
seal completion zsh > ~/.zfunc/_seal
```

| Flag for `seal telemetry` | What it does |
|---|---|
| `--since TIME` | Entries at or after this time: RFC 3339, `YYYY-MM-DD`, Unix seconds, or a duration ago such as `2h`. |
| `--receipts FILE` | Receipts your clients kept, as `seal secret --receipt-file` writes them. Any with no entry are reported. |
| `--reader-key FILE` | A telemetry reader key, to decrypt encrypted batches. |
| `--certificates FILE` | Enclave key certificates to verify against, instead of the ones the control plane serves. |
| `--json` | One JSON object per entry, then one per finding. |

## How changes work

Every command that changes the tenant runs the same way.

### It shows the diff

The CLI prints the change against the state in force, the settings your enclaves obey now. A narrowing change applies at once. A widening one waits out your [pending window](https://seal.zone/docs/changes) first.

### It proposes the change

The proposal goes to the control plane. The CLI prints its short id and opens its console page, where admins sign with passkeys.

### It waits until the change is logged

Once enough admins sign, the CLI submits the change and says when it's logged and when it takes effect. Ctrl-C stops the wait, not the proposal. Anyone can finish it with `seal changes sign`.

These flags work on every command that makes a change. The CLI holds no admin key of its own, so without `--key-file` the signatures come from your admins.

| Flag | What it does |
|---|---|
| `--key-file FILE` | Signs with an admin key file instead of in the console. Repeat for several signatures. With enough to meet the threshold, the CLI publishes the change straight away. |
| `-y`, `--yes` | Skips the confirmation prompt. |
| `--no-browser` | Prints the console link instead of opening it. |
| `--no-wait` | Proposes the change and exits. `seal changes` lists it. |
| `--freshness-key FILE` | Also signs a freshness statement for the new state, for tenants that run their own freshness signer. |

## Global flags, environment and exit codes

These work on every command. The flag wins over its variable.

| Flag | Variable | Default | What it sets |
|---|---|---|---|
| `--home` | `SEAL_HOME` | `~/.config/seal` | Where the CLI keeps its config: the tenant you picked and the root chain it verified. No keys or credentials. |
| `--console-url` | `SEAL_CONSOLE_URL` | console.seal.zone | The console to sign in to. |
| `--control-url` | `SEAL_CONTROL_URL` | control.seal.zone | Seal's control plane, where signed documents and telemetry live. |
| `--log-url` | `SEAL_LOG_URL` | log.seal.zone | The transparency log. |
| `--trust` | `SEAL_TRUST` | built in | A `trust.json` to verify with instead of the built-in one. For development. |
| `--no-color` | `NO_COLOR=1` | off | Plain output. |

| Variable | Used by | What it does |
|---|---|---|
| `SEAL_CONSOLE_TOKEN` | any command that talks to the console | Uses this token instead of the stored login. Handy in CI. |
| `SEAL_TENANT` | `seal secret` | The tenant to seal for, as `<tenant>@<root id>`. Same as `--tenant`. |
| `SEAL_ENDPOINT` | `seal secret` | Replica URLs, comma-separated. Same as `--endpoint`. |
| `SEAL_NO_BROWSER` | `login`, changes | Prints links instead of opening a browser. Same as `--no-browser`. |
| `SEAL_FRESHNESS_KEY` | changes, `settings freshness sign` | A freshness key file to sign new states with. Same as `--freshness-key`. |
| `SEAL_REGISTRY_PASSWORD` | `deploy` | The registry password that goes with `--registry-user`. |

| Exit code | Meaning |
|---|---|
| 0 | Done. |
| 1 | Error. Also `seal status --log` when it finds something wrong. |
| 2 | Usage error: a bad flag or a missing argument. |
| 3 | `seal changes --watch --once` found a change still pending. |
| 130 | You pressed Ctrl-C while the CLI waited for signatures. The proposal stays. |
