Secret Manager
Secret Manager keeps API keys, database URLs and other secrets end-to-end encrypted. Store them once and fetch them at runtime everywhere, while the platform itself can never read them.
The zero-knowledge modellink
Encryption happens on your side. Every value is encrypted client-side with AES-256-GCM under a per-app Master Encryption Key (MEK); the MEK itself is stored wrapped (encrypted) under your App Secret, which never leaves your machines. The server holds ciphertext + IVs + auth tags, nothing decryptable. Losing the App Secret means the data is unrecoverable by design.
Conceptslink
| Concept | What it is |
|---|---|
| App | A container of secrets (one service or product), identified by appId. |
| Environment | development / staging / production; each variable belongs to one. |
| App Secret | Client-held key that unwraps the MEK. Never sent to the API. |
| MEK | The key your variables are actually encrypted with, fetched wrapped and unwrapped locally. |
| API key | A normal platform key with secret-read scope. It authorizes fetching ciphertext, nothing more. |
Three ways to consume secretslink
- Dashboard: create apps, add variables, share across apps with inheritance links.
- This REST API: your service fetches the wrapped MEK once and the encrypted variables per environment, then decrypts in memory.
- The
nismCLI: wraps the whole fetch + decrypt flow for local dev and CI (guide below).
Using this APIlink
Fetch encrypted secrets at runtime without hardcoding them in your application.
Scope required: full-access or secret-read
All requests use: x-api-key: {{apiKey}}
How it works:
- Store secrets in the merchant dashboard at
/dashboard/org/[orgId]/secret-manager - Your app fetches the encrypted master key (MEK) at startup
- Decrypt the MEK using your App Secret (stored securely server-side)
- Fetch encrypted variables; decrypt them locally with the MEK
Secrets are never returned in plaintext: everything is encrypted in transit and at rest.
App Secret vs API key
- The App Secret is a per-app value shown once in the dashboard. It is used to decrypt the MEK client-side.
- The API key (
x-api-key) is the central key that authorises the fetch. Both are required.
Rate limit: 50 requests/hour for GET master-key
Get encrypted master key
Returns an app's Master Encryption Key (MEK), still encrypted under your App Secret.
Scope: full-access or secret-read
appId in the path is the App ID from the Secret Manager dashboard (set it as {{appId}} in your environment).
Unwrap encryptedMasterKey locally with your App Secret, using masterKeyIV and masterKeyTag (AES-256-GCM). The nismsdk package does this for you with decryptMasterKey. Each call counts toward your Secret Manager request allowance, and 429 means the allowance is used up under a hard limit.
Authorization
x-api-keyAPI key · headerrequiredParameters
appIdpath · stringrequireddefault: Responses
200Encrypted MEK400App secret not set up yet401Invalid or missing API key403Missing permission scope404App not found429Rate limitedcurl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/{appId}/master-key' \
-H 'x-api-key: YOUR_API_KEY'{
"success": true,
"data": {
"appId": "66f1a2b3c4d5e6f7a8b9c0a1",
"appName": "checkout-api",
"encryptedMasterKey": "k3J9vQx0bW1lc3NhZ2UtY2lwaGVydGV4dA==",
"masterKeyIV": "q8Zx4Lm2Tn9Pw1Rs",
"masterKeyTag": "Vb7nC2xQ9mL4kP0sR6tY1w=="
}
}Get encrypted variables
Returns an app's environment variables for one environment, each value still encrypted under the app's MEK.
Scope: full-access or secret-read
appId in the path is the App ID from the Secret Manager dashboard.
Decrypt each value locally with the MEK from Get encrypted master key, using the variable's iv and tag (AES-256-GCM). The nismsdk package does this for you with decryptWithMasterKey. An environment with no variables answers 200 with an empty variables list.
The nism CLI (recommended for local dev and CI)link
The published CLI/SDK nismsdk (Node 18+, binaries nism and nismsdk) wraps the fetch + local-decrypt flow; plaintext never transits the platform.
npm i -g nismsdk # or: npx nismsdk <command>
nism setup # store appId / orgId / apiKey in ~/.nism/config.json
nism verify # fetch, decrypt and confirm values are readable
nism load --environment production --create-env .env
eval "$(nism env --appSecret "$NISM_APP_SECRET" --format shell)" && node server.jsCommands: load (the default when no subcommand is given; flags include --environment, --create-env <file>, --env-set-name (default default) and --system-level), setup, config [--show-secrets], env (prints to stdout for piping; fish shells get set -gx), verify. nism does not wrap or spawn your process: to inject secrets into a command, eval the env output first as shown.
Credentials resolve in order: CLI flags, then NISM_* env vars, then legacy NEW_INSTANCE_* env vars, then ~/.nism/config.json, then an interactive prompt. The App Secret is never written to the config file: supply it per run or via NISM_APP_SECRET. Point at the live platform with NISM_GRAPHQL_URL=https://service.newinstance.cloud/service (the built-in default targets a local dev server, so production runs must set this).
Load flags (defaults): --environment development, --format dotenv|json|shell, --output <file>, --bridge true with --bridge-storage-dir ~/.nism, --shell-integration true (detects bash, zsh, fish, ksh, dash), --auto-add-to-profile, --ide-integration, --create-env [file] (a bare flag means .env), --silent, --debug.
Bridge files in ~/.nism: <environment>.json and ide.env at mode 0600, load-<environment>.sh at 0700, config.json at 0600. --auto-add-to-profile appends a marked source block to the matching shell profile. --create-env writes into the current directory with default permissions, unlike the 0600 bridge files, so gitignore the output.
CI: npx nismsdk load --silent --create-env .env with NISM_* repository secrets; add --no-bridge in Docker builds.
Programmatic use (same package): loadSecrets(), injectSecrets(), NismGraphQLClient, decryptMasterKey, decryptWithMasterKey.
Troubleshooting: an invalid App Secret fails the key check; "all variables failed to decrypt" means the right app but the wrong secret; authentication failures point at the API key or org; a missing inheritance link key surfaces when a linked app was unshared; ECONNREFUSED means the GraphQL URL is wrong or unreachable. Re-run with --debug.
Authorization
x-api-keyAPI key · headerrequiredParameters
environmentquery · stringdefault: Optional. development, staging or production. Defaults to development.
appIdpath · stringrequireddefault: Responses
200Encrypted variables401Invalid or missing API key403Missing permission scope404App not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/{appId}/variables?environment=production' \
-H 'x-api-key: YOUR_API_KEY'{
"success": true,
"data": {
"appId": "66f1a2b3c4d5e6f7a8b9c0a1",
"environment": "production",
"variables": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0b1",
"key": "DATABASE_URL",
"value": "Zm9vYmFyLWNpcGhlcnRleHQtMQ==",
"type": "String",
"description": "Primary Postgres connection string",
"environment": "production",
"isInherited": false,
"sourceAppId": null,
"version": 3,
"iv": "p2Lm8Qx1Zt7Rw4Ns",
"tag": "Hc5vB8nM2xK9qL1sT4wY7g=="
},
{
"id": "66f1a2b3c4d5e6f7a8b9c0b2",
"key": "STRIPE_SECRET_KEY",
"value": "Zm9vYmFyLWNpcGhlcnRleHQtMg==",
"type": "String",
"description": "Stripe live secret key",
"environment": "production",
"isInherited": false,
"sourceAppId": null,
"version": 1,
"iv": "Tn6Wq3Lx9Zp2Rm5K",
"tag": "Ld8sF3gH6jK1mN4pQ7rT0w=="
}
]
}
}