# Quickstart

> A working project in one command, starting from an empty directory or an existing one — no console, no card, no API keys to copy.

You need Node.js 20 or later. There is no card form and no console to visit — every
example below is the exact command and the exact output the CLI prints.

## Provision what you need

<Tabs>
<Tab label="Autopilot">

Run it from the project root. It reads what the project already says it needs and
fills in the gaps:

```bash
npx extraorbital provision .
```

```txt
Provisioning 1 resource for .

  mongo  MONGODB_URI · unfilled in .env.local

✓ Created project cosmic-otter → .extraorbital.json
✓ Provisioned mongo/default (database cosmic-otter-default)
✓ Wrote 1 variable to .env.local — MONGODB_URI
→ Commented out your own MONGODB_URI so the provisioned value wins

1 resource created · no card required
```

Your placeholder `MONGODB_URI=` was commented out, because dotenv keeps the first
definition it sees and a real credential below an empty one would never be read.
Run it again and nothing happens, which is the point:

```bash
npx extraorbital provision .
# ✓ Nothing to provision — every variable we support already has a value
```

Preview without changing anything with `--dry-run`.

</Tab>
<Tab label="One resource">

Know what you want? Name it, from any directory:

```bash
npx extraorbital add redis
```

```txt
✓ Created project cosmic-otter → .extraorbital.json
✓ Provisioned redis/default (database cosmic-otter-default, us-east-1)
✓ Wrote 3 variables to .env

  UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN, REDIS_URL

No card required · extraorbital ledger to see spend
```

</Tab>
</Tabs>

## Identity, with no console visit

Nothing above named a project or an account, and that is by design, not by magic:

- **The machine name is generated locally, with zero setup.** Every install derives a
  stable label like `brave-fox-a3f2` from the host and user running it (overridable
  with `EXTRAORBITAL_MACHINE`) and sends it on every request, so `extraorbital list`
  and the ledger can tell your laptop apart from a fleet of agents without you ever
  registering one.
- **The project is generated too.** The first `add` or `provision` in a directory
  with no `.extraorbital.json` invents a name (`cosmic-otter`), creates it, and
  writes the pointer file — that's the `✓ Created project …` line above.
- **A token is the one thing that still needs a human, and only the first time.** If
  there is no cached credential and no `EXTRAORBITAL_TOKEN`, the very first command
  on a machine prints this before doing anything else:

```txt
→ No identity found. Signing in…

  Open https://extraorbital.dev/device and enter code B4VX-7T2Q
  Opened your browser

⠋ Waiting for confirmation…
```

Approve it once in a browser — this works over SSH and inside a container, since
only the code needs to reach a browser, not the CLI — and the command you actually
ran continues right where it left off. The token is cached in
`~/.extraorbital/credentials.json` at mode `0600`, so every later command, on that
machine, skips this entirely.

For a container that should never see a browser, skip the wait: mint a key from
[`/settings/keys`](/settings/keys) and export it. `EXTRAORBITAL_TOKEN` is checked
before any cache, so the run below prints none of the sign-in block above:

```bash
export EXTRAORBITAL_TOKEN=eo_live_…
npx extraorbital provision .
```

There is no anonymous path: every resource still belongs to a human's account, one
human approval unlocks it, and everything after that — the machine label, the
project, every following provision — is autonomous.

Both commands write a fenced block to `.env` (or `.env.local`, if the project
already has one), and both are idempotent — the same address always returns the
same resource and the same credentials:

```bash
npx extraorbital add redis
npx extraorbital add redis
npx extraorbital add redis
```

does exactly what this does:

```bash
npx extraorbital add redis
```

which is what makes both commands safe in a boot script, a retry loop or a CI job.

## How autopilot decides

