---
title: Machine commands
description: The polaris commands on every Machine, the tools Machines come with, and how to write setup scripts.
---

Every Machine has a `polaris` command that connects it to Polaris: it opens
Previews, shares screenshots, gives commands your variables, keeps long jobs
running and gets cloud credentials. The agent already knows these commands and
runs them when you ask, so you rarely need them yourself. Use this page to run
one in the [Terminal](/docs/terminal), to write setup scripts, or to see what you
can ask the agent for.

Some commands act for a Session, so they work only in the agent's shell and
refuse in a Terminal you open: `polaris port` (except `url` and `logs`),
`polaris services start`, `polaris share`, `polaris feedback`,
`polaris notify` and `polaris session`. Ask the agent to run those.

Run `polaris --help` on a Machine to list its commands. If a Machine doesn't
know a command on this page, start a new Session to get a new Machine. The CLI
on your own computer is a different tool; see [Polaris CLI](/docs/cli).

## Previews

| Command | What it does |
| --- | --- |
| `polaris port announce <port>` | Opens the web server on that port as a [Preview](/docs/previews) and prints its link. Add `--label "<name>"` to show a name instead of the port. |
| `polaris port announce <port> -- <command>` | Also keeps the server running: Polaris starts the command, starts it again if it fails, and starts it again each time the Machine wakes. After five failures within five minutes it stops trying; fix the command, run `polaris port retire <port>`, then announce it again. |
| `polaris port check <port> [path]` | Checks that the Preview loads end to end and shows the kept-running server's state. |
| `polaris port logs <port>` | Shows the end of a kept-running server's output. |
| `polaris port url <port>` | Prints the Preview link without opening it, so you can configure your app before it starts. |
| `polaris port retire <port>` | Closes the Preview and stops its kept-running server. |
| `polaris services list` | Lists the dev servers in the repository's [`.agents/services.yaml`](/docs/previews#start-the-apps-your-repository-lists), with their ports and commands. Names the first problem in a file Polaris can't read. |
| `polaris services start <name>` | Starts one of those servers and keeps it running, as `polaris port announce <port> --label "<label>" -- <command>` would from its folder, and prints its link. |

- The server must listen on `0.0.0.0`. Make it fail rather than move to another
  port if the port is taken, for example with Vite's `--strictPort`.
- A kept-running server gets the Project's
  [variables](/docs/environment-variables), `PORT` set to its port and
  `POLARIS_PREVIEW_ORIGIN` set to its Preview link. Use the link where your app
  writes its own address, such as an OAuth redirect.
- To change a kept-running server's command, retire the port first.

## Share images and videos

```bash
polaris share <path>
```

Polaris checks the file, then prints a public `link`, `markdown` to paste into
a pull request or issue, and when the file `expires`. If it says the file is
still processing, run `polaris share --status <file id> <attempt id>` with the
IDs it printed. Shared files also appear in the Session's
[Artifacts](/docs/artifacts) tab.

| Type | Formats | Largest |
| --- | --- | --- |
| Images | PNG, JPEG, GIF, WebP | 10 MB |
| Videos | MP4, WebM | 100 MB and 10 minutes |

Anyone with the link can open the file without signing in, so don't share
anything that shows secrets or private data. Organization admins turn sharing
on or off and choose how long files are kept (**Keep files for**, 1 year by
default) in **Settings → General → Shared files**. Turning sharing off stops
existing links from working.

Common messages:

- `this file type isn't supported`: share a PNG, JPEG, GIF, WebP, MP4 or WebM.
- `this video format isn't supported`: export it again as MP4 (H.264) or WebM
  (VP9).
- `sharing is turned off for this organization`: ask an organization admin to
  turn it on.
- `this organization reached today's 1 GB sharing limit`: try again after
  midnight UTC, or share a smaller file.

## Environment variables

| Command | What it does |
| --- | --- |
| `polaris env list` | Lists the names of the Project's variables and whether each is Config or a Secret, without values. |
| `polaris env run -- <command>` | Runs a command with the Project's current variables. |

