| name | stash-secrets |
| description | Manage encrypted secrets with @cipherstash/stack. Covers the Secrets API for storing, retrieving, listing, and deleting end-to-end encrypted secrets, the stash CLI for terminal-based secret management, environment-based isolation, and bulk secret retrieval. Use when implementing secret management, storing API keys or database URLs, or working with the CipherStash Secrets API or CLI. |
CipherStash Stack - Secrets Management
Guide for managing end-to-end encrypted secrets with @cipherstash/stack. Values are encrypted locally before being sent to the CipherStash API - your plaintext never leaves your machine unencrypted.
When to Use This Skill
- Storing sensitive credentials (database URLs, API keys, tokens)
- Retrieving secrets at runtime in application code
- Managing secrets across environments (production, staging, development)
- Using the
stash CLI for terminal-based secret management
- Bulk-retrieving multiple secrets efficiently
Installation
npm install @cipherstash/stack
Configuration
Environment Variables
CS_WORKSPACE_CRN=crn:ap-southeast-2.aws:your-workspace-id
CS_CLIENT_ID=your-client-id
CS_CLIENT_KEY=your-client-key
CS_CLIENT_ACCESS_KEY=your-access-key
Sign up at cipherstash.com/signup to generate credentials.
SDK Usage
Initialize
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!,
accessKey: process.env.CS_CLIENT_ACCESS_KEY!,
environment: "production",
})
const secrets = new Secrets({ environment: "production" })
The environment parameter isolates secrets - each environment gets its own encryption keyset.
Store a Secret
Encrypts the value locally, then sends the ciphertext to the API:
const result = await secrets.set("DATABASE_URL", "postgres://user:pass@host:5432/db")
if (result.failure) {
console.error("Failed:", result.failure.message)
} else {
console.log(result.data.message)
}
Retrieve a Single Secret
Fetches the encrypted value from the API, decrypts locally:
const result = await secrets.get("DATABASE_URL")
if (!result.failure) {
console.log(result.data)
}
Retrieve Multiple Secrets (Efficient)
Fetches all secrets in one API call and decrypts in a single ZeroKMS call:
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)
}
Constraints: 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 Secret Names
Returns metadata only - values remain encrypted on the server:
const result = await secrets.list()
if (!result.failure) {
for (const secret of result.data) {
console.log(secret.name)
}
}
Delete a Secret
const result = await secrets.delete("OLD_API_KEY")
if (result.failure) {
console.error("Failed:", result.failure.message)
}
CLI Usage
stash init adds stash to the project as a dev dependency, so stash <command> runs through whichever package manager the project uses (Bun / pnpm / Yarn / npm). Examples below show the bare stash form. Before init has run, prefix with your package manager's runner — bunx, pnpm dlx, yarn dlx, or npx — whichever matches your project.
Set a Secret
stash secrets set --name DATABASE_URL --value "postgres://..." --environment production
stash secrets set -n DATABASE_URL -V "postgres://..." -e production
Get a Secret
stash secrets get --name DATABASE_URL --environment production
stash secrets get -n DATABASE_URL -e production
Get Many Secrets
stash secrets get-many --name DATABASE_URL,API_KEY --environment production
stash secrets get-many -n DATABASE_URL,API_KEY,JWT_SECRET -e production
List Secrets
stash secrets list --environment production
stash secrets list -e production
Delete a Secret
stash secrets delete --name DATABASE_URL --environment production
stash secrets delete -n DATABASE_URL -e production --yes
CLI Flag Reference
| Flag | Alias | Description |
|---|
--name | -n | Secret name (comma-separated for get-many) |
--value | -V | Secret value (set only) |
--environment | -e | Environment name |
--yes | -y | Skip confirmation (delete only) |
The CLI reads credentials from the same CS_* environment variables. Use a .env file for convenience.
Complete Type Reference
SecretsConfig
interface SecretsConfig {
environment: string
workspaceCRN?: string
clientId?: string
clientKey?: string
accessKey?: string
}
SecretMetadata
interface SecretMetadata {
id: string
name: string
environment: string
createdAt: string
updatedAt: string
}
Error Types
type SecretsErrorType =
| "ApiError"
| "NetworkError"
| "ClientError"
| "EncryptionError"
| "DecryptionError"
interface SecretsError {
type: SecretsErrorType
message: string
}
All operations return Result<T, SecretsError> with either data or failure.
Secrets Class Methods
| Method | Signature | Returns |
|---|
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>> |
Patterns
Loading Secrets at Startup
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!,
accessKey: process.env.CS_CLIENT_ACCESS_KEY!,
environment: process.env.NODE_ENV || "development",
})
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
Environment Isolation
Each environment has its own encryption keyset, providing cryptographic isolation:
const prodSecrets = new Secrets({ ...creds, environment: "production" })
const stagingSecrets = new Secrets({ ...creds, environment: "staging" })
A secret set in one environment cannot be decrypted with credentials from another environment.