Reference: The CLI, the device CLI, permissions, webhooks, limits and troubleshooting
# Suta CLI
> Every command in the Suta CLI, with examples.
The `suta` command runs on your computer. Install it with `curl -fsSL https://suta.dev/install.sh | bash` and choose **Suta CLI**. Run `suta help ` for examples of any command. For the `suta` command on a device, see [On-device commands](/reference/device-cli/). `` is always a device name or ID. If two devices share a name, use the ID; Suta never guesses. ## Global options These work with every command. | Option | Description | | ------------------ | ---------------------------------------------------------- | | `--workspace NAME` | Use this workspace for one command. Also `SUTA_WORKSPACE`. | | `--json` | Print one JSON result. Never prompts or opens a browser. | | `--no-input` | Never prompt or open a browser. Also `SUTA_NO_INPUT=true`. |
```sh
suta device list --workspace Lab
suta device list --json
SUTA_NO_INPUT=true suta device inspect kitchen-pi
```
## Sign in and workspaces `suta auth login` opens your browser to sign in. The session lasts 30 days and is saved in `~/.config/suta/session.json`, readable only by you. | Command | Description | | --------------------------- | --------------------------------------------- | | `suta auth login` | Sign in through your browser. | | `suta auth status` | Show your account, workspace and permissions. | | `suta auth logout` | Sign out and close your remote sessions. | | `suta workspace list` | List your workspaces. | | `suta workspace use ` | Set your default workspace. | The CLI acts as you, with your role’s permissions. Anything that runs `suta` on your computer, including an AI agent, has the same access.
```sh
suta auth login
suta auth status
suta workspace list
suta workspace use Workshop
```
## Projects | Command | Description | | ------------------------------------------ | ----------------------------------------------------------------- | | `suta project list` | List projects with their devices, open alerts and latest rollout. | | `suta project create [--slug SLUG]` | Create a project. Admins only. | A project’s slug is the name `suta.yaml` uses for it, in `project:`. See [Projects](/get-started/projects/).
```sh
suta project list
suta project create "Warehouse robots"
```
## Set up devices | Command | Description | | ---------------------------------- | ---------------------------------------------------------------------------------------------------- | | `suta flash [disk] [--image FILE]` | Flash Suta OS to an SD card. | | `suta device add [--project SLUG]` | Find a new device nearby and add it to a project. Asks which project when the workspace has several. | | `suta device network ` | Repair a device’s network from nearby. | `suta flash` accepts `.img` and `.img.xz` files. Downloaded images are cached in `~/.suta/cache/images/`; delete that folder to free space. These commands are interactive and need a terminal. See [Set up a new Pi](/add-devices/new-pi/) and [Fix a device’s network](/add-devices/repair-network/).
```sh
suta flash
suta flash /dev/disk4 --image suta-os-2026.09.5-pi.img.xz
suta device add --project delivery-drones
suta device network kitchen-pi
```
## Inspect devices | Command | Description | | -------------------------------------------- | ----------------------------------------------------------------- | | `suta device list` | List devices, their generation, apps and when each was last seen. | | `suta device inspect ` | Show identity, network and last reported measurements. | | `suta device metrics [1h\|6h\|24h]` | Show collected metrics. | | `suta device service list ` | List the services the device last reported. | These read what the device last reported. They don’t contact the device.
```sh
suta device list
suta device inspect kitchen-pi
suta device metrics kitchen-pi 6h
suta device service list kitchen-pi
```
## Read logs
```text
suta device logs [service] [options]
```
| Option | Default | Description | | ---------- | ------- | ---------------------------------------------- | | `--since` | `1h` | A duration such as `15m`, or an RFC 3339 time. | | `--until` | now | An RFC 3339 time (exclusive). | | `--search` | | Only messages that contain this text. | | `--limit` | `100` | 1 to 200 entries. | | `--before` | | The cursor from the previous page. | Logs are kept for 24 hours. [Monitoring](/operate/monitoring/) must be on and collecting the service.
```sh
suta device logs kitchen-pi
suta device logs kitchen-pi camera.service --since 15m
suta device logs kitchen-pi --search timeout --limit 200
```
## Remote access | Command | Description | | -------------------------------------------------- | ------------------------------------------- | | `suta device access status ` | Show whether access is open and until when. | | `suta device access enable [15m\|1h\|4h]` | Open access. Default `1h`. | | `suta device access disable ` | Close access and end sessions. | See [Remote access](/operate/remote-access/).
```sh
suta device access enable kitchen-pi
suta device access status kitchen-pi
suta device access disable kitchen-pi
```
## Terminals and commands | Command | Description | | -------------------------------------------------------- | ---------------------------------------------------------------------- | | `suta device ssh [--sudo] [--for 1h]` | Open a recorded terminal, 1 minute to 2 hours. | | `suta device exec [--sudo] -- [args]` | Run one program and print its output. | | `suta device forward <[local:]port> [--for 1h]` | Forward a local port to a port on the device’s loopback, until Ctrl-C. | | `suta device service restart ` | Restart a service without sudo. | `exec` options: | Option | Default | Description | | -------------- | -------- | --------------------------------------------------- | | `--timeout` | `1m` | Stop waiting after up to `5m`. | | `--request-id` | new UUID | Reuse after a lost response to avoid running twice. | Output is limited to 1 MiB. The remote exit code (0–255) becomes `suta`’s exit code. `suta` exits with `1` if the result is unknown, `2` for invalid arguments and `130` if you cancel.
```sh
suta device ssh kitchen-pi
suta device ssh kitchen-pi --sudo --for 15m
suta device exec kitchen-pi -- uname -a
suta device exec kitchen-pi -- sh -c 'uptime; df -h /'
suta device exec kitchen-pi --sudo --timeout 5m -- apt-get update
suta device forward kitchen-pi 8080
suta device forward kitchen-pi 9000:80 --for 15m
suta device service restart kitchen-pi camera.service
```
## Monitoring | Command | Description | | ---------------------------------------------------- | ------------------------------------------ | | `suta device monitoring status ` | Show settings and what the device reports. | | `suta device monitoring enable [services…]` | Turn on metrics, plus logs for services. | | `suta device monitoring disable ` | Turn off collection, keeping your choices. | | `suta device monitoring apply ` | Apply a complete settings file. |
```sh
suta device monitoring enable kitchen-pi camera.service
suta device monitoring apply kitchen-pi settings.json
```
settings.json
```json
{
"enabled": true,
"services": ["camera.service"],
"process_metrics": false,
"sample_interval_seconds": 30
}
```
## Activity
```text
suta workspace activity [device] [options]
```
| Option | Default | Description | | ----------- | ------- | --------------------------------------------- | | `--since` | | A duration such as `1d`, or an RFC 3339 time. | | `--limit` | `20` | 1 to 200 entries. | | `--before` | | The cursor from the previous page. | | `--request` | | Find one operation by its request ID. |
```sh
suta workspace activity
suta workspace activity kitchen-pi --since 1d
suta workspace activity --request 3f0c…
```
## Plan and deploy You describe what devices run in `suta.yaml`. `suta plan` shows what would change on each device; `suta deploy` applies a plan in stages. | Command | Description | | --------------------------------- | ---------------------------------------------------------- | | `suta plan [options]` | Preview the change for each device and update `suta.lock`. | | `suta deploy [plan] [options]` | Deploy a plan: a canary first, then in batches. | | `suta rollback [--to N]` | Return a device to an earlier accepted generation. | | `suta migrate [--yes]` | Move a device from Raspberry Pi OS to Suta OS. | `plan` and `deploy` read `./suta.yaml`, or the file you pass with `-f`. Files named with `source:` are read relative to `suta.yaml`; each must stay inside its directory, be UTF-8 text and be 64 KiB or smaller. Planning pins image digests, the OS release and secret versions in `suta.lock`, next to `suta.yaml`. Commit both files. Later plans reuse the pins until you pass `--update`. Nothing changes on devices until you deploy. In the plan, `+` marks a device that updates, `=` one that already runs the plan and `!` a pinned device, which keeps its generation. Options for choosing devices, for both commands: | Option | Description | | ----------------- | ------------------------------------------------------------------------------------------------ | | `-f`, `--file` | The spec to read. Default `suta.yaml`. | | `--device` | Only this device. Repeat for several. | | `--tag` | Only devices with this tag. Repeat to require several. | | `--site` | Only devices in this site. Repeat for several. | | `--update` | Pin the newest images, OS release and secret versions. | | `-m`, `--message` | One line saying what the deploy is for, up to 200 characters. It names the plan and its rollout. | With a plan ID, `deploy` starts exactly that plan. Without one, it plans first, shows the plan and asks you to confirm. With `--json` or `--no-input`, pass `--yes` to confirm in advance. `deploy` options: | Option | Default | Description | | ---------------- | ----------------------------------------------------------------------- | ------------------------------------------------------- | | `--canary` | Sized to the plan; see [Choose stages](/deploy/rollouts/#choose-stages) | Devices to update first, 0 to 20. | | `--batch` | Sized to the plan | Devices to update at a time after the canary, 1 to 500. | | `--max-failures` | `0` | Failures to allow before pausing, 0 to 100. | | `--wait` | | Wait until the rollout finishes or pauses. | | `--yes`, `-y` | | Deploy without asking for confirmation. | `deploy --wait` exits with `1` if the rollout pauses, is cancelled or any device fails. `migrate` asks you to confirm. Pass `--yes` in scripts. See [Move a device to Suta OS](/deploy/migrate/).
```sh
suta plan
suta plan -m "Fix sensor calibration"
suta plan --site london --update
suta deploy 3f2a9c1e-…
suta deploy --tag kiosk --wait
suta deploy --yes --canary 2 --batch 20 --json
suta rollback kitchen-pi
suta rollback kitchen-pi --to 5
suta migrate kitchen-pi
```
suta.yaml
```yaml
os: latest
apps:
web:
image: ghcr.io/acme/web:1.4
env:
LOG_LEVEL: info
secrets: [db-password]
files:
/etc/web/app.conf:
source: config/app.conf
ports: ["80:8080"]
sites:
london:
apps:
web:
env: { REGION: eu }
```
## Device generations | Command | Description | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `suta device generations ` | Show the device’s generations, apps and OS. | | `suta device diff [from] [to]` | Compare the generations of two deploys, by deploy number. | | `suta device config [number] [--lock]` | Print the `suta.yaml` (or with `--lock`, `suta.lock`) behind the running generation or deploy `number`. | | `suta device explain [key] [-f FILE]` | Show each value `suta.yaml` gives the device and where it comes from. | | `suta device pin [--reason TEXT]` | Keep the device on its current generation. | | `suta device unpin ` | Let plans and rollouts update the device again. | | `suta device retry ` | Run a failed or rolled-back generation again. | | `suta device site \|--clear` | Set or clear the device’s site. | By default, `diff` compares the previously accepted generation with the current one, or with the one being deployed. It shows environment variables and secret names and versions, never secret values. `explain` sources are `spec`, `site `, `device `, `lock` and `default`. Pass a key prefix such as `apps.web.env` to show fewer values. `explain` doesn’t change `suta.lock`. `retry` runs a generation the device refused after it failed there. Fix the cause first.
```sh
suta device generations kitchen-pi
suta device diff kitchen-pi
suta device diff kitchen-pi 5 8
suta device config kitchen-pi > suta.yaml
suta device explain kitchen-pi apps.web.env
suta device pin kitchen-pi --reason "Trade show demo"
suta device site kitchen-pi london
```
## Rollouts `suta deploy` starts a rollout. Use these commands to follow and control it. | Command | Description | | ----------------------------------------------------- | ------------------------------------------ | | `suta rollout list` | List recent rollouts. | | `suta rollout show [rollout]` | Show progress, stages and every device. | | `suta rollout wait [rollout]` | Wait until the rollout finishes or pauses. | | `suta rollout pause\|resume\|retry\|cancel [rollout]` | Control a rollout. | Without an ID, these commands use the current rollout, or the most recent one. `wait` exits with `1` if the rollout pauses, is cancelled or any device fails. See [Rollouts](/deploy/rollouts/).
```sh
suta rollout show
suta rollout wait
suta rollout retry
```
## Secrets | Command | Description | | -------------------------------------------- | -------------------------------------------------------------- | | `suta secret list` | List secrets, their latest version and whether they’re in use. | | `suta secret set [--value-file FILE]` | Store a new version of a secret. | | `suta secret remove ` | Delete a secret and every version. | `set` reads the value from standard input or from `--value-file`, exactly as stored. In a terminal, it prompts without showing what you type. Values are never accepted as arguments, so they don’t appear in your shell history. Each `set` creates a new version. Run `suta plan --update` to move existing pins to it. You can’t remove a secret while a device’s generation uses it. Managing secrets needs a workspace admin.
```sh
suta secret set db-password
suta secret set db-password < password.txt
suta secret set tls-key --value-file key.pem
suta secret list
```
## Alerts | Command | Description | | ------------------------------------------------------------------ | ---------------------------------------------- | | `suta alert list [--all] [--kind K] [--device D]` | List open alerts, or every alert with `--all`. | | `suta alert conditions [options]` | Show alert conditions, or change them. | | `suta alert webhook list` | List webhooks and their recent deliveries. | | `suta alert webhook add ` | Add a webhook and print its signing secret. | | `suta alert webhook test\|pause\|resume\|rotate\|remove ` | Manage a webhook. | `conditions` takes `--offline MINUTES`, `--memory PERCENT`, `--storage PERCENT` and `--temperature CELSIUS`, each also accepting `off`, and `--deploy-failed on|off`. `webhook add` takes `--on opened,resolved` and `--kind` to limit which alerts it receives. Changing conditions and webhooks needs a workspace admin. See [Alerts](/team/alerts/).
```sh
suta alert list
suta alert list --all --kind storage
suta alert conditions --storage 85 --temperature off
suta alert webhook add Ops https://hooks.slack.com/services/…
suta alert webhook test Ops
```
## API tokens | Command | Description | | -------------------------------------------------- | --------------------------------- | | `suta workspace token list` | List API tokens. | | `suta workspace token create --role ` | Create a token and print it once. | | `suta workspace token revoke ` | Revoke a token immediately. | `--expires` is `30d`, `90d` (default), `365d` or `never`. Managing tokens needs a workspace admin. See [API tokens](/ai-agents/api-tokens/).
```sh
suta workspace token create CI --role Operator --expires 30d
suta workspace token revoke CI
```
## Maintenance | Command | Description | | ------------------------------------ | ------------------------------------ | | `suta update` | Update the CLI. Keeps you signed in. | | `suta completions ` | Print a shell completion script. | `suta update` updates the CLI on your computer, not the software on devices.
```sh
suta update
suta completions zsh > "${fpath[1]}/_suta"
```
## JSON output With `--json`, `suta` prints exactly one JSON object to standard output. Progress and messages go to standard error. A failed remote command also includes `data`, with the state, exit code and any output. Human-readable output escapes control characters from devices. JSON output keeps the original text. Success
```json
{ "ok": true, "data": { "…": "…" } }
```
Failure
```json
{
"ok": false,
"error": { "code": "permission_denied", "message": "…" }
}
```
# On-device commands
> The suta command on the device itself.
Every device with Suta has its own `suta` command. When you open a terminal on the device, through [remote access](/operate/remote-access/) or SSH, you see a short summary of the device. Run `suta` any time to see it again. | Command | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------ | | `suta` | Show a short summary of the device. | | `suta status` | Show identity, hostname, interfaces, storage, the running generation and the last failed deploy. | | `suta apps` | Show each app on the device: whether it’s running, its health check, restarts and why it last failed. | | `sudo suta logs` | Show recent logs from the Suta agent. | | `sudo suta logs APP` | Show recent logs from one app. | | `sudo suta logs --all` | Show recent logs from the agent, every app and Suta’s other services, interleaved. | | `suta name` | Print the device’s name in your workspace. | | `suta id` | Print the device ID, or its public identity before it’s registered. | | `suta version` | Print the agent version. | | `sudo suta register` | Finish registering the device in your browser. | | `sudo suta setup` | Open a 15-minute window to [repair the network](/add-devices/repair-network/). Devices running Suta OS only. | | `sudo suta uninstall` | Remove Suta, keeping the device’s identity and settings. | `suta logs` shows the last 100 lines. Add `--lines N` (`-n N`) for more or fewer, and `--follow` (`-f`) to keep printing new lines until you press Ctrl-C. For example, to watch an app named `camera` restart:
```sh
sudo suta logs camera --follow
```
Logs come from the device’s system journal, so they need `sudo`. `suta apps` reads the same snapshot as `suta status` and doesn’t. Output is plain when piped or when `NO_COLOR=1` is set. The device’s name in Suta appears in the Suta prompt and summary. It doesn’t change the Pi’s hostname or other accounts.
# Limits and timings
> Durations, sizes and retention periods in Suta.
## Devices | | Limit | | ----------------------------------------- | ------------------------------ | | Heartbeat interval | About every 5 seconds | | Shown as offline after | 30 seconds without a heartbeat | | Tags per device | 10 | | Network repair window (`sudo suta setup`) | 15 minutes | ## Remote access | | Limit | | -------------------- | ------------------------------------------------------------------- | | Remote access window | 15 minutes, 1 hour or 4 hours, or until turned off (dashboard only) | | Terminal session | 1 minute to 2 hours | | Command timeout | 1 second to 5 minutes, default 1 minute | | Command output | 1 MiB, standard output and error combined | | Terminal recording | 25 MiB per session; the session ends if it’s reached | ## Monitoring | | Limit | | --------------------------- | --------------------- | | Sample interval | 15, 30 or 60 seconds | | History | 24 hours | | Log entries per CLI request | 1 to 200, default 100 | ## Deploys | | Limit | | ----------------------------------------- | ------------------------------------------------------ | | Probation | 10 seconds to 1 hour, default 60 seconds | | Rollouts | One running or paused rollout per project | | Canary | 0 to 20 devices | | Batch | 1 to 500 devices | | Time for a device to finish | 60 minutes; after that, the rollout pauses | | Offline devices | Skipped if silent for 2 minutes when their wave starts | | Configuration file | 64 KiB each | | Names of apps, sites, secrets and volumes | Up to 40 characters | ## Workspace | | Limit | | ----------------------- | -------------------------------------- | | Invitation expiry | 7 days | | Webhooks | 10 per workspace | | Activity and recordings | Kept for 30 days | | CLI sign-in | 30 days | | AI agent approval | Until revoked, or 15 minutes to 7 days | | API token expiry | 30 days, 90 days, 1 year or never |
# Permissions
> Every permission in Suta, what it allows and which roles include it.
Permissions control what people, AI agents and API tokens can do. People and [API tokens](/ai-agents/api-tokens/) get permissions from a [role](/team/people-and-roles/). AI agents get the permissions you approve, limited to what the approving person has. ## All permissions | Permission | ID | Allows | | ---------------------------- | ------------------ | ---------------------------------------------------------------------------------------- | | View device details | `device.read` | Identity, version, network and connection details. Every other permission includes this. | | View services | `services.read` | The services a device last reported. | | Read metrics | `metrics.read` | System and process measurements. | | Read logs | `logs.read` | Collected service logs. | | Restart services | `services.restart` | Restarting services, without general sudo. | | Change monitoring settings | `monitoring.write` | Turning collection on or off and choosing services. | | Manage remote access | `access.write` | Opening, changing and closing remote access. | | Run commands | `commands.exec` | Commands, terminals and port forwards as an unprivileged user. | | Run commands with sudo | `commands.sudo` | Commands and terminals with full `sudo`. | | Read activity and recordings | `audit.read` | Operation history and terminal recordings. | | Edit device details | `device.write` | Renaming devices and changing tags. | | Remove devices | `device.remove` | Removing devices and revoking their access. | | Enroll devices | `device.enroll` | Adding devices to the workspace. | | Configure networking | `network.write` | Approving local network repair and saving a project’s Wi-Fi networks. | | Deploy software | `deploy.apply` | Deploying generations, rolling back and pinning devices. | ## Scope Each permission applies to all devices in the workspace, including future ones, or to devices you choose. Three permissions can also be limited to named services: **View services**, **Read logs** and **Restart services**. **Read logs** can also be limited to a fixed time range. Caution Shell access can read anything on the device, beyond any log or service limits. Give **Run commands** and **Run commands with sudo** only to people and agents who need them.
# Webhooks
> The alert webhook payload, headers and how to verify signatures.
Suta sends a JSON `POST` to each webhook when an alert opens or resolves, and when you select **Send test**. To add a webhook, see [Alerts and webhooks](/team/alerts/). ## Payload | Field | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------------- | | `type` | `alert.opened`, `alert.resolved` or `test`. | | `text` | A one-line summary. Slack displays it as the message. For a failed deploy, it includes the reason the device reported. | | `alert.kind` | `offline`, `memory`, `storage`, `temperature` or `deploy_failed`. | | `alert.status` | `open` or `resolved`. | | `alert.details` | The reading and the limit that triggered the alert. | | `device` | The device’s ID, name and project (`id`, `slug` and `name`). | | `workspace` | The workspace’s ID and name. | | `url` | A link to the alert in Suta, where your team can acknowledge, snooze or assign it. | alert.opened
```json
{
"type": "alert.opened",
"text": "[Suta] Alert: Lobby camera storage is 96% full (limit 90%)",
"alert": {
"id": "8c1f…",
"kind": "storage",
"status": "open",
"summary": "Lobby camera storage is 96% full (limit 90%)",
"details": { "percent": 96.0, "threshold": 90.0 },
"opened_at": "2026-09-23T10:00:00Z",
"resolved_at": null
},
"device": {
"id": "…",
"name": "Lobby camera",
"project": { "id": "…", "slug": "lobby-kiosks", "name": "Lobby kiosks" }
},
"workspace": { "id": "…", "name": "Acme Robotics" },
"url": "https://app.suta.dev/projects/lobby-kiosks/alerts/8c1f…"
}
```
## Headers | Header | Value | | ---------------- | -------------------------------- | | `Content-Type` | `application/json` | | `User-Agent` | `Suta-Alerts/1` | | `Suta-Signature` | `t=,v1=` |
```http
POST /hooks/suta HTTP/1.1
Content-Type: application/json
User-Agent: Suta-Alerts/1
Suta-Signature: t=1790157600,v1=6f1d…
```
## Verify signatures `v1` is an HMAC-SHA256 of `.`, keyed with the webhook’s signing secret, as lowercase hex. To verify a request: 1. Read the raw request body before parsing it as JSON. 2. Split `Suta-Signature` into `t` and `v1`. 3. Compute the HMAC of `t`, a `.`, and the raw body. 4. Compare it with `v1` in constant time. 5. Reject the request if `t` is more than five minutes old. * Node.js
```js
import crypto from "node:crypto";
export function verify(secret, header, rawBody) {
const { t, v1 } = Object.fromEntries(
header.split(",").map((part) => part.split("=")),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
return (
fresh &&
v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
);
}
```
* Python
```python
import hashlib, hmac, time
def verify(secret: str, header: str, raw_body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = parts["t"], parts["v1"]
expected = hmac.new(
secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256
).hexdigest()
fresh = abs(time.time() - int(t)) < 300
return fresh and hmac.compare_digest(v1, expected)
```
* Go
```go
func Verify(secret, header string, rawBody []byte) bool {
var t, v1 string
for _, part := range strings.Split(header, ",") {
k, v, _ := strings.Cut(part, "=")
if k == "t" { t = v }
if k == "v1" { v1 = v }
}
ts, err := strconv.ParseInt(t, 10, 64)
if err != nil || math.Abs(float64(time.Now().Unix()-ts)) > 300 {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(t + "."))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(v1), []byte(expected))
}
```
## Delivery * Suta treats any `2xx` response as delivered. * A different status, a timeout after 10 seconds or a connection error is retried after 1, 2, 4, 8 and 16 minutes, then marked as failed. * Delivery is at least once, so the same alert can arrive more than once. Use `alert.id` and `type` to ignore duplicates. * Suta doesn’t follow redirects, and only sends to public HTTPS addresses.
# Troubleshooting
> Fixes for common setup, connection and access problems.
## Setup ### Chrome doesn’t find my device * Check that you’re using Chrome on macOS at `https://app.suta.dev`. * Allow Chrome to use Bluetooth in **System Settings** > **Privacy & Security** > **Bluetooth**. * Wait for the Pi to finish its first boot. This can take a couple of minutes. * Close any other browser tab or terminal that is setting up the same device. * If you reflashed the card, forget the old device in your Mac’s Bluetooth settings, then try again. ### `suta device add` doesn’t find my device * On macOS, allow your terminal app to use Bluetooth in **System Settings** > **Privacy & Security** > **Bluetooth**. * For Ethernet, your computer and the Pi must be on the same network, and the network must allow mDNS. Over a direct cable, wait a little longer. * When the 30-second scan ends, choose to scan again. ### The installer stops with a conflict The Pi already has an account or file named `suta` that Suta didn’t create. Suta won’t take it over. Rename or remove it, then run the installer again. ## Connection ### Device stays offline A device is online only while it sends heartbeats. Check that: 1. The Pi has power and has finished starting. 2. It has internet access. On the Pi, `curl -I https://app.suta.dev` should work. 3. The Suta agent is running: `systemctl status suta-agent`. Its recent logs are in `sudo suta logs`. To change its Wi-Fi, see [Fix a device’s network](/add-devices/repair-network/). ### Measurements look old When a device is offline, Suta shows the last values it reported, with the time of the report. They update when the device reconnects. ## Deploys ### `suta plan` says an image has no `linux/arm64` build Devices need 64-bit ARM images. Build your image for `linux/arm64`, for example with `docker buildx build --platform linux/arm64,linux/amd64`, then plan again. ### `suta plan` says a secret doesn’t exist Create it first with `suta secret set NAME`. Only workspace admins can manage secrets. ### A deploy says Podman is not installed On Raspberry Pi OS, run the installer on the device again; it adds Podman. On Bookworm, whose Podman is too old, move the device to Suta OS instead. See [Requirements](/add-devices/requirements/#deploying-apps). ### A new generation was rolled back The device found a problem during probation and returned to the previous generation. To see why, open the device’s **Software** tab, or run `suta device generations DEVICE`. Common causes are an app that exits, a probe that never passes, and a change that stops the device reaching Suta. On the device itself, `suta apps` shows each app’s state and `sudo suta logs APP` shows what it printed. After you fix the cause, plan and deploy again, or [retry the generation](/deploy/device-software/#retry-a-failed-generation). ### A rollout skipped a device See [When devices are skipped](/deploy/rollouts/#when-devices-are-skipped). For `changed_since_plan`, make a new plan. ### `suta migrate` is refused * **Deploy first**: the device doesn’t run a generation yet. Deploy one, even with no apps, then try again. * **Raspberry Pi 4 or 5 required**: the device’s model can’t move to Suta OS yet. * **Already runs Suta OS**: there’s nothing to do. ## Access ### “Permission denied” in the dashboard or CLI Your role doesn’t include that permission. Run `suta auth status` to see your permissions, and ask a workspace admin to change your role. See [Permissions](/reference/permissions/). ### Commands say remote access isn’t ready Open remote access first with `suta device access enable `, then wait until `suta device access status ` shows it’s ready. ### Commands say the device hasn’t answered Remote access is open, but the device stopped answering. The error says when it last answered. Nothing ran on the device. Usually the device lost its network. A command that changes Wi-Fi settings can cause this on a device whose only connection is that Wi-Fi. Commands work again once it reconnects. If it stays [offline](#device-stays-offline), restart it or repair its network. ### My AI agent says it needs approval The agent should give you a link. Open it, review the request and select **Approve access**. Then tell the agent to continue. See [Connect an AI agent](/ai-agents/connect/#approve-more-access). ### A CI job gets “403” with an API token Tokens can’t open terminals, run commands or open remote access, and they can only do what their role allows. See [API tokens for CI](/ai-agents/api-tokens/). ## Still stuck? Open an issue on [GitHub](https://github.com/suta-dev/suta/issues), with the device ID from `suta id` and what you tried.