App environment variables
Reference
Environment variables are how apps see their configuration at runtime. Each app has one runtime environment; the platform injects values into the deployed pod. Locally, the bundled greenlight CLI injects the same contract into your dev process — see Local development below.
All env var values live in the customer’s Azure Key Vault. The control-plane database stores metadata only: the name, environment, sensitivity flag, vault reference, actor, and timestamps.
Names vs values
Section titled “Names vs values”Two halves come together at deploy:
- The names that reach the pod are governed by
greenlight.yml: theenv:block lists user-controlled names, and managed names are derived from the declaredresources:andgrants:. - The values are written separately with
envSet(or in the dashboard) and stored in Key Vault.
Env var values are never committed to the repository.
Plain vs sensitive
Section titled “Plain vs sensitive”| Kind | Where it lives | Visible in dashboard? |
|---|---|---|
| Plain | Key Vault | Yes, when explicitly revealed |
| Sensitive | Key Vault | Yes, the name only — value never shown |
Plain variables are for non-sensitive configuration: feature flags, hostnames, log levels. Sensitive variables are for tokens, signing keys, and other secrets.
A variable is marked sensitive in the dashboard or via envSet with sensitive: true. Once marked sensitive, it cannot be un-marked without removing and re-adding the value.
Environment scope
Section titled “Environment scope”| Environment | Vault path | Used by |
|---|---|---|
shared | apps/<app-id>/env/shared/<NAME> | The deployed pod and local greenlight run |
Each app uses a single shared environment; its values apply wherever the app runs — deployed or under the local CLI.
Reserved names
Section titled “Reserved names”Greenlight injects these automatically based on what greenlight.yml declares. Apps can read them but cannot set them via envSet — the reserved set is the union of every name the platform could inject, so a name stays reserved even before the app declares the resource or grant that would supply it.
| Name | Purpose |
|---|---|
GREENLIGHT_PROXY_URL | Base URL of the data broker (present once a proxied integration is granted). |
GREENLIGHT_DATA_KEY | Workload key the app sends to the proxy in the integration’s normal credential slot; injected into the deployed pod. |
DATABASE_URL | Present when a postgres resource is declared in greenlight.yml. |
STORAGE_ACCESS_URL | Present when a blob resource is declared, on Azure and GCP local access. On Azure today, the legacy STORAGE_SAS_URL name is also injected with the same value — new code should prefer STORAGE_ACCESS_URL. |
STORAGE_ACCESS_TOKEN | GCP local access only: a short-lived downscoped token paired with STORAGE_ENDPOINT. Not used on Azure. |
STORAGE_ENDPOINT | GCP local access only: the endpoint STORAGE_ACCESS_TOKEN authenticates against. Not used on Azure. |
STORAGE_CONTAINER_NAME | App-owned blob container name. |
PORT | Port the platform expects the app to listen on. |
PUBLIC_BASE_URL | App URL after first deploy. |
DEV_USER_EMAIL | Local identity simulation fixture. |
DEV_USER_GROUPS | Local identity simulation fixture. |
GREENLIGHT_AI_BASE_URL | AI inference gateway (when enabled). |
GREENLIGHT_AI_KEY | AI gateway key (when enabled). |
Integrations are delivered one of two ways, and that choice decides which names appear:
- Proxied — the app calls
GREENLIGHT_PROXY_URLwithGREENLIGHT_DATA_KEYwhere the upstream credential would normally go, and the broker swaps in the real credential. The secret never reaches the app. - Injected — the credential value is placed directly into the app’s environment under a name IT chose, and the app calls the upstream itself.
Each injected integration the app is granted reserves the env-var name IT picked for it, so that name can’t collide with a user-declared variable either.
Setting variables
Section titled “Setting variables”- Dashboard — immediate, audited.
envSet— same behavior from the agent;envdefaults toshared, and areasonis recorded in the audit log.
Setting a value stores it; the running app picks it up on its next deploy. Every name declared in the manifest env: block must have a value before it deploys, or the deploy fails with MISSING_ENV_VALUE — so set the value before merging the PR that declares the name.
await envSet({ app_id: 'app_k9x2m3p', name: 'APPROVAL_SECRET', value: '…', env: 'shared', sensitive: true, reason: 'Signing secret for the approvals webhook.',});Org policy
Section titled “Org policy”Org-level policy can restrict env operations — for example, requiring approval before a change applies. When a policy blocks an operation, envSet / envRemove return env.policy_denied (HTTP 403 over REST) with the reason and approval path in next_steps. Every env operation, allowed or denied, is written to the audit log; values are never recorded.
Local development
Section titled “Local development”App code is byte-identical on a laptop and in the cluster: it reads the same env-var names and gets the same values. The difference is purely in how those values are delivered locally.
The bundled greenlight CLI delivers them. Pair it once over your agent’s MCP session, then start the app with greenlight run:
greenlight run --app <app_id> -- npm run devgreenlight run --app resolves the app’s environment contract server-side and injects the values into the child process only — nothing is written to a file on disk. (Without --app, run operates in user mode on your own personal grants — same no-file, short-lived-token handling, but user-scoped and with no app resources; see Local development.) The managed plumbing is rewritten to local-safe equivalents:
GREENLIGHT_PROXY_URLpoints at the same public proxy URL the deployed app uses;GREENLIGHT_DATA_KEYis a short-lived, app- and user-scoped token the CLI mints from your session. The upstream secret stays in Greenlight, and every proxied call is attributed to you by name.- A
blobresource is reached through a short-lived credential the CLI mints per session. - The app’s own Postgres runs as a local fixture database; you read deployed rows through the
inspectAppDbtool rather than connecting to the live database.
To read real deployed data without leaving the laptop, the agent uses the always-on, results-only inspectAppDb / inspectIntegrationApi / inspectAppBlob tools — see the MCP tools reference. A granted integration is live locally; only user-delegated sources are fixture-only. The complete loop is in Local development.