> ## 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.

# Introduction

> Sponson is the plan for everything that ships beside the code.

[![ci](https://github.com/MishaBear94/Sponson/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/MishaBear94/Sponson/actions/workflows/ci.yml)
[![license: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/MishaBear94/Sponson/blob/main/LICENSE)
[![node: >=22](https://img.shields.io/badge/node-%3E%3D22-brightgreen.svg)](https://github.com/MishaBear94/Sponson/blob/main/package.json)
[![docs](https://img.shields.io/badge/docs-sponson.mintlify.site-0D9373.svg)](https://sponson.mintlify.site)

Sponson is Terraform-style plan/apply for per-PR preview state (database branches, env vars, auth callbacks) in
one YAML file, with drift detection and teardown.

A sponson is the float welded to the side of a hull so the boat does not roll. The code is the hull. The preview database, environment variables, callbacks, and feature flags are the float. Sponson writes that float into one file an agent can read and a human can diff.

```yaml theme={null}
# release.plan.yaml
version: 1
providers:
  vercel: { project: prj_xxx }
  neon:   { project: proj_xxx }

changes:
  - id: db
    adapter: neon
    op: branch
    environments: [preview]

  - id: env
    adapter: vercel
    op: env
    target: preview
    values:
      DATABASE_URL: { from: db.connection_string }      # a reference, never a value
      STRIPE_KEY:   { secret: "env://STRIPE_KEY" }      # a reference, never a value
    environments: [preview]

  - id: callback
    adapter: clerk
    op: redirect_allow
    url: { from: env.preview_url }                      # exists only after the deploy
    environments: [preview]
```

```
$ sponson plan
sponson plan · preview · pr-42 · plan 0583f85b

+ db        neon.branch           create   Neon branch sponson/preview/pr-42
? env       vercel.env            pending  waiting on `db`
    + DATABASE_URL (preview, feat/checkout)  (pending ← db.connection_string)
    + STRIPE_KEY (preview, feat/checkout)  (secret ← env://STRIPE_KEY)
? callback  clerk.redirect_allow  pending  waiting on `env` (deploy)
    + Clerk redirect  (pending ← env.preview_url)

1 to create, 2 pending
```

The first `apply` creates the branch and writes both variables, then stops at the deploy barrier: the callback needs
the preview URL, which exists once the deploy finishes. The next `apply` (the one the deploy triggers in CI) finishes
it.

```
$ sponson apply
sponson apply · preview · pr-42 · run run-1a2b3c4d

+ db        neon.branch           applied  Neon branch sponson/preview/pr-42
+ env       vercel.env            applied  DATABASE_URL (preview, feat/checkout), STRIPE_KEY (preview, feat/checkout)
? callback  clerk.redirect_allow  waiting  waiting on deploy

outputs
  db.branch_id = br-11
  db.host = br-11.sim.neon.tech

apply partial: callback waits for the deploy
```

Both are real output: `pnpm docs:gen` runs this plan against the fake cloud below and writes what the CLI prints here.

## Try it in 60 seconds

No Vercel, Neon or Clerk account needed: `@sponson/sim` is a local fake cloud with their APIs. You need Node.js 22
or later, git and curl. In one terminal, start it with a ready demo repository:

```bash theme={null}
npx --yes @sponson/sim --demo sponson-demo
```

Leave it running. In a second terminal, run these one at a time (the first terminal prints the same list):

```bash theme={null}
cd sponson-demo && source sim.env
sponson plan
sponson apply
sponson apply
curl -s -o /dev/null "$SPONSON_SIM_URL/_chaos" -d '{"drift":{"vercel.env.preview.DATABASE_URL":"postgres://typed-in-the-console"}}'
sponson plan
sponson apply --destroy
```

1. `sim.env` points `sponson` at the fake cloud with placeholder tokens, and makes `sponson` the published CLI of the
   sim's own version, through npx (a shell function, so a global install of another version cannot get in the way).
2. `plan` reads live state and prints the diff of `release.plan.yaml`: a Neon branch to create, and a `DATABASE_URL`
   and a Clerk redirect that wait on it. Nothing is written.
3. `apply` creates the branch, writes its connection string into Vercel's `DATABASE_URL` by reference (the password
   is never printed), finds the preview deployment of this commit, and allows its URL as a Clerk redirect.
4. The same `apply` again: every line `unchanged`, zero writes.
5. The `curl` plays someone editing `DATABASE_URL` in the Vercel console.
6. `plan` reports the drift and blocks that line (`x`, exit 1): `apply` will not overwrite it without `--reconcile`.
7. `apply --destroy` removes everything the demo created, in reverse order, and warns that `DATABASE_URL` was
   changed outside Sponson before it deleted it. Ctrl-C in the first terminal stops the fake cloud.

`scenarios/docs/try-it.test.ts` runs these exact commands from this file on every CI run.

## Why this exists

Agents can already change a repository. They cannot safely change the rest of a release.

The code lands in Git. The things that have to move with it do not. A preview database lives in the database console. Environment variables live in the deploy console. OAuth callbacks live in an identity console. Feature flags live in a fourth. A person copies state between them by hand. An agent does the same thing with no shared record of what it intended, what it tried, and what a human approved.

Existing tools each own one slice. Terraform and Pulumi plan resources, not the preview data and callback that belong to this release. Vercel and Railway deploy the app, not the secrets and flags that have to match it. Doppler and Infisical hold secrets, not the release those secrets belong to. A sequence of agent tool calls is not itself a record. A release needs its own file.

Sponson is that file. The format is the product. A hosted approval inbox can come later. A new cloud does not.

## What it does

One release, one plan. Three commands.

| Command | What it does | Writes anything? |
| - | - | - |
| `sponson init` | Detects your stack from the repository's files (every [supported provider](/reference/providers), the framework and ORM) and writes a plan for it, with the project ids it can find and a `# TODO:` naming where to find each one it cannot. Run again to adopt resources the plan does not know about. | the plan file, and `.sponson/` (local receipts) in `.gitignore` |
| `sponson plan` | Reads live state, prints the diff and any drift. Exits 1 when a line is blocked (`x`): drift, a missing secret, or a refused line it depends on. | no |
| `sponson apply` | Runs the plan in dependency order. Rolls back what this run created if a line fails. Writes a receipt. | yes |

* **Secrets are references.** `{ secret: "env://NAME" }`, or any other [secret scheme](/reference/plan-format#secret-schemes). Every command resolves them, `plan` and `apply --destroy` included (so a scheme backed by a secret manager's CLI runs that CLI on `plan` too), and registers each value for masking before anything is printed; only `apply` hands values to an adapter. They are redacted from every byte of output. A literal that looks like a secret is rejected at parse time.
* **References cross lines.** `{ from: db.connection_string }` reads another line's output. Outputs that only exist after an external event (a deploy) stop the run with status `partial`; the next `apply` continues from there. Same command, no flags.
* **Drift is reported, never silently overwritten.** Something changed in a console since the last apply? `plan` says so; `apply` refuses that line until you pass `--reconcile`. Something exists that the plan does not mention? It is listed and left alone.
* **Receipts are the agent's memory.** Each run writes what actually happened to an orphan branch of your repo, one per environment and scope (`sponson-receipts/<env>/<scope>`, so unrelated pull requests never wait on each other): a ledger of every resource the scope owns (kept across failed, refused and crashed runs), what each line did, and which commits were applied. Creates are recorded before they are sent, so even a write whose response was lost is never forgotten. The agent reads the receipt, not its own last tool call.
* **Adopting is not copying.** `sponson init` adopts existing resources as `{ keep: true }`: Sponson takes over that they exist, keeps their live values, and never destroys them.
* **Production needs a human.** `--env production` without `--approved-by` is refused before any adapter is touched, and so is any run with a line that writes to production (a Vercel `target: production`, a LaunchDarkly environment that is production). The default environment is always `preview`; nothing is inferred from a branch name.
* **Destroy is symmetric.** `sponson apply --destroy` removes what Sponson created, in reverse order, and never touches resources it merely adopted. A resource Sponson created that someone edited since (changed drift) is deleted too, since the pull request is closing, but never silently: the output, the PR comment and the receipt (`notes.changedOutside`) name it.
* **What no API can do is a step for a person.** A [`manual.step`](/reference/plan-format#manual-step) line (a Google OAuth redirect URI for the preview URL) shows its instructions as a todo, holds back the lines that depend on it until someone runs `apply --confirm <line>`, records who confirmed it and when, and asks for its undo on destroy. Agents show the step to a person; they never confirm it.

## Supported providers

Sponson manages per-environment state at **64 providers** in 13 categories, from deploy targets and databases to auth callbacks, feature flags, webhooks and DNS: 17 first-class ops and 68 recipe ops. `sponson init` detects 59 of them from a repository's files and writes their lines.

[docs/providers.md](/reference/providers) lists every one: what Sponson manages there, the op or recipe, the credential, what `init` detects it by and a line to start from.


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