The agent's own shell and Terminal shells don't get the Project's
[variables](/docs/environment-variables) on their own. Start a command with
`polaris env run --` to give it them:

```bash
polaris env run -- pnpm test:integration
```

Each command gets the current values when it starts; a command that is already
running keeps the values it started with. A key
[kept off the Machine](/docs/keep-secrets-off-the-machine) arrives as a placeholder,
and Polaris adds the real key when the request leaves the Machine.

## Workspace setup

| Command | What it does |
| --- | --- |
| `polaris setup status` | Shows each setup script's state, with a failure's reason and exit code, and whether the first Turn waited for setup. |
| `polaris setup log` | Shows the output of the Project setup script and `.agents/setup`. |
| `polaris setup log resume` | Shows the output of the latest `.agents/resume` run. |
| `polaris setup log --tail 50` | Shows only the last 50 lines. Works with `resume` too. |

`polaris setup status` prints one line per script:

```text
Project setup script: absent
.agents/setup: succeeded
.agents/resume: succeeded
setup mode: the first Turn does not wait for setup
```

Logs hide known secret values and keep the first 5 MB of output. Each resume
run replaces the previous resume log.

### When scripts run

| Script | When it runs |
| --- | --- |
| Project setup script | First, on a new Machine. It's saved in Project settings, not in the repository. |
| `.agents/setup` | Once, on a new Machine, after the Project setup script. |
| `.agents/resume` | After setup on a new Machine, whether setup succeeded or failed, and first on every wake. Messages don't wait for it unless **Start Turns during resume** is off; then a message waits for it for at most 10 seconds. |

Polaris never runs setup again on the same Machine, not even after a failure.
Scripts have no time limit, but Polaris keeps the Machine awake for setup for
only 60 minutes, so keep setup well under an hour.

### Writing setup scripts

- **Start with a shebang**, such as `#!/usr/bin/env bash`, followed by
  `set -euo pipefail`.
- **Make each script safe to run twice.** Check what is already installed or
  running before you change it, and test it twice from the repository root.
- **Expect a short `PATH`.** Scripts run from the repository root as your
  Machine user, with no terminal and no input. `PATH` is
  `/usr/local/polaris/bin:/usr/local/bin:/usr/bin:/bin`, so call a tool
  installed elsewhere, such as under `~/.local/bin`, by its full path.
- **`sudo` works on most Machines.** Use it to install system packages. On
  some Machines `sudo` asks for a password, so a script that needs it fails
  there; keep that setup in user space, or start a new Session to get a new
  Machine.
- **Commit the executable bit.** A script without it fails with
  **Not executable**.

  ```bash
  chmod +x .agents/setup .agents/resume
  git add .agents/setup .agents/resume
  ```

- **Keep secrets out of setup.** The Project setup script and `.agents/setup`
  get no Project variables. Sign in to registries and start tunnels in
  `.agents/resume`, which gets them. If the variables can't be loaded, resume
  doesn't run and shows **Project variables unavailable**.
- **Install in setup, start services in resume.** Processes that setup leaves
  running stop when it ends. Processes that resume starts keep running, so
  send their output to their own log file, for example
  `nohup node worker.js > "$HOME/worker.log" 2>&1 &`. Don't install
  dependencies in resume.
- **Run a script by hand only when it isn't running.** Check with
  `polaris setup status` first. To run resume with the Project's variables,
  run `polaris env run -- .agents/resume` from the repository root.

## Keep a command running

The agent ends its Turn while waiting for work outside the Machine, such as a
review or deployment. Where available, [check-ins](/docs/check-ins) let it return
later. Keep-awake holds are for commands running on the Machine.

The Machine stays awake while the agent works or waits on a command. A command
left running in the background with nobody waiting for it doesn't keep the
Machine awake, so it pauses when the Machine sleeps. To keep the Machine awake
until a long job finishes:

