# Variable types (/environment-variables/types)



## Choose a type [#choose-a-type]

The type decides where a value goes and who can read it. Choose it from what would happen if the value leaked: public configuration can ship to the browser, server configuration stays on the backend, and credentials get the extra protection of a secret.

```mermaid
flowchart TD
  F[Frontend] -->|built in| FB[Frontend bundle]
  B[Backend] -->|environment variable| R[Backend code]
  S[Backend secret] -->|stored securely| R
  FB --> BR[Every visitor's browser]
```

| Criteria        | Frontend                                                           | Backend                                                                   | Backend secret                                                                                |
| --------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Reaches         | The browser bundle, built in at build time.                        | Backend code at runtime. Never sent to the browser.                       | Backend code at runtime. Never sent to the browser.                                           |
| Who can read it | Every visitor to the deployment.                                   | Project members who can view variables, in plain text and change history. | Hidden in the **Variables** tab after saving; you can only replace it.                        |
| Key rule        | Must start with `VITE_`.                                           | Any valid name.                                                           | Any valid name.                                                                               |
| Choose it when  | The browser needs a public value that differs between deployments. | The value is configuration, not a credential.                             | Anyone who reads the value could act as your project.                                         |
| Examples        | `VITE_API_URL`, a feature flag.                                    | `LOG_LEVEL`, `DEFAULT_REGION`, a public upstream URL, a timeout.          | A third-party API key, a database password, a webhook signing secret, an OAuth client secret. |

Where you set the type depends on the scope:

* **Full deployment**: The deployment's **Variables** tab offers **Frontend**, **Backend**, and **Backend secret** as the **Type**.
* **Project variables**: The project's **Variables** tab offers **Backend** or **Frontend** as the **Service**. Check **Secret (hidden after saving)** to make a backend value a secret.
* **Lite deployment**: Overrides take no type. Keys starting with `VITE_` go to the frontend and the rest to the backend.

Commit constants that are the same everywhere, like a support email address, to the code instead of a variable.

## Frontend values are public [#frontend-values-are-public]

Frontend values are compiled into the JavaScript that every visitor downloads. Anyone who can load the site can read them with browser developer tools. Crucible requires the `VITE_` prefix and does not offer a secret option for frontend variables. A credential cannot be marked for the browser by accident.

If the browser needs data that depends on a credential, keep the credential in a Backend secret and add a backend route that calls the upstream service. For model access, use the [LLM Gateway](/llm-gateway/) instead of storing provider keys at all.

Changing a Frontend value rebuilds the frontend, because the value is fixed at build time. A running page keeps the old value until the visitor reloads.

## When a backend value should be a secret [#when-a-backend-value-should-be-a-secret]

Use Backend for values you would be comfortable pasting into a pull request, and Backend secret for everything else. Both reach backend code the same way, as an ordinary environment variable. The choice never changes how your code reads the value.

* **Backend** values stay readable in the **Variables** tab, in deployment change history, and in the cloud provider's service configuration. That visibility makes debugging configuration easy.
* **Backend secret** values show as **Stored securely** and never appear again: not in the **Variables** tab, and not in change history, which records only the secret reference. The deployment's backend runtimes still read the value. To change one, enter a new value.
* **Work locally** gives each member the resolved values for their own machine, including secrets they are allowed to read. Keep local `.env` files out of version control.

<Accordions>
  <Accordion title="Under the hood: where each type is stored and injected">
    - **Frontend**: Written to the frontend build configuration and baked into the compiled bundle. The backend rejects frontend keys without the `VITE_` prefix and rejects secret protection on non-backend services.
    - **Backend**: For full deployments, written to the deployment's Terraform variables (`terraform.auto.tfvars`) and set as a plain environment variable on the backend workloads. Lite deployments and workspaces receive the resolved value as a backend environment variable in their managed runtime.
    - **Backend secret**: For full deployments, stored in the provider's secret store (GCP Secret Manager, AWS Secrets Manager, or Azure Key Vault). A Kubernetes Secret synced from that store is injected as environment variables into the API deployment, the Temporal worker deployment (created only when agents are enabled), and each CD task step. Crucible's own records keep only the secret reference. Each saved value becomes a new secret version. Lite deployments and workspaces receive the resolved value as a backend environment variable in their managed runtime.
  </Accordion>
</Accordions>

## Never give secret values to an agent [#never-give-secret-values-to-an-agent]

Do not paste a secret value into a chat, a prompt, a workspace agent, or any other model context. Text sent to a model can persist in transcripts, logs, and generated code, which undoes everything a Backend secret protects. Store the value as a Backend secret and refer to it only by its key, for example by asking the agent to read `STRIPE_API_KEY` from the environment.
