Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion insta/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ keys and raw request data must not be included in source control or approval rep
| `insta domain nameservers set <domain> <nameservers...>` · `nameservers reset <domain>` — both take [`--org <id>`] [`--json`] | **(CLI ≥ 0.1.1)** delegate a **bought** domain's zone away from InstaCloud **to nameservers of yours**, or put it back (for InstaCloud's own nameservers use `domain delegate`, above — it keeps hostnames serving; `set` does not). The nameservers are space- or comma-separated and **must already host the zone**, because some registries verify before accepting the change. **`set` takes down every hostname the domain serves**, unless the ones you name are the registrar's own: the records `domain attach` published live in the zone you are leaving, so the platform marks each hostname `failed` with the reason and the CLI prints it — that is the answer, not an error. The published records are **left in place**, because a zone nothing answers from is inert; `reset` un-delegates and `domain attach` re-adopts them, so a hostname `set` took down comes back with an attach, not with the reset alone. While a domain is delegated **away** like this, **`domain attach` is refused** (409), and `domain list`/`status` say so on the domain's own line — a domain on a *managed* zone is not in this state. `reset` from a **managed** zone is the mirror move: record custody returns to the registrar and hostnames a managed zone was serving re-verify on their own. `--json` on both is the whole purchased-domain object. **Agent credentials: both answer 403 `unclassified_agent_action`** — the same org-scoped-write rule as `records` above — so hand the exact command to the user. |
| `insta --agent domain zone delegate <domain>` · `zone list` · `zone records <domain>` · `zone release <domain>` — all take [`--org <id>`] [`--json`] | **(CLI ≥ 0.1.4; platform BYO zones)** bring-your-own domains on **nameserver delegation** — the BYO twin of `domain delegate`, for a domain owned at an **outside** registrar (a bought domain has its own `delegate` verb above; the two never mix — a BYO claim of a bought name is a 409 naming the right door). `zone delegate` builds an InstaCloud-managed zone for the domain, seeds it from the provider's public-record **scan** (a HEURISTIC: common type/name combinations, not everything) and answers the **two nameservers** to set at the domain's registrar. From then on `domain attach` publishes its records into the zone itself — **apex included**, which the print-the-records path can never serve. **The contract is review-then-switch**: read `zone records <domain>` (every type shows — CAA, SRV included), add anything missing at your **CURRENT** DNS provider, re-run `zone delegate` (idempotent for the owning org; the re-run re-triggers the import), and only then switch the nameservers — records the review misses drop at the switch. A domain carrying **live MX records is refused outright** (moving mail-bearing DNS can drop mail) — as is a domain bought through InstaCloud, one overlapping an existing delegated tree (one zone covers a whole domain tree), one with an open purchase order, the 201st live zone (a 200-per-org quota), a billing-suspended org (release stays available), and a domain whose registry ALREADY answers from InstaCloud's pair with nothing on file (a TXT proof at `_instacloud-zone-challenge.<domain>` is demanded — fails closed on resolver trouble, so it is reachable on a blip). **Every refusal sentence names its fix — read it and relay it**; note two are TRANSIENT retries, not verdicts: "could not be checked for mail records just now" and the resolver-blip proof demand. `zone list` shows `waiting for nameservers` (with the pair) vs `delegated`; activation is the platform's ~minutely sweep noticing the registry switch, not a live probe. `zone release <domain>` prunes what the platform published, deletes the zone, and prints the next step — point the nameservers back at your own provider; hostnames re-verify on the records path. `zone delegate` and `zone release` need **org admin** (a member gets 403 `requires admin role`); `zone list` and `zone records` are member-level reads — any member can run the review step. The platform answers 501 when managed DNS is not enabled on the deployment. **Agent credentials: `zone delegate` AND `zone release` are both gated `zone.delegate` and follow the project's agent policy exactly like `domain.delegate`** — `full_access` executes outright, `branch_specific` answers 202 `approval_required` (relay it, not an error), `read_only` refuses; the two reads follow the ordinary member read path. **The projectless-credential rule on the `domain delegate` row applies here verbatim**: an `insta_` key or MCP assertion with no linked project is judged org-wide and anything less than every-project-`full_access` is refused with a misleading `project.billing.update` denial — run it from a linked project. |
| `insta domain transfer lock <domain> <on\|off>` · `transfer code <domain>` — both take [`--org <id>`] [`--json`] | **(CLI ≥ 0.1.1)** take a **bought** domain to another registrar. Two steps on purpose: `lock off` opens the **registrar** transfer lock, `code` reads the **EPP authorization code** the gaining registrar asks for, and the code alone moves nothing. **Both need org `admin`** — a `member` gets 403 — because either hands the domain to someone else. **ICANN's own 60-day lock** on a new registration outranks `lock off` and nothing here can waive it: `lock off` prints the date it is held until, when that is still in force. **Nothing refreshes the row after a transfer completes — not even a read** — so do not report the domain as gone. `--json` is the purchased-domain object for `lock` and `{ authCode }` for `code`; plain output for `code` is the bare code alone on a line, so it can be captured. **Agent credentials: both answer 403**, so hand the commands to the user. |
| `insta --agent compute start\|stop\|suspend [service]` · `insta --agent compute status [service]` [`--json`] | control a compute service's lifecycle — **persistent override** of auto scale-to-zero: `stop`/`suspend` take it offline and traffic will **not** wake it until `start`; `status` shows desired vs. live state. All plans; ungated. `[service]` defaults to the project's sole compute service |
| `insta --agent compute start\|stop\|suspend [service]` · `insta --agent compute status [service]` [`--json`] | control a compute service's lifecycle: `stop` keeps it offline until `start`; a normal `suspend` allows traffic to wake it but does not clear an existing stop; `start` clears the stop; `status` shows desired vs. live state. All plans; ungated. `[service]` defaults to the project's sole compute service |
| `insta --agent compute restart [service]` [`--branch <b>`] [`--json`] | **(CLI ≥ 0.0.51)** **re-run the image reference the service already runs**, against a freshly resolved env bundle — it asks for no new version and no new spec, though it does **not pin a digest**: a service recorded against a moving tag (`app:latest`) gets whatever that tag resolves to now (source deploys record a unique label and are unaffected). The two reasons to use it: a binding changed and the running app hasn't picked it up (env is baked into the machine at deploy time — a user secret set with `secrets set`/`unset` redeploys itself and does not need this), or the machine is up but **wedged** (`start` no-ops on a machine that is already `started`). The service must be **running**: a deliberately stopped/suspended one 400s and points at `insta --agent compute start`. A never-deployed service 400s like `exec` does. **A running machine is health-gated coming back up** — if the app doesn't answer on its port the machines are rolled back (best-effort) to the config they were serving and the failure is reported (that verdict means the app is broken, not the platform). Whether an **idle machine (a scale-to-zero service between requests) is woken and gated at all depends on the compute plane** (`insta --agent agent manifest --json` names it per compute row — `insta-compute`, a legacy value, or a neutral `compute` when the platform reported none) — a legacy plane hands it the new config without waking, so the command returns fast, bills no uptime, and proves nothing about whether the app boots. Send it a request if you need that proof; see [operate.md](references/operate.md). All plans; **gated: `deploy`** — it lands configuration the way a deploy does, so a policy denying deploys denies this too; `start`/`stop` stay ungated and cycle a wedged machine without one. Refused while the org is billing-suspended; any machine it wakes bills as ordinary uptime, an idle one it leaves asleep costs nothing. WebSocket concurrency **is** re-asserted (it is recorded on the service), so a socket app does not need a redeploy to stay one |
| `insta --agent compute exec [service] -- <command> [args...]` [`--branch <b>`] [`--timeout <sec>`] [`--json`] | run a **one-shot** command on the service's live machine — no interactive shell, no stdin. Deploy and exec migrations are separate, non-atomic operations; a 502 can follow execution. Check the migration ledger and final schema before retrying (see [Migration recovery](references/deploy.md#database-migrations)). Wakes a scaled-to-zero machine first (the wake counts as billed uptime). `--timeout` bounds the run, **1–180s** (default 30). At most **64 argv entries (the command included), each ≤ 65,536 characters, ~1 MiB in total** — move a larger payload through storage. The CLI's **exit code is the remote command's exit code** — safe for scripts/agents to branch on. stdout/stderr stream to their own local streams verbatim, each capped at **1 MiB** (truncation noted on stderr); `--json` returns the raw response instead of split streams. Gated on **both** `deploy` and `secrets.read` — a deny on either is a 403. A service with no image ever deployed 400s: "this service has no machines yet — deploy an image first, then retry". **`--api-url <url>` must come before `compute`** here (`insta --api-url <url> --agent compute exec …`) — everything after `--` is the remote command's own argv |
| `insta compute ssh [service]` [`--setup`] [`-b, --branch <b>`] [`--json`] | **(CLI ≥ 0.0.71)** issue a **short-lived SSH certificate** for a compute service and print the `ssh` command that uses it. **It does not open a session** — it makes `ssh` work; you then run the printed command yourself. **Agents cannot use this: it requires an interactive login and refuses API keys (403) — use `insta --agent compute exec` for one-shot commands.** Gated on **both** `compute.shell` and `secrets.read` (a shell inherits the service's decrypted env), and `compute.shell` **defaults to `approve`** for branch-specific agents. Bare = mint a certificate (default **1h**, max 24h) and print a self-contained `ssh -i … -o CertificateFile=… -o IdentitiesOnly=yes <user>@<host>`. `--setup` additionally does the **one-time client setup**: generates a dedicated key at `~/.insta/ssh/id_ed25519` (your own keys are never touched), adds one `@cert-authority` line to `~/.ssh/known_hosts` so every region is trusted without per-node fingerprint prompts, and writes a block **at the TOP of `~/.ssh/config`** (first-obtained-value wins in ssh_config) giving the service the alias **`<service>.insta`**. After that it is plain `ssh api.insta`, `scp` and `-L`: the block renews the certificate while OpenSSH parses the config, so the alias keeps working unattended. An alias already pointing at a **different** project/branch/service is **refused**, not silently repointed. **Compute-plane dependent** — a legacy-plane service has no SSH gateway and 400s naming `compute exec` instead (`insta --agent agent manifest --json` names the plane per compute row). On **Windows** the block omits connection multiplexing, which Win32-OpenSSH does not implement. A fourth flag, `--ensure-cert <alias>`, exists but is **internal**: it is the renewal hook the generated config invokes (as `insta __ssh-ensure-cert <alias>`) and is silent by design — never call it yourself |
Expand Down
7 changes: 3 additions & 4 deletions insta/references/operate.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,11 @@ to stop; those constraints lift after deletion.

## Pausing & resuming compute

To take a service **offline on purpose** — a maintenance window, cost control, or parking a
preview branch — use the lifecycle controls, which are a *persistent* override: a stopped/suspended
service will **not** be re-woken by incoming traffic (unlike scale-to-zero's auto-wake).
To keep a service **offline until you start it**, use `compute stop`. A normal `compute suspend`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: compute stop does not guarantee the service stays offline until start: a deploy can bring it live while desired state remains stopped. Qualify both descriptions to cover traffic-driven wake and call out deploys as an exception.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At insta/references/operate.md, line 173:

<comment>`compute stop` does not guarantee the service stays offline until `start`: a deploy can bring it live while desired state remains stopped. Qualify both descriptions to cover traffic-driven wake and call out deploys as an exception.</comment>

<file context>
@@ -170,12 +170,11 @@ to stop; those constraints lift after deletion.
-To take a service **offline on purpose** — a maintenance window, cost control, or parking a
-preview branch — use the lifecycle controls, which are a *persistent* override: a stopped/suspended
-service will **not** be re-woken by incoming traffic (unlike scale-to-zero's auto-wake).
+To keep a service **offline until you start it**, use `compute stop`. A normal `compute suspend`
+allows incoming traffic to wake the service; it does not clear an existing stop.
 
</file context>

allows incoming traffic to wake the service; it does not clear an existing stop.

- `insta --agent compute stop [service]` — clean shutdown; stays down until `start`.
- `insta --agent compute suspend [service]` — snapshot RAM for a faster resume; stays down until `start`.
- `insta --agent compute suspend [service]` — snapshot RAM for a faster resume; traffic can wake it unless it was already stopped.
- `insta --agent compute start [service]` — bring it back online and re-enable auto-wake.
- `insta --agent compute status [service]` — desired (your intent) vs. live runtime state.

Expand Down
Loading