App env vars

Declare the keys your app reads, set the values in the dashboard, read them with your language's standard env-var API at runtime. No SDK required.

The two-line model

Every app follows the same three steps:

  1. Declare keys in your repo's .env.example file. This is the Leash convention regardless of language — we scan this one file to know which keys to inject. Commit it; placeholder values are fine — only the key names are read.

    .env.example
    STRIPE_SECRET_KEY=
    OPENAI_API_KEY=
  2. Set values in the dashboard at /dashboard/apps/<id>/secrets. Values are encrypted at rest and injected into your Cloud Run revision at deploy time.

  3. Read them in your app code using your language's standard env-var API:

    const stripeKey = process.env.STRIPE_SECRET_KEY

Where values can come from

Step 2 — “set values” — has three flavors. Pick whichever matches how your org already stores secrets.

Dashboard direct entry

Paste values directly in the app's Secrets tab. Available on every plan. Best for small teams that don't already run a secrets manager.

SaaS token sources

Connect an existing secrets manager and Leash will resolve values from it on each deploy. Supported today:

  • Doppler
  • Infisical
  • 1Password
  • Bitwarden

Configure once at /dashboard/organization/secrets; every app in the org can resolve from it.

Requires Growth plan or above.

Cloud-native federation

Federate directly to your cloud provider's secret store — no values ever leave your account. Coming soon:

  • AWS Secrets Manager
  • GCP Secret Manager
  • Azure Key Vault
Requires Scale plan or above.

Upgrade path

The model above is deploy-time: values are baked into the container when you ship. If you need any of:

  • Rotate a key without redeploying
  • An audit trail of who read which value when
  • Central, immediate revocation

Move those keys to the SDK's dynamic env namespace. See Dynamic env vars — same key names, fetched on demand through @leash/sdk.

Related

  • Dynamic env vars — when you need rotation without redeploy, audit trail per read, or central revocation.
  • Secret sources — connect Doppler, Infisical, 1Password, or Bitwarden as a backing store for org-wide secrets.
  • Rotation gotcha: values read via your language's standard env-var API are injected at container start. Rotating a dashboard secret only takes effect on the next deploy — for instant rotation, use leash.env.get.
  • Local dev — pull the same values into your local environment for development with leash dev.

Encryption

Values are encrypted at rest in GCP Secret Manager under a Cloud KMS CMEK key. By default the key is held by Leash. Org owners can bring their own KMS key via the wizard at /dashboard/organization/security/encryption/cmek — paste a key resource name, Leash runs an encrypt+decrypt round-trip to validate the binding, and from that point on all new writes use your key. Revert to Leash-held at any time.

Audit log

Every source create/edit/revoke, every required-keys update, and every resolved-values pull writes a row at /dashboard/organization/security/audit. Owners can scan the log for anything unusual.

How rotation behaves

Rotating a value in your source updates Leash immediately, but when running app code sees the new value depends on how it reads the secret:

Read mechanismRotation behavior
process.env.XBake-time. The value is mounted into the Cloud Run revision at deploy. Re-deploy (or hit “sync now”) to pick up a new value.
await leash.env.get('X')Runtime fetch. The next call after rotation gets the new value (per-instance cache, ~60s); { fresh: true } bypasses the cache.
Custom MCP bearerPer-call resolve. The token is fetched fresh via getCustomMcpConfig on every call — picks up the new value on the next call.