```bash
polaris keep-awake run -- pnpm test:e2e
polaris keep-awake run --for 2h -- python train.py
```

| Command | What it does |
| --- | --- |
| `polaris keep-awake run -- <command>` | Keeps the Machine awake until the command ends. Polaris confirms the hold before the command starts. |
| `polaris keep-awake run --for <time> -- <command>` | Keeps it awake for at most that long, such as `20m`, `2h` or `1d`. When the time runs out, the command keeps running but no longer holds the Machine awake. |
| `polaris keep-awake list` | Lists the holds on this Machine, with when each ends. A hold someone started from a [Preview](/docs/previews#keep-it-running) isn't listed, and the agent can't end it. |
| `polaris keep-awake release <id>` | Ends one hold. It doesn't stop its command. |

Keep the job in the foreground inside `keep-awake run`: when it exits, the hold
ends. If the hold isn't confirmed, the command doesn't start.

## Cloud access

These commands use the services an admin connected in
[Cloud access](/docs/cloud-access). No long-lived cloud key is stored on the
Machine.

### Check Tailscale

| Command | What it does |
| --- | --- |
| `polaris tailnet status` | Shows this Machine's tailnet name, tags, MagicDNS suffix, and the online or offline state of peers visible to it. It reads status without changing the connection. |

If the Machine hasn't joined or the command can't read status, see
[Tailscale setup and troubleshooting](/docs/cloud-access#use-tailscale).

### Use an AWS profile

Each AWS role assigned to the Project is an AWS profile on its Machines, such
as `polaris-logs-reader`. Choose it with `--profile` or `AWS_PROFILE`.
Polaris profiles set no region, so pass `--region` or set `AWS_REGION`:

```bash
aws sts get-caller-identity --profile polaris-logs-reader --region eu-west-1
AWS_PROFILE=polaris-logs-reader AWS_REGION=eu-west-1 aws sts get-caller-identity
```

- Credentials renew on their own; there is nothing to sign in to or rotate.
- Polaris rewrites the part of `~/.aws/config` between
  `# BEGIN POLARIS MANAGED CLOUD ACCESS PROFILES` and
  `# END POLARIS MANAGED CLOUD ACCESS PROFILES`. Keep your own profiles outside
  it. If you define a profile with the same name, yours wins.
- The profiles get credentials from `polaris aws credentials`. You don't run it
  yourself.

### Request access to a role that needs approval

```bash
polaris access request polaris-admin --for 30m --reason "Delete the old staging stack" --wait
```

- `--for` takes a whole number followed by `s`, `m` or `h`, up to `24h`.
  A request above the role's **Longest approval** asks for that limit and tells
  you the duration it requested.
- On current CLIs, `--wait` returns within about 90 seconds. Older CLIs can wait
  longer. Approval exits with code 0 and prints the
  approval end time, duration, approver and profile to use. Denial, cancellation, invalidation or expiry exits nonzero
  with its reason. Without `--wait`, the command returns as soon as it sends
  the request. If a Machine's `polaris` rejects `--wait`, run the command
  without it. If a request is already waiting for the same role, the command
  tells you when it was sent; its duration and reason stay unchanged.
- Requesting an active role asks for an extension. Approval starts a fresh
  window from the approval time; it does not add time to the old window.
- At the wait limit, the command prints the exact command to check it:
  `polaris access status <grantId>`. Status prints whether that request is
  pending, approved (with its end time) or ended. Reaching the wait limit
  leaves the request open until the printed approval deadline.
- Use the profile name shown in the Machine's Cloud access notes. Those notes
  include the role's limit. When access ends, the next AWS call prints a new
  request command using that profile and its current limit.
- Ask from a Session: the request appears in its transcript, where approvers
  decide. A request from a shell Polaris can't match to a Session appears in no
  transcript.

### Get an identity token

```bash
polaris id-token --audience https://vault.example.com
```

The command prints only the token, for a service connected as **Other
service**. A token is valid for 5 minutes, so get a new one
each time instead of saving it. An audience isn't a secret: every Machine in
your organization can get a token for every connected service, so have the
service also check the Project or Machine in the token.

Tokens are signed with RS256. Services find the keys at
`https://plrs.sh/oidc/org/<org id>/.well-known/openid-configuration`. Each
token has these claims:

| Claim | Value |
| --- | --- |
| `iss` | `https://plrs.sh/oidc/org/<org id>` |
| `sub` | `org:<org id>:project:<project id>:workspace:<Machine id>` |
| `aud` | The service's audience, exactly as connected |
| `iat`, `exp` | Issue and expiry times, 5 minutes apart |
| `jti` | A unique token ID |
| `org_id`, `project_id`, `workspace_id` | The organization, Project and Machine. `org`, `project` and `workspace` repeat them. |

To find the IDs, look at addresses in Polaris. A Session's address has the form
`https://plrs.sh/workspaces/<Machine id>/sessions/…?orgId=<org id>`. The address
of a Project's page in **Settings → Projects** contains its ID, which starts
with `project_`. A Machine keeps its ID when it sleeps and wakes.

### What an AWS role trusts

A role that Polaris sets up trusts only tokens from your organization's issuer,
`https://plrs.sh/oidc/org/<org id>`, with the audience `sts.amazonaws.com` and
the subject for that one role:

| Mode | Subject the role trusts |
| --- | --- |
| Always available | `org:<org id>:access:<access profile id>:mode:always:project:*` |
| Needs approval | `org:<org id>:access:<access profile id>:mode:approval` |

Polaris checks the role's Projects, and any approval, each time it issues a
token. Tokens from `polaris id-token` can't assume these roles. In CloudTrail,
each session's source identity, `m-` followed by 32 characters, identifies the
Machine.

## Report a problem

```bash
polaris feedback draft < report.json
```

Drafts a report about Polaris from a JSON file. The draft waits in the Session
until you review and send it; see [Report a problem](/docs/feedback). Run
`polaris feedback --help` for the fields.

## CI notifications

| Command | What it does |
| --- | --- |
| `polaris notify ci --pr <number>` | Gets the CI result for the pull request's current commit back in this Session. The agent runs it after it pushes, then ends its Turn. The result arrives when the checks finish, or after an hour; see [Wait for CI](/docs/pull-requests#wait-for-ci). |

Common messages:

- `CI notification armed for <commit>; deadline <time>. End your Turn to wait.`:
  Polaris sends the result for that commit when the checks finish.
- `CI result already sent for <commit>. No new notification armed.`: Polaris
  already sent the result for that commit. Push a new commit to get a new one.
- `CI notification not armed (not_linked); reconnect GitHub in Polaris.`: the
  Session owner's GitHub account isn't connected. They
  [connect GitHub](/docs/github#connect-github), then the agent runs the command
  again.
- `CI notification not armed (reauthorization_required); reconnect GitHub in Polaris.`:
  the Session owner's GitHub sign-in expired. They select **Reconnect GitHub**;
  see [Troubleshooting](/docs/troubleshooting#github-access-needs-attention).
- `CI notification not armed (...); try again shortly.` or
  `CI notification was not confirmed; retry.`: Polaris couldn't arm it right
  now. The agent runs the command again.

## Session

| Command | What it does |
| --- | --- |
| `polaris session rename "<title>"` | Renames the Session. |
| `polaris session url` | Prints a link to the Session, for example for a pull request description. |
| `polaris session snooze --for 2h` | Moves the Session to **Snoozed** for a number of minutes, hours or days, such as `30m`, `2h` or `1d`. |
| `polaris session snooze --until <date and time>` | Snoozes it until a date and time with its time zone offset, such as `2026-11-02T09:00+01:00`. |
| `polaris session unsnooze` | Brings a snoozed Session back. |

The agent snoozes a Session only when you ask it to.

## Desktop

| Command | What it does |
| --- | --- |
| `polaris desktop start` | Starts the Machine's [Desktop](/docs/browser-and-desktop) if it isn't running and waits until it is ready. |
| `polaris desktop status` | Says whether the Desktop is ready. |

## Docker

Docker isn't preinstalled. When you ask the agent to add it to
[Workspace setup](/docs/workspace-setup#add-docker), it uses this tested script, which keeps Docker's images, containers and
volumes on the same disk as your code. It then starts Docker and your
containers again each time the Machine wakes. Docker needs root on the Machine;
where a Machine doesn't allow it, the agent tells you.

```bash
#!/usr/bin/env bash
set -euo pipefail

mountpoint -q /opt/polaris/workload

sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
printf '%s\n' 'deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable' |
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo install -d /etc/docker /etc/containerd /opt/polaris/workload/docker /opt/polaris/workload/containerd
if ! sudo test -e /etc/containerd/config.toml; then
  printf '%s\n' 'version = 2' 'root = "/opt/polaris/workload/containerd"' 'disabled_plugins = ["io.containerd.grpc.v1.cri"]' |
    sudo tee /etc/containerd/config.toml > /dev/null
fi
for service in containerd docker; do
  sudo install -d "/etc/systemd/system/$service.service.d"
  printf '%s\n' '[Unit]' 'ConditionPathIsMountPoint=/opt/polaris/workload' |
    sudo tee "/etc/systemd/system/$service.service.d/project-disk.conf" > /dev/null
done
sudo systemctl daemon-reload
if ! sudo test -e /etc/docker/daemon.json; then
  printf '%s\n' '{"data-root":"/opt/polaris/workload/docker"}' |
    sudo tee /etc/docker/daemon.json > /dev/null
fi
sudo apt-get update
sudo apt-get install -y --no-upgrade -o Dpkg::Options::=--force-confold \
  docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now docker
```

## Preinstalled tools

Every Machine runs Debian 12 with these tools installed, so you and the agent
use the same versions. A Machine keeps the versions it started with.

| Tool | Version | What you use it for |
| --- | --- | --- |
| Node.js and npm | 24 | Running and installing JavaScript and TypeScript projects. |
| pnpm | 11.1 | Installing and running pnpm projects. |
| Yarn | 4.18 | Installing and running Yarn projects. |
| Bun | 1.3 | Running and installing Bun projects. |
| Python | 3.11 | Running Python projects and scripts. |
| uv | 0.12 | Fast Python environments and dependency installs. |
| pip | 23.0 | Installing Python packages. |
| Git | 2.39 | Branches, commits, history, and diffs on your checkout. |
| AWS CLI (`aws`) | 2.31 | Uses the roles connected through [Cloud access](/docs/cloud-access). |
| GitHub CLI (`gh`) | 2.101 | Pull requests, issues, checks, and releases from the Machine. |
| ripgrep (`rg`) | 15.1 | Searching a large checkout quickly. |
| fd | 8.6 | Finding files by name. |
| jq | 1.6 | Reading and reshaping JSON output. |
| tmux | 3.3 | Keeping long-running commands in a session. |
| Compilers and build tools | GCC 12, make 4.3 | Building projects with native code. |
| curl | 7.88 | Fetching URLs and calling APIs from the Machine. |
| OpenSSH client | 9.2 | Connecting to hosts your project needs. |
| zip, unzip, zstd, less, file | — | Everyday archive and file handling. |
| Build headers and libraries | — | Building packages that link against PostgreSQL, MySQL, SQLite, OpenSSL, XML, YAML, or ImageMagick. |
| Mercurial and Subversion | 6.3, 1.14 | Working with repositories that do not use Git. |

Machines also have Chromium, which the agent uses for the
[Browser](/docs/browser-and-desktop). `git` and `gh` act as you on GitHub; see
[GitHub](/docs/github). Docker isn't preinstalled; see
[Docker](#docker). For anything else, ask the agent
to install it, and to add it to your setup if every new Machine should have it.
