Github|...

Environment & vault

Encrypted environment variables: set them once, share them with your team, inject them at deploy time.

Sp00ky Cloud provides encrypted environment variable storage that syncs across your team. Each variable supports separate development and production values, so your local dev setup and production deployment stay in sync without .env files.

All values are encrypted at rest using the tenant’s Shared Vault. For details on the encryption model, key hierarchy, and security properties, see the vault documentation.

Quick Start

# 1. Initialize your encryption vault
spky env unlock

# 2. Set a variable (interactive, prompts for value and environment)
spky env set DATABASE_URL

# 3. Load variables and start your app
spky env pull && pnpm dev

Initializing the Vault

Before using environment variables, initialize your encryption vault:

spky env unlock

You’ll be prompted to:

  1. Choose a vault passphrase: this protects your encryption keys, so pick something strong
  2. Confirm the passphrase
  3. Optionally cache the derived key locally in ~/.sp00ky/vault-derived-key (the raw passphrase is never written to disk; only its Argon2id-derived key)

If you skip env unlock, the vault will be created automatically the first time you run env set.

Teams

The first team member to run env unlock creates the tenant vault. Other members receive vault access through the invitation flow. They set their own passphrase when accepting the invitation. See Shared Vault for details.

Setting Variables

# With a name, goes straight to the value prompt
spky env set DATABASE_URL

# Without a name, prompts for the name first
spky env set

The CLI will interactively ask you:

  1. Variable name (if not provided as argument)
  2. Which environment(s): choose from:
    • Both (same value): use one value for dev and prod
    • Development only
    • Production only
    • Both (different values): prompts for each separately
  3. The value: entered with masked input (like a password prompt)

Variable names are automatically uppercased. Running env set on an existing variable updates only the environments you specify. It won’t overwrite the other environment’s value.

Loading Variables

Load environment variables and pipe them into your dev workflow:

# Load development variables (default) and start your app
spky env pull && pnpm dev

# Load production variables
spky env pull --prod && pnpm start

The output format is KEY=value, one per line, designed for shell evaluation:

# Export into the current shell
eval "$(spky env pull)"

# Or use with env
env $(spky env pull | xargs) pnpm dev

The first time you run env pull, you’ll be prompted for your vault passphrase. You can choose to cache it locally so subsequent loads are automatic.

CI/CD Usage

In non-interactive environments (CI/CD), set the SPOOKY_API_KEY environment variable for authentication and cache the derived key in ~/.sp00ky/vault-derived-key during your pipeline setup.

Listing Variables

spky env list

Example output:

NAME                           DEV    PROD   UPDATED
-----------------------------------------------------------------
DATABASE_URL                   yes    yes    2026-04-08
STRIPE_SECRET_KEY              yes    yes    2026-04-08
DEBUG_MODE                     yes    -      2026-04-08

Values are never shown, only names and whether each environment has a value set.

Deleting Variables

spky env rm DATABASE_URL

Importing from a File

Import variables from an .env file:

spky env import .env

Supports standard .env format. One KEY=value per line, comments with #, and quoted values.

Passphrase Management

Changing your passphrase:

spky env passphrase

Only your copy of the encryption key is re-wrapped. Other members and existing variables are unaffected.

Forgot your passphrase? A team admin can approve a reset so you can set a new one:

# Request a reset (admins are notified by email)
spky env reset request

# After admin approves, set your new passphrase
spky env reset complete

See Passphrase Reset for the full flow.

Caching: The CLI can cache the Argon2id-derived key at ~/.sp00ky/vault-derived-key (your raw passphrase is never persisted). To clear the cache:

rm ~/.sp00ky/vault-derived-key

CLI Reference

spky env <COMMAND>

Commands:
  set [NAME]       Set an environment variable
  list             List all environment variable names
  rm <NAME>        Delete an environment variable
  pull [--prod]    Load and output decrypted variables
  import <FILE>    Import variables from an .env file
  unlock           Initialize or unlock the encryption vault
  passphrase       Change your vault passphrase
  share-ci         Enable, disable, or check CI/CD vault access
  reset <COMMAND>  Manage vault passphrase resets

