# InstaCloud for AI agents

InstaCloud is agent-native cloud infrastructure: managed Postgres, object storage, and
compute, with a whole environment you can fork per branch. A coding agent drives it end to
end through the `insta` CLI — every command is one control-plane API call, so anything a
human can do, an agent can do.

> This is the canonical agent document. It is served identically at
> `https://instacloud.com/auth.md`, `/agents.md`, and `/skill.md`.

## Install

One command installs the `insta` CLI globally, the `insta` skill for every coding agent on
the machine, and the remote MCP server. It works verbatim on every OS and shell (Node 18+),
is non-interactive on a headless machine, and always targets **production** (staging is its
own explicit form: append `--env staging`):

```bash
npx -y insta@latest setup agent
```

On macOS or Linux without Node, one command does both via a native binary — never run it
on native Windows, where PowerShell's `curl` alias and the WSL `bash` shim break it:

```bash
curl -fsSL agents.instacloud.com | sh
```

Or install just the CLI — from npm (any OS), or the native binary (no Node; macOS/Linux
only, same never-on-native-Windows caveat):

```bash
npm install -g insta
curl -fsSL https://raw.githubusercontent.com/InsForge/insta-cli/main/install.sh | sh
```

## Authentication

Using InstaCloud requires an account. An agent gets a credential one of four ways: autonomous
registration through its provider (below), claimed registration confirmed by the user with a code,
the device flow on a headless machine, or a normal browser login.

### Autonomous registration (ID-JAG)

