CipherStash Docs

SDK reference

Programmatic API for storing, retrieving, and managing encrypted secrets

Secrets SDK reference

Initialize

secrets.ts
import { Secrets } from "@cipherstash/stack/secrets"

const secrets = new Secrets({
  workspaceCRN: process.env.CS_WORKSPACE_CRN!,
  clientId: process.env.CS_CLIENT_ID!,
  clientKey: process.env.CS_CLIENT_KEY!,
  apiKey: process.env.CS_CLIENT_ACCESS_KEY!,
  environment: "production",
})

The environment parameter isolates secrets — each environment gets its own encryption keyset, providing cryptographic isolation.

Store a secret

Encrypts the value locally, then sends the ciphertext to the API:

secrets.ts
const result = await secrets.set("DATABASE_URL", "postgres://user:pass@host:5432/db")

if (result.failure) {
  console.error("Failed:", result.failure.message)
  // result.failure.type: "ApiError" | "NetworkError" | "ClientError" | "EncryptionError"
} else {
  console.log(result.data.message) // success message
}

Retrieve a single secret

Fetches the encrypted value from the API, decrypts locally:

secrets.ts
const result = await secrets.get("DATABASE_URL")

if (!result.failure) {
  console.log(result.data) // "postgres://user:pass@host:5432/db"
}

Retrieve multiple secrets

Fetch and decrypt multiple secrets in a single call:

secrets.ts
const result = await secrets.getMany(["DATABASE_URL", "API_KEY", "JWT_SECRET"])

if (!result.failure) {
  console.log(result.data.DATABASE_URL)
  console.log(result.data.API_KEY)
  console.log(result.data.JWT_SECRET)
}

getMany requires a minimum of 2 secret names and a maximum of 100 names per request.

Use getMany over multiple get calls — it's significantly more efficient because it batches the decryption into a single ZeroKMS operation.

List secrets

List all secret names in the current environment. Values stay encrypted — only metadata is returned:

secrets.ts
const result = await secrets.list()

if (!result.failure) {
  for (const secret of result.data) {
    console.log(secret.name)
    // Also available: secret.createdAt, secret.updatedAt, secret.environment
  }
}

Delete a secret

secrets.ts
const result = await secrets.delete("OLD_API_KEY")

if (result.failure) {
  console.error("Failed:", result.failure.message)
}

API summary

MethodSignatureReturns
set(name: string, value: string)Promise<Result<{ success: true, message: string }, SecretsError>>
get(name: string)Promise<Result<string, SecretsError>>
getMany(names: string[]) (min 2, max 100)Promise<Result<Record<string, string>, SecretsError>>
list()Promise<Result<SecretMetadata[], SecretsError>>
delete(name: string)Promise<Result<{ success: true, message: string }, SecretsError>>

All operations return Result<T, SecretsError> with either data or failure.

Type reference

SecretsConfig

interface SecretsConfig {
  workspaceCRN: string    // Cloud Resource Name
  clientId: string        // Client identifier
  clientKey: string       // Client key material
  apiKey: string          // API access key (CS_CLIENT_ACCESS_KEY)
  environment: string     // Environment name
  accessKey?: string      // Optional additional access key
}

SecretMetadata

interface SecretMetadata {
  id: string
  name: string
  environment: string
  createdAt: string
  updatedAt: string
}

Error types

type SecretsErrorType =
  | "ApiError"         // HTTP/API failures
  | "NetworkError"     // Network connectivity issues
  | "ClientError"      // Client initialization failures
  | "EncryptionError"  // Encryption operation failed
  | "DecryptionError"  // Decryption operation failed

Patterns

Loading secrets at startup

config.ts
import { Secrets } from "@cipherstash/stack/secrets"

const secrets = new Secrets({
  workspaceCRN: process.env.CS_WORKSPACE_CRN!,
  clientId: process.env.CS_CLIENT_ID!,
  clientKey: process.env.CS_CLIENT_KEY!,
  apiKey: process.env.CS_CLIENT_ACCESS_KEY!,
  environment: process.env.NODE_ENV || "development",
})

// Load all needed secrets in one efficient call
const result = await secrets.getMany(["DATABASE_URL", "STRIPE_KEY", "SENDGRID_KEY"])
if (result.failure) {
  throw new Error(`Failed to load secrets: ${result.failure.message}`)
}

const config = result.data
// Use config.DATABASE_URL, config.STRIPE_KEY, etc.

Environment isolation

Each environment has its own encryption keyset, providing cryptographic isolation:

secrets.ts
// Production secrets
const prodSecrets = new Secrets({ ...creds, environment: "production" })

// Staging secrets (completely isolated keys)
const stagingSecrets = new Secrets({ ...creds, environment: "staging" })

A secret set in one environment cannot be decrypted with credentials from another environment.

How it works

  1. The SDK encrypts secrets locally using @cipherstash/stack before any network call.
  2. Encrypted values are sent to the CipherStash API over HTTPS.
  3. CipherStash stores them in your workspace's isolated vault.
  4. On retrieval, the encrypted value is fetched and decrypted locally by the SDK.

CipherStash never has access to your plaintext secrets. Each workspace has its own isolated vault, and each environment uses a separate encryption keyset managed by ZeroKMS.

On this page