spky env reset <COMMAND>

Commands:
  request          Request a vault passphrase reset
  approve <EMAIL>  Approve a pending reset (admin only)
  complete         Set a new passphrase after approval
  list             List pending resets (admin only)

API Reference

All endpoints require authentication via Bearer token or API key.

Vault Endpoints

Initialize Vault

POST /v1/vault/init
Content-Type: application/json

{
  "passphrase": "your-vault-passphrase"
}

Returns 201 Created on success. Returns 409 Conflict if already initialized.

Vault Status

GET /v1/vault/status
{ "initialized": true }

Change Passphrase

POST /v1/vault/change-passphrase
Content-Type: application/json

{
  "current_passphrase": "old-passphrase",
  "new_passphrase": "new-passphrase"
}

Returns 401 with code invalid_passphrase if the current passphrase is wrong.

Passphrase Reset Endpoints

Request Reset

POST /v1/tenants/{tenantID}/vault-resets

Returns 201 Created. Sends email notification to all admins. Returns 409 Conflict if a request is already pending.

List Reset Requests (Admin)

GET /v1/tenants/{tenantID}/vault-resets
[
  {
    "id": "uuid",
    "account_id": "uuid",
    "email": "alice@example.com",
    "status": "pending",
    "expires_at": "2026-04-18T12:00:00Z",
    "created_at": "2026-04-11T12:00:00Z"
  }
]

Check Own Reset Status

GET /v1/tenants/{tenantID}/vault-resets/mine

Returns the current member’s active reset request, or 404 if none exists.

Approve Reset (Admin)

POST /v1/tenants/{tenantID}/vault-resets/{resetID}/approve
Content-Type: application/json

{
  "passphrase": "admin-vault-passphrase"
}

Returns 401 with code invalid_passphrase if the admin’s passphrase is wrong. Sends email to the requesting member.

Complete Reset

POST /v1/tenants/{tenantID}/vault-resets/complete
Content-Type: application/json

{
  "passphrase": "new-vault-passphrase"
}

Returns 404 if no approved reset exists. Passphrase must be at least 8 characters.

Cancel Reset

DELETE /v1/tenants/{tenantID}/vault-resets/{resetID}

Returns 204 No Content. Can be called by the requesting member or any admin.

Environment Variable Endpoints

All endpoints are scoped to a project: /v1/projects/{projectID}/envs.

Set Variable

PUT /v1/projects/{projectID}/envs/{NAME}
Content-Type: application/json

{
  "dev_value": "localhost:5432",
  "prod_value": "prod-host:5432",
  "both": false
}
  • dev_value and prod_value are optional; provide one or both
  • Set both: true with dev_value to use the same value for both environments
  • Variable names are stored uppercase
  • Returns 400 with code vault_not_initialized if the vault hasn’t been set up

List Variables

GET /v1/projects/{projectID}/envs

Returns variable names and metadata (no decrypted values):

[
  {
    "name": "DATABASE_URL",
    "has_dev": true,
    "has_prod": true,
    "updated_at": "2026-04-08T12:00:00Z"
  }
]

Load Variables (Decrypt)

POST /v1/projects/{projectID}/envs/load
Content-Type: application/json

{
  "passphrase": "your-vault-passphrase",
  "environment": "development"
}
  • environment accepts "development" (default) or "production"
{
  "variables": {
    "DATABASE_URL": "postgres://localhost:5432/myapp",
    "API_KEY": "sk_test_abc123"
  }
}
  • Returns 401 with code invalid_passphrase if the passphrase is wrong
  • Returns 400 with code vault_not_initialized if the vault hasn’t been set up

Delete Variable

DELETE /v1/projects/{projectID}/envs/{NAME}

Returns 204 No Content on success, 404 if the variable doesn’t exist.