Dynamic env vars
Most apps use process.env.YOUR_KEY — see App env vars. That's fine for static values injected at deploy time.
Use leash.env.get when you need something process.env can't do: rotation without redeploys, an audit trail of every read, or centralized revocation across every app in the org.
Use it
import { Leash } from '@leash/sdk/leash'export async function GET(req: Request) {const leash = new Leash({ request: req })// Single keyconst stripeKey = await leash.env.get('STRIPE_SECRET_KEY')// Force-fresh — bypass the in-memory cacheconst fresh = await leash.env.get('STRIPE_SECRET_KEY', { fresh: true })// Bulk fetchconst { OPENAI_API_KEY, STRIPE_KEY } = await leash.env.getMany(['OPENAI_API_KEY','STRIPE_KEY',])return Response.json({ ok: true })}
The TypeScript SDK (@leash/sdk) is the reference implementation of the 0.4 Leash() client shape. Python, Go, Ruby, Java, and Rust SDKs are tracking the same shape — aspirational samples shown here; separate parity tickets track each language SDK rebuild.
Where the values come from
Org owners configure a secret source on the dashboard. Options:
- Native — type values directly into Leash. Encrypted with the org's KMS key. Free on every plan.
- External sources (Growth+) — point Leash at your existing Doppler / 1Password / Bitwarden / Infisical / GCP Secret Manager. Leash never stores the secret value, only a token to fetch it.
The call is the same regardless of source. Rotation in your source takes effect on the next call (results are cached for the lifetime of the Leash instance — pass { fresh: true } to bypass).
Failure modes
All SDK errors are LeashError — see Error handling for the full code list. The most common ones for env reads:
NO_API_KEY—LEASH_API_KEYis missing.UNAUTHORIZED— the API key is invalid or revoked.INTEGRATION_ERROR— the external secret source returned an error (rejected token, source down). Org owners are notified via the dashboard.
When a key isn't configured in the secret source, get resolves to null and getMany omits it from the returned record — no exception.
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 mechanism | Rotation behavior |
|---|---|
process.env.X | Bake-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 bearer | Per-call resolve. The token is fetched fresh via getCustomMcpConfig on every call — picks up the new value on the next call. |
Plan gating
The runtime fetch (leash.env.get) is on the Growth plan or above, regardless of which source the value lives in. Dashboard-native values are still free to store and read via process.env — the gate is on the runtime fetch surface, not the source. Calls from a Starter or free org receive HTTP 402 with UPGRADE_REQUIRED.