<!-- zibby-template-version: 1 -->
# /zibby-deploy-app — deploy a hosted app (n8n / grafana / custom goal-mode)

You are helping the user deploy a **Zibby Managed App** — a long-running hosted SaaS instance (n8n, Grafana, Outline, whatever) that runs on Zibby's managed fleet with an `agent-ops` sidecar.

There are two deploy paths. Pick **one** with the user before running anything.

| Path | When to use | Command shape |
|---|---|---|
| **Catalog** | The user wants a known-good off-the-shelf app | `zibby app deploy <appType>` |
| **Goal-mode** | The user describes a custom install in natural language | `zibby app deploy --goal "<text>"` |

Canonical docs: **https://docs.zibby.app/apps/deploying**

## Decision tree

1. **Ask the user what they want to deploy.** Examples:
   - "I want n8n" → catalog (`n8n` is in the catalog)
   - "I want Outline wiki" → catalog if present, else goal-mode
   - "Install Rails 7 + Postgres from this git repo" → goal-mode
   - "Set up a SonarQube on this VPS" → goal-mode

2. **If catalog**, list available templates first so the user picks an exact id:
   ```
   Bash(zibby app templates)
   ```
   Pick the row's `id` column (e.g. `n8n`, `grafana`, `gas-town`). Use that as the positional arg.

3. **If goal-mode**, get one concise English sentence describing the desired end-state — e.g. `"a running n8n instance with the latest stable image, exposing port 5678"`. Goal-mode is the **LLM bootstrap path**: `agent-ops` reads the goal and runs an autonomous install loop inside the app's task.

## Pre-flight (both paths)

Always confirm with the user:

- **Project** — the deploy lives under a project. If they have multiple, run `Bash(zibby list)` to show options. The CLI will prompt interactively if `--project` isn't passed.
- **Friendly name** — `--name "<text>"` (optional). Defaults to `<appType>-<short-id>`. Useful when running multiple instances of the same template.
- **Auth on the public URL** — every app gets a `https://*.apps.zibby.app` URL. Anyone with the URL can hit it unless you put auth in front. Ask: "Should this be public, or behind basic-auth / a bearer token?" See `/zibby-set-auth` for the deeper walkthrough. Pass `--auth-type basic|token|none` at deploy time to set it from the start.

## Catalog deploy

```
Bash(zibby app deploy <appType> --project <id> [--name "..."] [--auth-type basic --auth-user admin --auth-password $(openssl rand -hex 16)])
```

The catalog path is **deterministic** — no LLM runs to figure out the install; the backend uses a baked task definition. Cold start is ~2-3 minutes (image pull + first boot).

## Goal-mode deploy

```
Bash(zibby app deploy --goal "<text>" --project <id> \
    [--provider claude|codex] [--model <id>] [--anthropic-token sk-ant-...] \
    [--max-turns 80] [--timeout-min 45] \
    [--auth-type basic --auth-user admin --auth-password $(openssl rand -hex 16)])
```

Key flags:

- `--provider` — `claude` (default) or `codex`. Picks which agent drives the install.
- `--model` — explicit model id (e.g. `claude-sonnet-4-6`). Defaults to a known-cheap model.
- `--anthropic-token` — per-deploy Claude credential override. Format: `sk-ant-oat01-...` (OAuth) or `sk-ant-api03-...` (API key). **Sensitive — never echo back.** Defaults to the workspace-stored token configured in Settings.
- `--max-turns` — caps the agent's tool-call budget. Range 1..200. Heavy installs (n8n, OpenHands, anything that npm-installs hundreds of packages) blow past the 25 default — bump to 60-100.
- `--timeout-min` — caps the bootstrap task's wall-clock. Range 1..120. Heavy installs need 30-45 min.

Cold start for goal-mode is **5-30 minutes** depending on what's being installed. Don't be surprised by long-running bootstrap.

## After the deploy call

The CLI prints `{ instanceId, url, projectId, status }`. Save the `instanceId` — every other `/zibby-app-*` command takes it.

1. **Tail the supervised loop** while it boots so you (and the user) can see what's happening:
   ```
   Bash({ command: "zibby app logs <instanceId> -t", run_in_background: true })
   ```
   Goal-mode prints each agent turn. Catalog prints the app's stdout.
2. **Check status periodically** until `status` reaches `running`:
   ```
   Bash(zibby app status <instanceId>)
   ```
3. **Tell the user the URL** (from the deploy output). If they set `--auth-type basic`, also print the credentials (and tell them to save them — auth is rotateable but the initial password isn't recoverable).

## Common failure modes

- **402 from billing** → workspace doesn't have an active Apps subscription. Direct to https://zibby.dev/billing.
- **`--anthropic-token must start with sk-ant-oat01- or sk-ant-api03-`** → user pasted a Claude Code interactive session token; those are IP-bound and don't work in cloud. Tell them to run `claude setup-token` for a long-lived OAuth token, or use an Anthropic API key.
- **Goal-mode times out at `--timeout-min`** → install was too heavy. Suggest re-running with `--timeout-min 60 --max-turns 120` and a more specific goal.
- **Status sticks at `pending`** → image pull is slow. Wait another 90s. If still pending, run `/zibby-app-status` and surface the failure reason from the response body.

## Goal-mode safety

Goal-mode runs `agent-ops` autonomously inside the app's task. It can `shell` to anything inside the task's filesystem + egress proxy. It CANNOT touch other apps, other accounts, or your local machine — it's sandboxed. But you ARE responsible for what gets installed; license terms of any software the agent picks apply to the user, not Zibby.

Ask before goal-mode if the user's request is ambiguous about cost or licensing ("install a database on a real server" → "open-source or commercial? Postgres/MySQL/SQLite?").