`extraorbital provision` reads three signals, and only ever looks for the exact
variable names the [catalog](#what-you-can-provision) publishes — it cannot invent a
resource:

1. **`.env.example`** (or `.env.sample`, `.env.template`) — a declared contract, and
   the strongest signal
2. **A variable present but unfilled** in `.env` or `.env.local` — empty, or left at
   a placeholder like `your-bucket` or `<connection string>`
3. **The variable's exact name in your source** — `process.env.MONGODB_URI`,
   `os.environ["MONGODB_URI"]`, `os.Getenv("MONGODB_URI")`, in any language

Then it subtracts everything that already holds a real value, and provisions one
resource per service that is left, at slug `default`. `node_modules`, `dist`,
`.next`, build output and lockfiles are never scanned, so a dependency mentioning
`MONGODB_URI` does not provision you a database. `--dry-run` prints the plan and the
reason for each line without changing anything:

```bash
npx extraorbital provision . --dry-run
```

```txt
Would provision 2 resources for .

  mongo  MONGODB_URI · declared in .env.example
  redis  UPSTASH_REDIS_REST_URL +1 · used in packages/worker/src/queue.ts

  Left alone: 5 variables that already have a value
```

## Use it

The credentials are standard, so any client works unchanged:

```ts title="upload.ts"
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({ region: process.env.S3_REGION });

await s3.send(
  new PutObjectCommand({ Bucket: process.env.S3_BUCKET, Key: "hello.txt", Body: "hi" }),
);
```

## When a human is needed

Every resource starts on the Prototype plan's free allowance — no card, no quota
request. A provision is only refused once the account is past that allowance, or
already holds as many of that resource as the Prototype plan permits:

```txt
$ npx extraorbital add s3

! Your team has spent its Prototype allowance and cannot add s3. Move to a paid plan by
  having a human open the link below — then you are billed only for what you use.

  A human must link a payment method to continue:
  https://extraorbital.dev/link/fl_8a2c91d4e7b3

  Link expires in 24h · extraorbital link for a fresh URL
  Exit code 3 (payment required)
```

Only *new* provisioning is refused — everything already running keeps running, and
every existing credential keeps working. An agent that would rather not fail at all
can mint the same link ahead of time:

```bash
npx extraorbital link
```

On a paid plan there is no resource ceiling: $20 a month, then only what you use,
at what it costs us. The agent never sees a card.

## What you can provision

| Resource | Command | Writes | Free on Prototype |
| --- | --- | --- | --- |
| [MongoDB](/docs/resources/mongo) | `add mongodb` | `MONGODB_URI` | 512 MB, up to 5 |
| [S3 storage](/docs/resources/s3) | `add s3` | `S3_BUCKET` and 4 more | 1 GB, 10K simple + 2K advanced ops/mo |
| [Redis](/docs/resources/redis) | `add redis` | `REDIS_URL`, `UPSTASH_REDIS_REST_*` | 256 MB, 500K commands/mo, up to 5 |
| [Vector](/docs/resources/vector) | `add vector` | `UPSTASH_VECTOR_REST_*` | 10K queries + 10K updates/day, up to 5 |
| [QStash](/docs/resources/qstash) | `add qstash` | `QSTASH_URL`, `QSTASH_TOKEN` and 2 more | 1,000 messages/day, up to 5 |
| [Auth](/docs/resources/authlocker) | `add authlocker` | `AUTHLOCKER_CLIENT_ID`, `AUTHLOCKER_CLIENT_SECRET`, `AUTHLOCKER_ISSUER` | fair use; SMS on a shared ceiling |
| [AI Gateway](/docs/resources/ai-gateway) | `add ai` | `OPENROUTER_API_KEY`, `OPENROUTER_BASE_URL` | first $5.00 of small models |
| [Git](/docs/resources/git) | `add git` | `GIT_URL`, `GIT_USERNAME`, `GIT_TOKEN` | 30 repositories, 100 MB each |
| [Stripe](/docs/resources/stripe) | `add stripe` | `STRIPE_SECRET_KEY`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | free — one shared test account |

Allowances are counted across the account rather than per resource, so five
databases share one 512 MB rather than getting 512 MB each, and they last as long
as the resource is used — a Prototype resource nothing has touched for 30 days is
deleted, after a warning a week out. On a paid plan nothing expires, backups run,
and usage costs what it costs us — see [pricing](/pricing) for the table.

Only the two model-key services meter usage today; the rest report `metered: false`
rather than printing a measured-looking `$0.00`.

Everything above is provisioned for your project alone, with one exception:
[Stripe](/docs/resources/stripe) hands out test-mode keys to a single account every
ExtraOrbital project shares. It is the fastest way to build a checkout flow with no
human in the loop, and it is not somewhere to keep anything — read that page before
you use it.

<Details>
<Summary>Verify it and run it unattended</Summary>

See everything in the project, including resources other machines added:

```bash
npx extraorbital list
```

```txt
  cosmic-otter · 2 resources · $0.00 this month

  RESOURCE       STATUS  CREATED BY       CREATED       THIS MONTH
  mongo/default  active  brave-fox-a3f2   Aug 1 09:14   —            (not metered)
  s3/default     active  brave-fox-a3f2   Aug 1 09:16   —            (not metered)
```

Check every credential on every boot:

```bash
npx extraorbital check
```

```txt
✓ mongo/default  credentials valid (reachable in 44ms)
✓ s3/default     credentials valid (reachable in 91ms)

2 of 2 healthy
```

`check` exits `0` when the whole environment is good, which makes it the ideal first
line of an agent's boot script. `check --fix` re-provisions anything missing and
rewrites the block.

In CI, skip the browser with a key from [`/settings/keys`](/settings/keys):

```bash
export EXTRAORBITAL_TOKEN=eo_live_…
npx extraorbital provision .
npx extraorbital check --quiet || npx extraorbital check --fix
```

Every command takes `--json` and exits with a distinct code, so a supervising agent
can branch without parsing text. See [exit codes](/docs/cli#exit-codes).

</Details>

<Details>
<Summary>Teams, projects and multiple instances</Summary>

Resources belong to a team and a project, and one project can hold several
instances of the same resource — any part you leave out defaults to `default`:

```bash
npx extraorbital add mongodb --team vercel
npx extraorbital add mongodb --team vercel --project skills.dev
npx extraorbital add mongodb --team vercel --project skills.dev --slug analytics
```

A project id may be a plain name or a domain — `cosmic-otter` and `skills.dev` are
both valid. A directory belongs to **one team and project pair**, written to
`.extraorbital.json`, which we encourage you to commit:

```json title=".extraorbital.json"
{
  "projectId": "skills.dev",
  "teamId": "team_9c1f77a2_vercel"
}
```

Set the pair once with `init`, and later commands need no flags at all:

```bash
npx extraorbital init --team vercel --project skills.dev
npx extraorbital provision
```

Passing `--team` or `--project` that disagrees with the committed pair is an error
rather than a silent second home for your resources. If you made a mistake, move the
directory deliberately:

```bash
npx extraorbital switch --team acme --project skills.dev
```

</Details>

## Next

- [CLI reference](/docs/cli) — every command and flag
- [API reference](/docs/api) — the REST surface behind the CLI

---

More for agents: [Docs index](https://extraorbital.dev/sitemap.md) · [llms.txt](https://extraorbital.dev/llms.txt) · [agents.md](https://extraorbital.dev/agents.md)
