@heizen-labs/secrets-sdk
v1.2.2
Published
TypeScript SDK for loading Heizen Project Studio secrets
Readme
@heizen-labs/secrets-sdk
Lightweight SDK to fetch secrets from Heizen Studio and load them into your runtime environment.
Install
npm install @heizen-labs/secrets-sdk
# or
pnpm add @heizen-labs/secrets-sdk
# or
bun add @heizen-labs/secrets-sdkQuick Start
import { SecretsClient } from "@heizen-labs/secrets-sdk";
const client = new SecretsClient({
projectId: process.env.HEIZEN_STUDIO_PROJECT_ID,
environment: process.env.HEIZEN_STUDIO_ENVIRONMENT_NAME,
apiKey: process.env.HEIZEN_STUDIO_API_KEY,
});
await client.loadSecrets();
console.log(process.env.DATABASE_URL);Local development: sign in with a developer token
For local development you don't need an API key. When heizen-secrets run finds no API key,
it signs you in through Heizen Studio in your browser and caches a short-lived developer token:
heizen-secrets run proj_11 development -- pnpm devAuthentication required.
Open this URL to sign in with Heizen Studio:
https://api.studio.heizen.work/oauth/authorize?...
Press Enter to open it in your browser (Ctrl+C to cancel)…Press Enter, sign in (if you are already signed in to Studio, the browser returns immediately), and the command continues with your secrets. Later runs reuse the cached token without a prompt.
- Developer tokens last 24 hours (the server sets the lifetime) and are tied to one API host, not
to a project. One sign-in reads secrets for every project you can access. When a token expires,
runsigns you in again. - Access is checked on every request:
runfails with 403 for a project you are not a member of and 404 for a project that does not exist. - Tokens are cached in
~/.heizen/config(%USERPROFILE%\.heizen\configon Windows) with file mode0600. Override the path withHEIZEN_CONFIG_PATH. - Sign-in only happens in an interactive terminal. In CI (
CI,GITHUB_ACTIONS,GITLAB_CI,BUILDKITE,JENKINS_URL,TF_BUILD), without a TTY, or withHEIZEN_NO_INTERACTIVE=1, the CLI never prompts or opens a browser; it fails with:No Heizen credentials found. Set HEIZEN_STUDIO_API_KEY (CI/CD) or run "heizen-secrets login" in a terminal. --optionalnever triggers sign-in; it uses a cached token if there is one and otherwise skips the fetch.- An API key always takes priority. When one is used in an interactive terminal, the CLI prints a one-line reminder that API keys are intended for CI/CD.
Commands:
heizen-secrets login [--base-url <url>] # sign in and cache a token
heizen-secrets logout [--all] [--base-url <url>] # delete cached token(s)
heizen-secrets status [--base-url <url>] # show which credential would be usedOne sign-in covers every project you can access in Heizen Studio; the CLI caches one developer
token per base URL. Only run needs a project ID. logout only removes the local copy; the token stays valid on the server
until it expires.
CI/CD
Keep using an API key: set HEIZEN_STUDIO_API_KEY (or pass --api-key). Nothing changes for
CI/CD pipelines or the GitHub Action.
CLI: run command with injected secrets
The package also provides a heizen-secrets CLI binary.
heizen-secrets run [<projectId> [environment]] [--api-key <key>] [--base-url <url>] [-e|--env-file <path>] [--override] [--optional] -- <command> [args...]Example:
heizen-secrets run proj_11 development -- nest start --watchWith values from environment variables:
export HEIZEN_STUDIO_PROJECT_ID=proj_11
export HEIZEN_STUDIO_ENVIRONMENT_NAME=development
export HEIZEN_STUDIO_API_KEY=xxx
heizen-secrets run -- nest start --watchWith explicit API key:
heizen-secrets run proj_11 development --api-key xxx -- pnpm devCLI argument resolution
Positional arguments are positional: the first value is always projectId, and the second is always environment.
- To pass the environment from the CLI, you must pass
projectIdfirst. - A single positional is treated as
projectId, notenvironment. --api-keyis independent of positionals. You can pass it withoutprojectIdorenvironmenton the command line.
Examples:
# All values from env / .env files
heizen-secrets run -- nest start --watch
# Project ID from CLI; environment and API key from env
heizen-secrets run proj_123 -- nest start --watch
# API key from CLI; project ID and environment from env
heizen-secrets run --api-key sk_fdsfsdf -- nest start --watch
# Both project ID and environment from CLI; API key from env
heizen-secrets run proj_123 env_1 -- nest start --watchCommon mistake:
# env_1 is treated as projectId, not environment
heizen-secrets run env_1 -- nest start --watchIf you want env_1 as the environment name, pass both positionals:
heizen-secrets run proj_123 env_1 -- nest start --watchOr set both in env and omit positionals:
export HEIZEN_STUDIO_PROJECT_ID=proj_123
export HEIZEN_STUDIO_ENVIRONMENT_NAME=env_1
heizen-secrets run -- nest start --watchWhat you pass on the CLI vs what must come from env:
| You pass on CLI | You need in env / .env |
| --- | --- |
| nothing | HEIZEN_STUDIO_PROJECT_ID, HEIZEN_STUDIO_ENVIRONMENT_NAME, HEIZEN_STUDIO_API_KEY* |
| --api-key only | HEIZEN_STUDIO_PROJECT_ID, HEIZEN_STUDIO_ENVIRONMENT_NAME |
| <projectId> only | HEIZEN_STUDIO_ENVIRONMENT_NAME, HEIZEN_STUDIO_API_KEY |
| <projectId> and <environment> | HEIZEN_STUDIO_API_KEY |
| <projectId>, <environment>, and --api-key | nothing |
* Locally, HEIZEN_STUDIO_API_KEY can be omitted: the CLI signs you in with a developer token instead.
If a required value cannot be resolved, the CLI exits with a clear error:
Missing project ID. Pass <projectId> or set HEIZEN_STUDIO_PROJECT_ID.Missing environment name. Pass <environment> or set HEIZEN_STUDIO_ENVIRONMENT_NAME.No Heizen credentials found. Set HEIZEN_STUDIO_API_KEY (CI/CD) or run "heizen-secrets login" in a terminal.(only when no API key or cached developer token exists and the terminal is non-interactive)
Optional credentials
By default the CLI fails when the project ID, environment, or API key cannot be resolved.
Pass --optional when the .env file may already contain the full environment (for example
in CI or for prisma generate): missing credentials are then ignored, the remote fetch is
skipped, and the command runs with the values loaded from the env file.
heizen-secrets run --optional -- prisma generateBehavior and guarantees:
- Secrets are fetched and injected only into the spawned process environment.
- Secrets are never printed, written to files, or exported by the CLI.
- Parent process env is not mutated.
- Project ID lookup order:
<projectId>->process.env.HEIZEN_STUDIO_PROJECT_ID->.env.local->.env. - Environment lookup order:
<environment>->process.env.HEIZEN_STUDIO_ENVIRONMENT_NAME->.env.local->.env. - Credential lookup order:
--api-key->process.env.HEIZEN_STUDIO_API_KEY->.env.local->.env-> cached developer token -> browser sign-in (interactive terminals only). -e|--env-file <path>reads that file instead of.env.local/.env.--optionalskips the remote fetch instead of failing when those values are missing.
Limitation:
- Your application can still read and print its own environment values if your code chooses to do that.
Environment variables
| Variable | Used for |
| --- | --- |
| HEIZEN_STUDIO_PROJECT_ID | Heizen project ID |
| HEIZEN_STUDIO_ENVIRONMENT_NAME | Environment name (for example, development) |
| HEIZEN_STUDIO_API_KEY | API key sent as x-api-key |
| HEIZEN_CONFIG_PATH | Override the developer token cache path (default ~/.heizen/config) |
| HEIZEN_NO_INTERACTIVE | Set to 1 or true to disable CLI sign-in prompts |
How to get HEIZEN_STUDIO_API_KEY
- Go to
https://studio.heizen.work. - Open your project.
- Go to
Project Settings. - Create a new API key.
- Grant
secrets:readpermission. - Save the key and set it as
HEIZEN_STUDIO_API_KEYin your environment.
App Script Integration
In your app package.json:
{
"scripts": {
"dev": "heizen-secrets run proj_11 development -- nest start --watch"
}
}API Reference
new SecretsClient(options)
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| projectId | string | No* | - | Heizen project ID (example: proj_123). Falls back to process.env.HEIZEN_STUDIO_PROJECT_ID. |
| environment | string | No* | - | Environment name (example: development, production). Falls back to process.env.HEIZEN_STUDIO_ENVIRONMENT_NAME. |
| apiKey | string | No* | - | API key sent as x-api-key. Falls back to process.env.HEIZEN_STUDIO_API_KEY. |
| accessToken | string | No | - | Developer token sent as Authorization: Bearer. Used only when no API key resolves. |
| baseUrl | string | No | https://api.studio.heizen.work | Override API host |
| axiosInstance | AxiosInstance | No | internal instance | Custom axios config/interceptors |
* At least one source must be provided for each field (option value or matching environment variable).
client.loadSecrets(options?): Promise<Record<string, string>>
Fetches secrets and writes them into an env-like target object.
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| override | boolean | false | If true, replaces existing values in target |
| target | Record<string, string \| undefined> | process.env | Target object where secrets are written |
loadSecrets Examples
// Default: writes into process.env, does not overwrite existing keys
await client.loadSecrets();
// Overwrite existing process.env keys
await client.loadSecrets({ override: true });
// Write into a custom object
const envObject: Record<string, string | undefined> = {};
await client.loadSecrets({ target: envObject });Errors
All SDK errors are thrown as SecretsClientError.
import { SecretsClientError } from "@heizen-labs/secrets-sdk";
try {
await client.loadSecrets();
} catch (error) {
if (error instanceof SecretsClientError) {
console.error(error.message, error.status);
}
}