# Secrets & API keys

Sooner or later your app will need a key for an outside service: Stripe to charge cards, OpenAI
to call a model, SendGrid to send mail. You don't want those keys living in the source code —
they're secrets, they get rotated, and you don't want to redeploy the whole app to change one.

onvibe stores them for you, **encrypted at rest**, and injects them into the app on every deploy.
There's an explicit "request a secret, fill it yourself" flow so your AI never has to see the
value at all.

## The recommended flow — your AI never sees the value

This is how to keep your secrets out of the chat entirely. Useful for production keys, anything
billable, or anything that gets shared with end users.

1. Tell your AI what slot to create:

   > *"My app needs a Stripe key — request a `STRIPE_KEY` slot for the my-shop project."*

2. Your AI calls `set_env` **without a value**. The platform creates an empty slot marked
   *Pending*, and returns a dashboard URL to your AI.

3. Your AI hands you the URL: *"Open <onvibe.run/dashboard/projects/my-shop/env#STRIPE_KEY>,
   paste your key, and save."*

4. You open the page, paste the value into the input field, and click **Save**. The page never
   shows the value back to anyone.

5. Ask your AI to deploy. The value is now available to the app as
   `Deno.env.get("STRIPE_KEY")`.

The secret travels only from your screen to the encrypted store. It never enters the
conversation or your AI's context.

## The shortcut — when you'd rather paste it in chat

If a key is low-stakes (a sandbox token, a personal API key you don't mind sharing), you can
just paste it:

> *"Save my Stripe **test** key as `STRIPE_KEY` on the my-shop project: `sk_test_abc123…`"*

Your AI calls `set_env` with the value. It's stored encrypted the same way, status *Set*, ready
to inject on the next deploy.

## Managing what's there

To list the keys (just names, never values):

> *"List the env vars on my-shop."*

For each key your AI gets back the **name**, its **status** (*Pending* if the slot is waiting,
*Set* if filled), and the last update date.

To remove one:

> *"Delete `OLD_KEY` from my-shop."*

To rotate, you can either set it again from chat (shortcut) or open the dashboard page and
update the value there.

## On the dashboard

Go to **Dashboard → your project → Environment variables**. The page shows every variable, its
status, and one field per row to paste a new value. There's also a free-form box at the bottom
to add a variable manually (without asking your AI first).

Values never come back out — there's no "reveal" button. If you forget what a value was, the
only path is to set it again.

## Naming rules

Keys must look like `STRIPE_KEY` or `OPENAI_API_TOKEN`: uppercase letters, digits, and
underscores; starting with a letter. Your AI will tell you if a name doesn't fit.

A handful of names are reserved by the platform — `DATABASE_URL`, `APP_ID`, `APP_URL`,
`APP_TOKEN`, and the various `UPLOADS_*` / `ASSET_URL_ENDPOINT` ones. Those are injected
automatically and can't be overridden.

## Where this fits

This is for **runtime secrets your app needs** (third-party API keys, signing keys, feature
flags as opaque values). It's not for:

- **End-user uploads** — those have their own dedicated flow, see
  [Files & images](/docs/files-and-images).
- **Database credentials** — `DATABASE_URL` is provisioned and injected for you, no setup.
- **Things you'd hardcode** — if it's not actually secret, regular code is fine.
