Secrets

How leash dev and leash deploy turn the keys in your repo's .env.example into a runtime environment — locally and on the deployed service.

Declaring required secrets

Leash injects only the env keys you explicitly declare. List them in your project's .env.example:

.env.example
# Required env keys for this app
HUBSPOT_API_KEY=
LINEAR_API_KEY=
SLACK_BOT_TOKEN=

When you run leash dev, the CLI reads this file, matches the keys against secrets you've configured in the dashboard, and injects matches into your local process. Keys you don't declare are not injected — keeping your local env explicit and auditable.

When you run leash deploy, the same file drives which secrets are mounted on the deployed service.

Commit .env.example to git

The file is the manifest the CLI uses on every machine. Empty values are fine — Leash only reads the key names. Never commit .env or .env.local.

How leash dev resolves a secret

Each key in .env.example takes this path:

.env.example                         (your repo, the manifest)
   |
   v
leash dev / leash deploy             (CLI parses key list)
   |
   v
POST /api/apps/<id>/secrets/sync     (platform matches keys -> sources)
   |
   v
org-level secret sources             (Native, Doppler, 1Password, GCP SM, ...)
   |
   v
process environment                  (injected into your dev process or
                                      mounted on the Cloud Run revision;
                                      read via process.env, os.environ,
                                      os.Getenv, ENV[], etc.)

See it run:

Terminal

$ leash dev

✓ Synced 3 required keys from .env.example

✓ Resolved 3 secrets from 1 source

Starting `npm run dev` with 3 secrets injected...

TypeScript only: generate a declaration that types process.env from your manifest.

Terminal

$ leash secrets types

✓ Wrote leash-secrets.d.ts (3 keys)

Secret scoping

Secret sources are configured once per organization by an admin (one HubSpot OAuth, one Linear API key, etc.). All apps in your org can request those secrets — but each source carries a per-app allow-list of which apps see it.

The dashboard URL /dashboard/apps/<id>/secrets is the per-app grant view: “which org-level secrets does THIS app see?” The actual credentials live at the org level at /dashboard/organization/secrets.

ConceptWhere it livesWho configures it
Source (the credentials)/dashboard/organization/secretsOrg owner
Per-app grant/dashboard/apps/<id>/secretsApp owner / org owner
Required keys.env.example in repoAnyone with commit access

Wire once, reuse everywhere means the source-level wiring (OAuth, API key, KMS-encrypted value) happens once at the org. Each app then declares which keys it needs via .env.example and is granted access by an admin.

Common issues

  • No .env.example, no injection. leash dev errors with a hint to create the file. Add it (even with empty values) and re-run.
  • Key declared but missing on the deployed app. The org has no source defining that key, or the app isn't in the source's allow-list. The required-keys section of the app detail page surfaces both.
  • Rotated value not picked up. Secrets are baked into the Cloud Run revision at deploy time. Re-run leash deploy (or click sync-now in the dashboard) to roll the running revision.

For the full org-level model — sources, encryption, audit log — see Environment Variables. For the full leash dev command reference (flags, auto-detection, sample output), see leash dev.