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.

AI/LLM: this page is available in plain markdown at /docs/quickstart.md

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

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

npx extraorbital provision .
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:

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

Preview without changing anything with --dry-run.

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:
→ 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 and export it. EXTRAORBITAL_TOKEN is checked before any cache, so the run below prints none of the sign-in block above:

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:

npx extraorbital add redis
npx extraorbital add redis
npx extraorbital add redis

does exactly what this does:

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 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 sourceprocess.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:

npx extraorbital provision . --dry-run
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:

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:

$ 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:

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

ResourceCommandWritesFree on Prototype
MongoDBadd mongodbMONGODB_URI512 MB, up to 5
S3 storageadd s3S3_BUCKET and 4 more1 GB, 10K simple + 2K advanced ops/mo
Redisadd redisREDIS_URL, UPSTASH_REDIS_REST_*256 MB, 500K commands/mo, up to 5
Vectoradd vectorUPSTASH_VECTOR_REST_*10K queries + 10K updates/day, up to 5
QStashadd qstashQSTASH_URL, QSTASH_TOKEN and 2 more1,000 messages/day, up to 5
Authadd authlockerAUTHLOCKER_CLIENT_ID, AUTHLOCKER_CLIENT_SECRET, AUTHLOCKER_ISSUERfair use; SMS on a shared ceiling
AI Gatewayadd aiOPENROUTER_API_KEY, OPENROUTER_BASE_URLfirst $5.00 of small models
Gitadd gitGIT_URL, GIT_USERNAME, GIT_TOKEN30 repositories, 100 MB each
Stripeadd stripeSTRIPE_SECRET_KEY, NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYfree — 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 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 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.

Verify it and run it unattended
Teams, projects and multiple instances

Next