Secret Manager

Secret Manager is the New Instance end-to-end-encrypted configuration product: store API keys, database URLs and other secrets once, 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

ConceptWhat it is
AppA container of secrets (one service or product), identified by appId.
Environmentdevelopment / staging / production - each variable belongs to one.
App SecretClient-held key that unwraps the MEK. Never sent to the API.
MEKThe key your variables are actually encrypted with - fetched wrapped, unwrapped locally.
API keyA normal platform key with secret-read scope - it authorises fetching ciphertext, nothing more.

Three ways to consume secretslink

  1. Dashboard - create apps, add variables, share across apps with inheritance links.
  2. This REST API - your service fetches the wrapped MEK once and the encrypted variables per environment, then decrypts in memory.
  3. The nism CLI - 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:

  1. Store secrets in the merchant dashboard at /dashboard/org/[orgId]/secret-manager
  2. Your app fetches the encrypted master key (MEK) at startup
  3. Decrypt the MEK using your App Secret (stored securely server-side)
  4. 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/api/v1/secret-manager/apps/master-key

Get encrypted master key

Fetch the encrypted Master Encryption Key (MEK) for an app.

Scope: full-access or secret-read

Path param: appId - the App ID from the secret manager dashboard (set as {{appId}} in your environment)

Success - 200 OK

{
  "encryptedMek": "<base64-encoded-encrypted-key>",
  "algorithm": "AES-256-GCM",
  "kdfAlgorithm": "HKDF-SHA256"
}

Decrypt the MEK using your App Secret:

const mek = await crypto.subtle.decrypt(
  { name: 'AES-GCM', iv: decodeBase64(iv) },
  await deriveKey(appSecret),  // HKDF-SHA256
  decodeBase64(encryptedMek)
);

Common errors

  • 401 - invalid API key
  • 403 - key lacks secret-read scope
  • 404 - app not found
  • 429 - rate limited (50 req/hour)

Headers

x-api-key

Responses

200 – Encrypted MEK

{
  "encryptedMek": "base64encodedEncryptedKeyHere==",
  "algorithm": "AES-256-GCM",
  "kdfAlgorithm": "HKDF-SHA256"
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/secret-manager/apps/master-key

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/master-key'
GET/api/v1/secret-manager/apps/variables

Get encrypted variables

Fetch encrypted environment variables for an app.

Scope: full-access or secret-read

Path param: appId - App ID from dashboard Query param: environment (optional) - development | staging | production (default: development)

Success - 200 OK

{
  "environment": "production",
  "variables": [
    { "key": "DATABASE_URL", "encryptedValue": "<base64>", "iv": "<base64>" },
    { "key": "STRIPE_SECRET_KEY", "encryptedValue": "<base64>", "iv": "<base64>" }
  ]
}

Decrypt each variable using the MEK from GET master-key:

const plaintext = await crypto.subtle.decrypt(
  { name: 'AES-GCM', iv: decodeBase64(variable.iv) },
  mek,
  decodeBase64(variable.encryptedValue)
);

Common errors

  • 401 - invalid API key
  • 403 - key lacks secret-read scope
  • 404 - app not found or no variables for this environment

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.js

Commands: 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.

Headers

x-api-key

Parameters

environmentquerystringdefault: development | staging | production (default: development)

Responses

200 – Encrypted variables

{
  "environment": "production",
  "variables": [
    {
      "key": "DATABASE_URL",
      "encryptedValue": "base64ciphertext==",
      "iv": "base64iv=="
    },
    {
      "key": "STRIPE_SECRET_KEY",
      "encryptedValue": "base64ciphertext2==",
      "iv": "base64iv2=="
    }
  ]
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/secret-manager/apps/variables

Query parameters

environment

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/variables'