# CLI and SSH

Source: https://heaven.dev/docs/cli

Run the same commands from a terminal, signed in or with your SSH key, and SSH straight to a Computer.

## Overview

Every endpoint's command also runs from a terminal, with the same name, and with its fields as flags. `heaven domain add my-site example.com` is the `domain add` request, with the same permission check as the API. It prints what `ssh heaven.dev COMMAND` prints, and `--json` prints the endpoint's JSON. `heaven help` lists the commands, and `heaven help COMMAND` explains one.

```sh
curl -fsSL https://heaven.dev/install | sh
heaven login
heaven create my-site --pool web
```

## Sign in

`heaven login` prints a code and opens a link in your browser to approve it. The program then keeps the [sign-in key](https://heaven.dev/docs/authentication#device-sign-in) it gets, for the key's 30 days, in a file only you can read: `~/.config/heaven/sign-in/heaven.dev.json`. Its commands go to the API with that key, so you don't need an SSH key. The sign-in also offers [the program's own SSH key](https://heaven.dev/docs/cli#ssh) for `heaven ssh`, so keep its box ticked when you approve. `heaven logout` revokes the key and forgets it.

- **Scripts and CI** set `HEAVEN_API_KEY` for a run, or keep a key with `heaven login --key-stdin < key.txt`. A key limited to some commands can run only those. `heaven logout` forgets a key you gave it, without revoking it.
- **Without a sign-in,** commands go over SSH with your computer's SSH key, as `ssh heaven.dev COMMAND` does, and the program keeps no secret. When both work, the program uses the sign-in.
- **A sign-in that expired or was revoked** makes commands exit with status 255 and a message to run `heaven login` again. The program doesn't fall back to SSH on its own. To go back to SSH, run `heaven logout`.

## Commands

When you're signed in, each of these goes to its endpoint over HTTPS. Without a sign-in, it runs as the last column shows.

| Run | Does | Without a sign-in |
| --- | --- | --- |
| `heaven COMMAND …` | Any endpoint's command, such as `view`, `pool view` or `restart my-site` | `ssh heaven.dev COMMAND …` |
| `heaven ssh NAME` | Opens a shell, as the browser terminal does. Signed in, it uses [the program's own key](https://heaven.dev/docs/cli#ssh). | `ssh NAME@heaven.dev` |
| `heaven exec NAME CMD` | Runs `CMD`, with standard input and exit status ([`exec`](https://heaven.dev/docs/exec#stream)) | `ssh NAME@heaven.dev CMD` |
| `heaven exec NAME --download PATH` | Writes the Computer's file to standard output ([download](https://heaven.dev/docs/exec#download)) | SFTP to `NAME@heaven.dev` |
| `heaven exec NAME --upload PATH` | Replaces the file with standard input, once all of it has arrived. `--mode MODE` sets its permissions ([upload](https://heaven.dev/docs/exec#upload)) | SFTP to `NAME@heaven.dev` |
| `heaven export NAME` | Writes the Computer's whole disk to standard output, as in `> disk.img.zst` ([export](https://heaven.dev/docs/exec#export)) | `ssh export+NAME@heaven.dev` |
| `heaven mcp` | Serves the commands to a local agent, which starts it ([MCP](https://heaven.dev/docs/mcp#local)) | SSH |
| `heaven mcp install` | Adds `heaven mcp` to Claude Code, Codex, Gemini CLI, Claude Desktop, Cursor and VS Code on this computer ([MCP](https://heaven.dev/docs/mcp#local)) |  |
| `heaven logout` | Revokes the sign-in and the program's own SSH key, and forgets both |  |

- **Exit status** matches SSH's. A command exits `1` when it's refused or when some of a change's targets failed. `heaven exec` exits with its command's status, or `255` when it's refused. Every command exits `255` when Heaven can't be reached or refuses the credential.
- **Retries.** When you're signed in, a change that [takes a request ID](https://heaven.dev/docs/operations#retries) always sends one: your `--request-id ID`, or one the program makes. If the connection drops before a response, the program sends the change once more with the same ID, which returns the first result rather than doing it again. If there's still no response, it prints the ID. Run the command again with `--request-id ID` to get its result.
- **Time limits.** When you're signed in, `exec`, files and `export` are streams of up to an hour, as in the browser terminal, so start a longer command in the background. Over SSH, a shell, `exec` and `export` connect to the Computer itself, with no time or size limit.

## SSH

`ssh NAME@heaven.dev` reaches a Computer, and `scp`, SFTP, port forwarding and editors use the same address. You need ownership or root access on the Computer. You have it if you own the Computer, if you're an admin of the team or organization that owns it, or if a share or a team gives you `root` or `admin`. The `web` role reaches only the website.

- **The program's own key.** When you're signed in, `heaven ssh` doesn't need an SSH key of yours. `heaven login` makes one for this computer, `~/.config/heaven/ssh/heaven.dev`, and offers it with the sign-in. Approving with "Add its SSH key" ticked adds it to your account as `Heaven on HOSTNAME`. It reaches every Computer you can SSH to, as a key you add does, and `heaven logout` removes it. If you approved without it, or removed it since, `heaven ssh` says so and prints the key. Run `heaven logout`, then `heaven login` with the box ticked, or add the key on the dashboard's SSH keys page.
- **Losing access** ends your open sessions within two seconds. That happens when a share is removed, you leave a team or the organization, a Computer moves out of a shared pool, or a Computer is transferred.
- **A paused Computer** refuses new connections with a line saying how to resume it, and its open sessions end.
- **A static address** doesn't accept your key on port 22. SSH to the Computer is still `ssh NAME@heaven.dev`.
- **Several organizations.** Commands take `--organization`, as the endpoints take `organization`.
- **An SSH key limited to one Computer or a tag** reaches only those Computers. It can't run commands or export ([SSH keys](https://heaven.dev/docs/account#ssh-keys)).
