# insta-cli
[](https://www.npmjs.com/package/insta)
[](LICENSE)
The InstaCloud CLI. Provision Postgres, object storage and compute, fork the whole
environment per branch, and give your coding agent scoped credentials without pasting
secrets into a chat window.
`insta` is a thin client over the InstaCloud control-plane API. Every command is one API
call, so anything you can do, an agent can do.
## Install
Native binary, no Node required (macOS / Linux / WSL). Installs to `~/.insta/bin` and
verifies the download against `SHA256SUMS`:
```bash
curl -fsSL https://raw.githubusercontent.com/InsForge/instacloud-cli/main/install.sh | sh
```
From npm:
```bash
npm install -g insta
```
For coding agents. Installs the CLI, the `insta` skill for every agent on the machine, and
registers the MCP server โ one command for macOS, Linux, WSL, and native Windows shells
(PowerShell/cmd). Needs Node 18+ with a writable npm global prefix (a Node version manager
qualifies; if the global install can't write, setup continues and prints the exact
version-pinned `npm install -g` fallback to run yourself). E2e-validated on macOS, Linux,
and Windows (PowerShell + cmd):
```bash
npx -y insta@latest agent setup
```
`insta setup agent` is still accepted as a hidden alias, so older instructions keep working.
This command means **production** (CLI โฅ 0.0.38): if the machine was previously switched to
staging it switches back โ announced, session dropped, like `insta env use prod`. Staging is
its own explicit command, which also persists the choice:
```bash
npx -y insta@latest agent setup --env staging
```
On macOS/Linux without Node, the native-binary installer puts the `insta` CLI on PATH (the
skill + MCP steps it then runs still need Node โ the skills tool runs via npx). Never run it
on native Windows โ PowerShell's `curl` alias and the WSL `bash` shim break it; use npx
above, or download `insta-windows-x64.exe` from the
[releases page](https://github.com/InsForge/instacloud-cli/releases):
```bash
curl -fsSL agents.instacloud.com | sh
```
Pin a version with `INSTA_VERSION=v0.0.22`; change the install directory with
`INSTA_INSTALL_DIR`. While the CLI is pre-1.0 it updates itself on new releases. Turn that
off with `insta config autoupdate off`.
## Quickstart
```bash
insta login
insta project create my-app
insta services add postgres db
insta services add compute api
insta secrets bind DATABASE_URL postgres/db --to compute/api
insta secrets
insta deploy .
```
`project create` makes an empty project and links the current directory. Services are
opt-in, so you add only what you need. `secrets` writes the current branch's user-defined
secrets plus the primary provider credentials of each service type to `./.env`. A service created with
`insta service add compute` starts with no provider credential bindings; bind the sources it needs before deploying. `deploy .`
builds the directory remotely and ships it to the branch's compute
service, with no local Docker. Whether it needs a `Dockerfile` depends on where the
service runs: on insta-compute it is optional, and a directory without one is built
by nixpacks on the build gateway; on legacy-plane services one is still required.
## Authentication
```bash
insta login # sign in from the browser (any account type)
insta login --email you@example.com # password from $INSTA_PASSWORD or a prompt
insta login --oauth github # or google, through the browser
insta login --env staging # log in to a specific deployment
```
Tokens are stored in `~/.insta/config.json` and refresh automatically.
Bare `insta login` opens the console's device-approval page in your browser: sign in there
with whatever your account uses (email, GitHub, Google), check the code matches, and approve.
On a machine that can't open a browser, `--device` prints the same link to open from any
other device.
`--oauth` starts a loopback listener on `127.0.0.1`, opens the browser at the control
plane's `/auth/cli/authorize`, and receives the token back on that listener once the
provider has authorized you. Nothing is pasted by hand.
If you operate your own control plane, the provider's OAuth app needs
`GITHUB_OAUTH_CLIENT_ID` / `GITHUB_OAUTH_CLIENT_SECRET` (or the `GOOGLE_*` equivalents), and
its callback URL must be `{INSTA_API_BASE_URL}/api/auth/callback/<provider>` โ the control
plane's address, not the CLI's loopback address.
## How it works
### Services are branch-scoped
A project holds services (`postgres`, `storage`, `compute`) and each branch owns its own
set. `insta branch create feature-x` forks the parent's services: a copy-on-write database
branch per Postgres, a copy-on-write bucket per storage, a clone of every compute service. From there
the two branches diverge independently. A project is capped at 10 branches.
### Credentials come from the secret seam, not a file you maintain
`insta secrets` writes the current branch's **user-defined secrets plus primary provider
credentials** to `./.env`: one credential set per service type, from that type's primary service.
`insta run <cmd>` injects the same bundle into a local child process without touching disk.
Fetching this bundle does not bind credentials to compute. A compute service receives provider
credentials only through explicit `insta secrets bind` rules; `insta service add compute` creates
a service with no provider credential bindings inherited from other services. Bind each source it
needs, then deploy or restart to apply the bindings. For a specific postgres service's connection string, use
`insta postgres url <name>` (or `insta postgres connect <name>` for a psql session).
A postgres database is reachable on its public endpoint (`DATABASE_URL`) by default.
`insta postgres private-access on` adds `DATABASE_PRIVATE_URL` beside it, which resolves only inside
InstaCloud compute; bind it into a compute with `insta secrets bind DATABASE_URL postgres/db
--source-name DATABASE_PRIVATE_URL --to compute/api`. `insta postgres public-access off` then makes the
database not reachable from the internet: it prints what will break and asks first (`--yes` when
there is no terminal), and external clients, CI and local development can no longer connect.
Secrets can be scoped per compute service, so several services may each define the same name โ and
a flat bundle cannot carry two values for one name. Such a name is **withheld** from the bundle and
reported on stderr (which services define it, and how to read one). `insta run` then **refuses to
start the command** rather than let it inherit a stale value for that name from your shell; re-run
it as `insta run --service compute/<name>` to inject exactly what that one service receives, or
`--ignore-collisions` to run with the name removed from the child environment altogether.
`insta secrets --service compute/<name>` reads the same scoped env into `.env`.
### Destructive actions can require approval
Agent requests are governed by the project's `agent-policy`; human requests use normal RBAC.
Where the agent policy says `approve`, the command stops and prints an approval id for a human
admin to grant with `insta agent approvals approve <id>`. The agent then retries the unchanged request.
Run `insta --agent agent policy get --json` for stored overrides, `defaultRules`, `effectiveRules`,
`bootstrapRules` and `ruleNotes`. Rules distinguish no affected branches (`project`), unprotected
branches and protected branches. They describe policy, not authorization: RBAC, session checks,
actual affected resources and compound actions still apply. An empty override object does not
mean rules are unavailable. Text output also lists effective rules. The old `policy` command and approval
`--always` option have been removed.
### Agents get the same surface
`insta agent manifest` prints an agent-legible view of every branch and its URLs. `insta agent
setup` installs the InstaCloud skill and registers the remote MCP server for the coding
agents on the machine โ and, when running from the npx cache with no durable `insta` on
PATH, first installs the CLI itself globally.
## Environments
`prod` and `staging` are separate deployments, in different regions, with different
databases and different auth. A session minted by one cannot authenticate against the
other, so switching environments drops the stored session and you log in again.
| | `prod` (default) | `staging` |
|---|---|---|
| control plane | `api.instacloud.com` | `api.staging.instacloud.com` |
| MCP server | `mcp.instacloud.com/mcp` | `mcp.staging.instacloud.com/mcp` |
| MCP registers as | `insta-cloud` | `insta-cloud-staging` |
| agent skills | `InsForge/instacloud-skills` | `InsForge/instacloud-skills#devel` |
| CLI channel | latest stable release | newest prerelease, else stable |
```bash
insta env # current environment and everything derived from it
insta env use staging # switch; persisted to ~/.insta/config.json
```
The control plane, the MCP host and the skill source all resolve from that one switch, so a
machine cannot end up running staging while its agents read production's skill text. The
two MCP registrations use different names, so both environments can be installed side by
side.
To install against staging directly:
```bash
curl -fsSL agents.staging.instacloud.com | sh
```
That host is a CloudFront cache, so after a change to the installer it can serve the
previous copy for up to about a day. This form is equivalent and always current:
```bash
curl -fsSL https://raw.githubusercontent.com/InsForge/instacloud-cli/main/install.sh | sh -s -- --agents --staging
```
If the environment cannot be applied โ an installed CLI older than 0.0.23 has no `insta
env` โ the installer exits non-zero and says so, rather than leaving you silently pointed
at production. The canonical usage is `curl โฆ | sh && insta project create`, often run
unattended by an agent, and a silent fallback there would provision real production
infrastructure.
Resolution order, most specific first:
1. `INSTA_API_URL` โ a literal URL, and the only way to reach a host no environment name
covers, such as a local daemon or a preview deployment. `INSTA_MCP_URL` and
`INSTA_SKILLS_REPO` do the same for the MCP host and the skill source.
2. `INSTA_ENV` โ `prod` or `staging`. An unrecognised value is an error, never a fallback.
3. The `apiUrl` persisted in `~/.insta/config.json`.
4. `prod`.
Prereleases publish with `--prerelease` on GitHub and under npm's `next` tag, so a staging
build never reaches a production installer.
## Commands
`insta --help` is the authoritative list. For flags, approval gates and plan limits, see the
[full command reference](https://github.com/InsForge/instacloud-skills/blob/main/insta/cli-reference.md).
| Command | What it covers |
|---|---|
| `insta login` ยท `logout` ยท `status` | Browser sign-in (default), `--email` + password, or `--oauth github\|google`; `status` shows the environment, login and linked project/branch |
| `insta org` | `list` ยท `create` (one free org per user) |
| `insta tokens` | API tokens for CI and agents: `list` ยท `create <name>` ยท `revoke <id>`. A new token binds to the current org by default (`--org <id>`, `--project <id>`, or an explicit `--account` for everything the account can do); `--read-only` (GET only); `--expires 30d\|90d\|1y\|never` (default 90d). The plaintext is printed once |
| `insta project` | `create` ยท `list` ยท `link` ยท `rename` ยท `delete` |
| `insta branch` | `create` ยท `list` ยท `switch` ยท `delete` ยท `merge` |
| `insta service` (`services`, `svc`) | `add` ยท `list` ยท `remove` ยท `rename` |
| `insta secrets` | Write `.env`, plus `list` ยท `set` ยท `unset` ยท `bind` ยท `unbind` ยท `bindings` ยท `sources` ยท `tree` |
| `insta domain` | Bought or bring-your-own: `attach` ยท `check` ยท `detach`. Domains already bought through InstaCloud: `list` ยท `status` ยท `records โฆ` |
| `insta compute` | `start` ยท `stop` ยท `suspend` ยท `restart` ยท `status` ยท `scale` ยท `limits` ยท `volume` ยท `always-on` ยท `exec` ยท `ssh` ยท `repo` ยท `connect-repo` ยท `watch-paths` ยท `disconnect-repo` ยท `logs` ยท `metrics` |
| `insta postgres` | `url` (print the DSN) ยท `connect` (psql) ยท `stats` ยท `limits` ยท `restart` ยท `volume` ยท `always-on` ยท `private-access` ยท `public-access` ยท `logs` ยท `metrics` โ every verb takes `[service]` |
| `insta redis` ยท `mysql` ยท `mongodb` | `query` ยท `status` ยท `limits` ยท `volume` ยท `always-on` ยท `logs` ยท `metrics` |
| `insta storage` | `list` ยท `get` ยท `delete` ยท `set-access` |
| `insta build [dir]` ยท `deploy [dir]` | Verify a source dir would build; deploy a source directory (built remotely) or `--image <url>`. `insta build logs <id>` reads source-build output: `--source archive` (default) uses the deploy operation ID, `--source github` uses a GitHub build ID; `--follow` watches output, `--json` returns one snapshot |
| `insta cron` | Branch-scoped HTTP schedules: `list` ยท `create` ยท `show` ยท `edit` ยท `pause` ยท `resume` ยท `delete` ยท `run` ยท `runs` ยท `preview`. Expressions are UTC and so is every time printed; `run` is one EXTRA execution and does not consume the next scheduled tick; literal header values are write-only, so `show` lists their names only โ a header backed by `--secret-ref` is resolved from a project secret at send time and `show` names that secret too |
| `insta run <cmd>` | Run a command with the branch bundle injected, nothing written to disk |
| `insta template` | Browse and deploy: `list` ยท `info` ยท `deploy`. Author your org's community templates: `create` (from a project, or `--blank`) ยท `drafts` ยท `draft` ยท `edit --patch <file>` ยท `regenerate` ยท `publish` ยท `unpublish` ยท `delete`. `publish` and `delete` ask on a terminal and need `--yes` anywhere else, and always for an agent |
| `insta billing` | Current cycle overview; `subscribe <tier>` ยท `portal` ยท `usage` |
| `insta agent` | `setup` (this machine's coding agents) ยท `manifest` ยท `policy โฆ` ยท `approvals โฆ` ยท `observe โฆ` ยท `events` |
| `insta config` | `install-mcp` ยท `regions` ยท `autoupdate` |
| `insta feedback` | Report an InstaCloud-side hurdle (bug / feature-request / friction) to the team โ never for the app you are building; on InstaCloud it needs `insta login`, so the team can reply, and prints the ticket it opened; `status <ticket-id>` โ New / In Progress / Resolved / Closed (the replies are in the console) |
| `insta upgrade` | Update the CLI |
Every command accepts `--api-url <url>` for this invocation only (internal debugging); for `compute exec`, place it before `compute`. `insta --help` documents it.
## Configuration
| Location | Contents |
|---|---|
| `~/.insta/config.json` | API URL, access and refresh tokens, user, the API token's scope (org/project-bound logins), auto-update preference |
| `./.insta/project.json` | Project id, org id, current branch |
| `./.insta/link-plane.json` | The control-plane URL this machine linked against. Gitignored and per machine; a link made against a different control plane is refused rather than reused. The home directory is never a project |
| Variable | Effect |
|---|---|
| `INSTA_API_URL` | Control-plane URL; outranks every other source |
| `INSTA_ENV` | `prod` or `staging` |
| `INSTA_MCP_URL` ยท `INSTA_SKILLS_REPO` | Override the MCP host and the agent-skill source |
| `INSTA_PROJECT_ID` ยท `INSTA_ORG_ID` ยท `INSTA_BRANCH` | Target a project, org or branch without linking |
| `INSTA_PASSWORD` | Password for non-interactive login |
| `INSTA_NO_AUTOUPDATE` | Disable self-update |
| `INSTA_NO_TELEMETRY` ยท `DO_NOT_TRACK` | Disable usage analytics. Each command sends one event (command, flags, outcome, version, OS) to the same PostHog project as the console. Only ids, enums and numbers among the arguments are kept โ names, branches, keys, paths, secret values, free text and error messages never leave the machine; custom API hosts report nothing. Before you sign in, events carry a random id stored in `~/.insta/telemetry.json`; the first command after you sign in sends one extra event merging that id into your account, and the first command after the session ends (logout, `env use`) retires it |
## Agent skills
The `insta` skill and its task guides live in
[InsForge/instacloud-skills](https://github.com/InsForge/instacloud-skills). `insta agent setup`
installs it user-globally for every coding agent on the machine. `insta project create` and
`insta project link` additionally install the stack skills (Tigris, Better Auth) into the
project, along with the `insta agent observe` credential-audit hook. Postgres needs no stack
skill โ it's plain Postgres, reached directly via `DATABASE_URL`.
## Contributing
Dev loop, architecture, cross-compilation and the release process are in
[CONTRIBUTING.md](CONTRIBUTING.md). Issues and pull requests are welcome.
## License
Apache 2.0. See [LICENSE](LICENSE).