If your agent runs inside a participating provider that can mint a WorkOS ID-JAG (an identity
assertion carrying the user's verified email), it can register with no human sign-up form and
receive an InstaCloud CLI credential directly. Discover, register, then use it.

**1. Discover.** Fetch the protected-resource metadata, then the authorization-server metadata it
references:

```http
GET https://api.instacloud.com/.well-known/oauth-protected-resource
GET https://api.instacloud.com/.well-known/oauth-authorization-server
```

Use the `resource` value as the `aud` when minting the ID-JAG. The `agent_auth` block lists the
supported method: `identity_assertion` with an `id-jag` assertion, credential type `api_key`.

**2. Get consent, then register.** First confirm the user consents to assert their identity to
InstaCloud, using the `resource_name`, `resource_logo_uri`, and scopes from discovery. Then have
the provider mint a WorkOS ID-JAG with `iss` `https://auth.workos.bot`, `typ` `oauth-id-jag+jwt`,
alg `RS256`, `aud` set to the `resource` above, a stable `sub`, the user's `email` with
`email_verified: true`, a fresh `jti`, and a near-term `exp` (about 5 minutes). Post it:

```http
POST https://api.instacloud.com/agent/auth
Content-Type: application/json

{
  "type": "identity_assertion",
  "assertion_type": "urn:ietf:params:oauth:token-type:id-jag",
  "assertion": "<ID-JAG JWT>",
  "requested_credential_type": "api_key"
}
```

InstaCloud verifies the assertion against the provider's published keys, matches or creates the
user by verified email, and returns an `insta_` API key (the same credential `insta login
--api-key` accepts). Errors use OAuth-style `{ "error": "<code>", "error_description": "<details>" }`.

**3. Use it.** Log the CLI in with that key, then work as the user:

```bash
insta login --api-key insta_xxxxxxxx
insta project create my-app
insta status
```

Autonomous registration is in preview. Only agents whose provider participates (it signs the
ID-JAG) can use it. Every other agent uses the device flow below.

### Claimed registration (any agent, human confirms a code)

If your agent cannot mint an ID-JAG but knows the user's email, register with `service_auth`.
InstaCloud gives you a 6-digit code and a link. The user opens the link, signs in (or signs up)
as that email, and types the code. Only that user can complete it. You then receive an `insta_`
API key.

**If `insta` is on the machine, this is one command.** It runs the whole ceremony for you, and it
is the path to take unless you have no CLI:

```bash
insta --agent login --claim user@example.com > /tmp/insta-claim.log 2>&1 &
```

It goes in the background because it waits for the user. Poll that file until the link and the
6-digit code appear, show both to the user in one message, then keep polling until it prints
`logged in as`. Run it in the foreground, or pipe it through `tail` or `head`, and you will see
nothing: those hold the output until the command finishes, and it does not finish until the user
has already entered the code.

Everything below is the REST flow that command performs, written out for an agent that cannot
install the CLI. If you already ran the command above, you are done here.

**1. Discover.** Same two documents as above. `agent_auth.identity_types_supported` lists
`service_auth`, and `grant_types_supported` lists `urn:workos:agent-auth:grant-type:claim`.

**2. Confirm with the user, then register.** Tell the user you are about to register with
InstaCloud as their email, then post:

```http
POST https://api.instacloud.com/agent/auth
Content-Type: application/json

{ "type": "service_auth", "login_hint": "user@example.com", "client": "claude-code" }
```

`client` is optional and one of `claude-code`, `codex`, `cursor`, `unknown`. The 200 response
carries `claim_token` (keep it in memory, you poll with it) and a `claim` block with `user_code`,
`verification_uri`, `expires_in` and `interval`.

**3. Hand off.** Show both in one message and be explicit that the code goes into the page, not
back to you:

```text
Open this link, sign in as user@example.com, and enter this code: 482913
https://console.instacloud.com/claim?claim_attempt_token=...
```

**4. Poll.** Every `interval` seconds:

```http
POST https://api.instacloud.com/api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=clm_...
```

That body is the whole request. There is no `client_id` and no `client_secret`, and `grant_type`
must be exactly that URN: it is what routes the request to the claim handler, so a request without
it reaches the plain OAuth endpoint and comes back complaining about a missing `client_secret` or
an unsupported grant.

`authorization_pending` means keep waiting. `slow_down` means add 5 seconds. `expired_token`
means the code lapsed: post `{ "claim_token": "clm_..." }` to
`https://api.instacloud.com/agent/auth/claim`, show the new code, keep polling. A 200 carries
`access_token`, which is your `insta_` key. Errors use the OAuth envelope
`{ "error": "<code>", "error_description": "<details>" }`.

**5. Use it.**

```bash
insta login --api-key insta_xxxxxxxx
```

Without a CLI, send it as `Authorization: Bearer insta_xxxxxxxx` on the control-plane API.

### Device flow (headless / no local browser)

On a headless machine (an agent in a VM, an SSH box, a CI container) the browser can't reach a
local callback, so use the **device flow**: the human approves a short code in a browser on any
other device while the CLI polls.

The easy path — the CLI runs the flow and stores the credential for you:

```bash
insta login --device   # prints a link + code; approve it in any browser, then it polls
```

To drive the endpoints yourself instead (RFC 8628) — e.g. from an agent that calls the API
directly rather than shelling out to the CLI:

**1. Request a device code** (client `insta-cli`; no secret):

```http
POST https://api.instacloud.com/api/auth/device/code
Content-Type: application/json

{ "client_id": "insta-cli" }
```

The response carries `device_code`, `user_code`, `verification_uri`,
`verification_uri_complete`, `expires_in` (900s), and `interval` (5s).

**2. Have the user approve.** Show them `verification_uri_complete` (it points at
`https://console.instacloud.com/device?user_code=…`). They sign in and click Approve.

**3. Poll for the token,** waiting `interval` seconds between polls:

```http
POST https://api.instacloud.com/api/auth/device/token
Content-Type: application/json

{ "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code": "<device_code>", "client_id": "insta-cli" }
```

`400 { "error": "authorization_pending" }` while waiting; `400 { "error": "slow_down" }`
means add 5s to your interval; `200 { "access_token", ... }` once approved. The
`access_token` is a session bearer — send it as `Authorization: Bearer <access_token>`.

**4. A long-lived key comes from the human, not from you.** The device-flow `access_token` is a
normal session and eventually expires; when it does, run the device flow again. Do **not** mint an
`insta_` key yourself (`insta tokens create` / `POST /tokens`): an `insta_` key is an ordinary
account credential, so an agent that minted one could step outside its own project's agent policy.
Agent-marked requests (`insta --agent`, or a key from agent registration) are refused with
`403 unclassified_agent_action`; do not retry, and do not work around it with a plain session. If this machine
needs a standing credential (CI, an agent you won't return to), ask the human to mint one in their
own terminal, not through this session (by default it is bound to one org and expires in 90 days):

```bash
insta tokens create agent-<hostname>
```

then adopt the key they hand you, once:

```bash
insta login --api-key insta_xxxxxxxxxxxxxxxxxxxxxxxx
```

### CLI login (machine with a browser)

```bash
insta login --oauth github        # or google — opens the browser
insta login --email you@example.com
```

## Start working

After login, provision a project and go. `project create` makes an empty project and links
the current directory; services are opt-in, so add only what you need:

```bash
insta project create my-app        # create + link ./ to the project
insta status                       # confirm login + linked project
insta services add postgres db     # add a Postgres service
insta services add compute api     # add a compute service
insta secrets bind DATABASE_URL postgres/db --to compute/api   # give compute its credentials
insta deploy .                     # build remotely and ship to the branch (needs a Dockerfile)
```

Credential handling follows the installed skill: service credentials reach compute through
`insta secrets bind`, and local commands that need user-defined secrets run under
`insta run -- <cmd>`, which injects them into that process only. Nothing is written to disk.

Fork the whole environment (database + storage + compute) for a branch with
`insta branch create <name>`, and print an agent-legible view of every branch and its URLs
with `insta agent manifest`.

## Resources

- CLI + agent skills: `insta setup agent` (installed by the install flow above)
- Remote MCP server: register with `insta mcp install`
- Environments: `prod` (default) and `staging` are separate deployments — a session from
  one cannot authenticate against the other.
