> ## Documentation Index
> Fetch the complete documentation index at: https://sponson.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Write a plan, read the diff, apply it: locally and in GitHub Actions.

### Install

Install the CLI from npm (Node.js 22 or later):

```bash theme={null}
npm install -g sponson    # or run it without installing: npx sponson <command>
```

To run an unreleased commit instead, run it from source (the workspace packages resolve to their TypeScript
sources, so it runs through the `tsx` loader rather than a build):

```bash theme={null}
git clone https://github.com/MishaBear94/Sponson ~/sponson && (cd ~/sponson && pnpm install)
alias sponson="node --import ~/sponson/node_modules/tsx/dist/loader.mjs ~/sponson/packages/cli/src/bin.ts"
```

Then, in your app's repository:

```bash theme={null}
sponson init --setup  # detects the stack, writes release.plan.yaml, then sets up each provider's token (below)
sponson plan          # read-only diff
sponson apply         # creates the branch, injects the variable, waits for the deploy
```

`--setup` turns the plan into credentials you can run it with, in one pass in your own terminal. For each provider the
plan uses it shows the page where the token is created and the least it needs, takes the token from the environment
or asks for it (hidden input, never written to disk), and checks it with one read-only request. Then, after a yes for
each, it stores the checked tokens as GitHub Actions secrets of your `origin` with your own `gh` (the value goes to
`gh secret set` on stdin, nowhere else), and writes `.github/workflows/sponson.yml` with the action pinned to the
commit of the installed version's release tag (an existing file is never overwritten). TODOs the plan still has (a
placeholder such as a project id `init` could not find) are listed first, and the workflow is not offered until they
are filled in: it would run a plan that cannot work. A default the plan runs with but you should look at (a recipe's
documented default, such as Stripe's webhook path, marked `# CHECK:`) is listed as "please check" and blocks nothing.
Run it again any time, also on an existing plan; `--yes` accepts every offer for scripted use, `--json` reports what
`init` detects (`todo` and `check`), each credential's status without a value, and `next`: `{ command, why }`, where
`command` is the command to run next (`sponson plan` once ready, `sponson init --setup` when a token only needs to be
typed) or `null` when the next step is not a command (filling in a TODO, making a new token), and `why` says what to do.
Agents ask a human to run it rather than handle a token themselves ([SKILL.md](/guides/agents)).

```
$ sponson init --setup
Wrote release.plan.yaml
Added .sponson/ to .gitignore
Detected: Vercel (.vercel/project.json), Neon (package.json: @neondatabase/serverless), Clerk (package.json: @clerk/nextjs), Next.js (package.json: next)
Credentials for release.plan.yaml:

Neon  NEON_API_KEY
  create it: https://neon.com/docs/manage/api-keys
  pick:      A project-scoped API key for the one project (an organization admin creates it); otherwise a personal API key.
  Sponson calls Neon with this key; it is Sponson's credential, not one of your app's variables.
  NEON_API_KEY (input hidden, Enter to skip):
  ok: GET /projects/proj_demo (reaches project proj_demo)

Vercel  VERCEL_TOKEN
  create it: https://vercel.com/account/tokens
  pick:      A token whose Scope is the team that owns the project, with an expiration.
  Sponson calls Vercel with this token; it is Sponson's credential, not one of your app's variables.
  VERCEL_TOKEN: from the environment
  ok: GET /v9/projects/prj_demo (reaches project prj_demo)

Clerk  CLERK_SECRET_KEY
  create it: https://dashboard.clerk.com/last-active?path=api-keys
  pick:      The Secret Key of the instance whose redirect URLs the plan manages (sk_test_ for development); Clerk secret keys have no scopes.
  Sponson calls Clerk with this key; it is Sponson's credential, not one of your app's variables.
  CLERK_SECRET_KEY (input hidden, Enter to skip):
  unauthorized: clerk: GET /redirect_urls?paginated=true&limit=1 → 401: {"error":"chaos"}

GitHub Actions secrets of acme/web (with your `gh`; the value goes to its stdin only):
  Store NEON_API_KEY as an Actions secret of acme/web (replaces one of that name)? [y/N] y
  NEON_API_KEY: stored
  Store VERCEL_TOKEN as an Actions secret of acme/web (replaces one of that name)? [y/N] y
  VERCEL_TOKEN: stored
  CLERK_SECRET_KEY: not offered (no value, or it did not check ok)

  Write .github/workflows/sponson.yml (the action pinned to the commit of sponson@<version>)? [y/N] y

Workflow: wrote .github/workflows/sponson.yml (action pinned to 3f1c0a9e7b2d, sponson@<version>).

Not ready yet: Fix the credential that did not check ok (Clerk: unauthorized), then run `sponson init --setup` again and `sponson plan`.
To try Sponson first without any account, against a local fake cloud: `npx --yes @sponson/sim --demo sponson-demo`
```

`<version>` is the installed version; the commit is the one its release tag points at.

Where each provider's token is created and what to pick is listed per provider in [docs/providers.md](/reference/providers).

`sponson init` reads the repository's files only (no network, no credentials): `.vercel/project.json` and
`vercel.json`, `netlify.toml` and `.netlify/state.json`, `package.json` dependencies, Prisma and Drizzle configs,
`.neon`, and the variable *names* in
`.env.example`, `.env.local` and the other `.env*` files. It never reads a value out of them, except to recognise a
database host such as `*.neon.tech`. It writes a line only for what it found (one database: a Neon branch, a PlanetScale branch and
password, or a Supabase preview branch, in that order of preference when several are detected; the Vercel or Netlify preview
variable that references it under the name your code reads; a Clerk redirect or a Supabase Auth redirect for the
preview URL), fills in the project ids it can find, and marks each one it cannot with a `TODO` comment saying where to
look (`vercel link`, `netlify link`, `neonctl projects list`, `supabase link`). LaunchDarkly gets a commented example line to fill in with a
flag key and uncomment. A Wrangler configuration (`wrangler.toml`, `wrangler.json`) gives a Cloudflare Pages
variables line with the project's `name`; Pages preview variables are shared by every preview, so no per-PR value
is wired into them. A provider managed by [recipes](/reference/recipes) gets its recipe's line, ready when the plan has
everything it needs and commented out with a `TODO` otherwise. With nothing detected it writes the Vercel + Neon
template. Besides the plan, `init` writes one line, `.sponson/` (where local receipts go), into `.gitignore`.

A service `init` detects that no adapter or recipe manages yet is listed as "not supported yet", with where to ask for it (or why no API can manage it), and never silently dropped: today Pusher Channels and Liveblocks.

```
$ sponson init
Wrote release.plan.yaml
Added .sponson/ to .gitignore
Detected: Vercel (.vercel/project.json), Neon (package.json: @neondatabase/serverless), Clerk (package.json: @clerk/nextjs), Next.js (package.json: next), Prisma (package.json: @prisma/client)
To fill in:
  providers.neon.project (now "proj_xxx"): run `neonctl projects list`, or https://console.neon.tech → your project → Settings → General → Project ID
Next: fill in the TODOs, set VERCEL_TOKEN, NEON_API_KEY, CLERK_SECRET_KEY in your environment, then run `sponson plan`
Receipts: each `sponson apply` records what it did on an orphan branch of this repository (`sponson-receipts/<env>/<scope>`, never merged), so the next run, on any machine or in CI, sees drift and knows what to destroy.
```

A value is filled in once its `# TODO` comment is gone too: until then it counts as a placeholder. `sponson init --json` reports the same under `detected` (also on a re-run with `--setup`, where `todo` is what the plan still has):
`found` and `unsupported` (each with its `evidence`), `todo` (each placeholder's `path` and `hint`) and `assumed` (true
when the plan starts from the template). Paths are relative to the working directory in text and absolute in JSON.

Which providers are covered today, and what is planned, is measured in [docs/coverage.md](/reference/coverage).

Beyond the built-in adapters, any JSON REST API is reachable with the generic `http` adapter, and the providers it
has been verified against ship as [recipes](/reference/recipes): one plan line names a provider operation and fills in
its params, for example `recipe: cloudflare.dns_cname` with a `zone_id`, a `name` and a `target`. Each recipe cites the
API docs it was checked against and runs through create, re-apply, drift and destroy in the test suite.

Third-party adapters and secret sources load as plugins: `SPONSON_PLUGINS=sponson-adapter-x,./local-adapter.mjs`, each module exporting `register(registry)`.

Credentials come from the providers' own conventions (`VERCEL_TOKEN` for Vercel; every adapter's variable is listed under [built-in ops](/reference/plan-format#built-in-ops)). Sponson has no credential store of its own: `sponson init --setup` checks them and hands them to GitHub's.

In GitHub Actions, one workflow with three triggers calls the same action:

```yaml theme={null}
on:
  pull_request: { types: [opened, synchronize, closed] }
  deployment_status:
permissions: { contents: write, pull-requests: write, deployments: read }
jobs:
  sponson:
    runs-on: ubuntu-latest
    # only finished preview deployments; production has its own approved job (see action/README.md)
    if: >-
      github.event_name == 'pull_request' || (github.event.deployment_status.state == 'success' &&
      github.event.deployment.environment != 'Production' && github.event.deployment.environment != 'production')
    env:
      VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
      NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
      CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
    steps:
      - uses: actions/checkout@v4
        # full history lets Sponson recognise a late build of an older commit
        with: { ref: "${{ github.event.deployment.sha || github.sha }}", fetch-depth: 0 }
      - uses: MishaBear94/Sponson/action@<commit-sha>
        with:
          command: ${{ github.event.action == 'closed' && 'destroy' || 'apply' }}
```

The action comments the plan and the receipt on the pull request. See [action/README.md](/guides/github-actions).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.