---
title: "Environment variables"
description: "The environment variables MainSDK reads (MAINSDK_DEBUG, MAINSDK_TELEMETRY, MAINSDK_KEY, MAINSDK_TENANT), ingest key prefixes and OIDC in CI."
section: "reference"
canonical: "https://mainsdk.dev/docs/reference/environment"
updated: "2026-10-28"
---

# Environment variables

> The environment variables MainSDK reads (MAINSDK_DEBUG, MAINSDK_TELEMETRY, MAINSDK_KEY, MAINSDK_TENANT), ingest key prefixes and OIDC in CI.

Canonical: https://mainsdk.dev/docs/reference/environment · Updated: 2026-10-28

MainSDK needs no environment variable to run. With none set, everything works locally and nothing is sent anywhere. The variables below turn on Cloud features, debugging and secrets you keep in the environment.

## MainSDK variables

| Variable | Values | What it does | Phase, milestone |
| --- | --- | --- | --- |
| `MAINSDK_DEBUG` | `driver` | Prints each provider call (driver, operation, duration, status) with bodies and headers redacted. In production it is ignored with `MAIN-LOG-0003` unless a debug session allows it | 1, 0.2 |
| `MAINSDK_TELEMETRY` | `1` | Same as adding `mainsdk` to `telemetry.to`. Nothing is sent before `mainsdk login` (phase 2, 0.6) | 1, 0.2 |
| `MAINSDK_KEY` | `msk_live_…` or `msk_test_…` | Ingest key that lets the app send telemetry to MainSDK Cloud | 2, 0.6 |
| `MAINSDK_TENANT` | `ten_…` | Fleet tenant ID an enterprise sets in its own environment, always with the platform's OIDC token. Without a valid token, enrolment is refused and the app runs normally | 4, 1.3 |
| `MAINSDK_DEVELOPER_KEYS` | JSON object of public keys by id | Extra Cloud keys that `mainsdk info` trusts when it checks the signed developer record in `.mainsdk/developer.json`. A record that fails the check shows no level | 2, 0.7 |

`MAINSDK_KEY` is the name the product requirements use; the Cloud spec defines the keys but doesn't name the variable.

```text
$ MAINSDK_DEBUG=driver npx mainsdk dev -- next dev
driver email:local   send       4 ms   ok
driver db:sqlite     orders.get 1 ms   ok
```

## Ingest keys

Ingest keys arrive in phase 2 (0.6) with MainSDK Cloud. They only send data. The project ID in `main.yaml` names the project and never works as a credential, so a public repo is safe.

| Prefix | Environment | Created with |
| --- | --- | --- |
| `msk_test_` | dev, stage | `mainsdk keys create --env dev` or `--env stage` |
| `msk_live_` | prod | `mainsdk keys create --env prod` |

The prefixes let secret scanners catch a leaked key, and `mainsdk check` treats either one in source as a secret. Keep the key in your platform's secret store, never in `main.yaml` or a committed `.env`:

```bash title=".env.local (git-ignored)"
MAINSDK_KEY=msk_test_[your key]
```

## OIDC instead of keys

In CI and on Vercel you don't need `MAINSDK_KEY`. MainSDK Cloud accepts the platform's identity token (OIDC) in its place, so there is no long-lived key to leak or rotate.

```yaml title=".github/workflows/check.yml"
name: check
on: [pull_request]
permissions:
  contents: read
  id-token: write        # lets the job request an OIDC token
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx mainsdk check --sarif mainsdk.sarif
```

`mainsdk check` itself needs no token: it runs fully in CI. The OIDC token matters once the job sends results to MainSDK Cloud (phase 2). How the CLI picks up the token on each platform, and which audience it requests, is not decided yet. For Vercel, see [deploy on Vercel](https://mainsdk.dev/docs/guides/deploy-vercel).

## Secrets in the environment

`main.yaml` refers to provider keys as `secret:NAME`. With the `env` secrets driver, `NAME` is read from the process environment; with `local`, from `.mainsdk/secrets.json` (git-ignored).

```yaml title="main.yaml"
secrets: { driver: env }
db:
  driver: postgres
  regions: { us: secret:DATABASE_URL_US, eu: secret:DATABASE_URL_EU }
```

```bash
DATABASE_URL_US=postgres://…  DATABASE_URL_EU=postgres://…  npx mainsdk dev -- next dev
```

Resolved values are redacted everywhere they could be printed: a log line shows `[redacted:secret]`. For production, prefer a key service driver with workload identity; see [secrets](https://mainsdk.dev/docs/interfaces/secrets).

## Next

- [Deploy on Vercel](https://mainsdk.dev/docs/guides/deploy-vercel)
- [Free and Cloud](https://mainsdk.dev/docs/concepts/free-and-cloud)
- [CLI](https://mainsdk.dev/docs/reference/cli)
