Vault Architecture
The Shared Vault is a team-wide encrypted store that protects your environment variables at rest. All team members on a tenant share the same vault, and each member accesses it with their own passphrase. Every Starter and Pro plan includes a shared vault.
For CLI usage and commands, see Environment Variables. This page explains how the vault works internally and how it is secured.
Overview
Every tenant has a single vault containing an RSA-4096 keypair. Environment variables are encrypted with this keypair and stored in Postgres. The private key is itself encrypted, and each team member holds a personal key to unlock it — protected by their own passphrase.
The vault is initialized the first time any team member runs spky cloud env init. Subsequent members receive access through the invitation flow.
Architecture
Key Hierarchy
Passphrase (client-side only)
└─ Argon2id ─▶ Derived key (per-member)
└─ decrypts ─▶ Encryption key (shared)
└─ decrypts ─▶ RSA private key (per-tenant)
Per variable:
Random AES key
├─ encrypts ─▶ Value (AES-256-GCM)
└─ wrapped by ─▶ RSA public key (OAEP)
There are three layers of encryption:
- Passphrase layer (per-member). Each member’s passphrase is run through Argon2id locally on the CLI to derive a 32-byte key. The passphrase never leaves your machine. This derived key encrypts that member’s copy of the shared encryption key.
- Encryption key layer (shared). A single random AES-256 encryption key protects the tenant’s RSA private key. Every member stores their own encrypted copy of this key.
- Tenant keypair layer (per-tenant). An RSA-4096 keypair. The public key encrypts environment variable values via hybrid encryption. The private key (encrypted at rest) decrypts them.
How Variables Are Encrypted
When you set a variable:
- The CLI sends the plaintext value to the server
- The server generates a random AES-256 key, encrypts the value with AES-256-GCM, and encrypts the AES key with the tenant’s RSA public key
- The combined ciphertext (encrypted AES key + encrypted value) is stored in Postgres
There is one encrypted copy per variable per environment (dev/prod) — not one per member.
How Variables Are Decrypted
When you load variables:
- The CLI derives a key from your passphrase locally using Argon2id — the passphrase never leaves your machine
- The CLI sends the derived key to the server
- The server uses the derived key to decrypt your copy of the encryption key, decrypts the tenant’s RSA private key, and decrypts each variable
- The server returns the plaintext values to the CLI
Variable decryption happens on the server, not on the client. However, your passphrase never leaves your machine — only the derived key is sent to the server over TLS. The derived key is used only in memory and is never persisted server-side. The encryption protects data at rest in the database, not in transit between the server and CLI (that’s handled by TLS).
Algorithms
| Layer | Algorithm | Purpose |
|---|---|---|
| Passphrase derivation | Argon2id (time=1, mem=64 MB, threads=4) | Derives a 32-byte key from the member’s passphrase (client-side) |
| Encryption key wrapping | AES-256-GCM | Each member’s derived key encrypts their copy of the encryption key |
| Private key encryption | AES-256-GCM | The encryption key encrypts the tenant’s RSA private key at rest |
| Value encryption | RSA-4096 OAEP (SHA-256) + AES-256-GCM | Hybrid encryption of each environment variable value |
What Is Stored Where
| Data | Location | Protection |
|---|---|---|
| Tenant RSA public key | Server (Postgres) | Plaintext — used to encrypt new values |
| Tenant RSA private key | Server (Postgres) | Encrypted with the encryption key (AES-256-GCM) |
| Encryption key | Server (Postgres) | One copy per member, each encrypted with that member’s passphrase-derived key |
| Member passphrase | Never leaves the client | Never stored on disk; only the derived key is optionally cached at ~/.sp00ky/vault-derived-key |
| Environment variable values | Server (Postgres) | Hybrid encrypted with tenant public key (one copy per variable) |
Sharing Across a Team
When a member sets a variable, it’s encrypted with the tenant’s public key — every member who has the encryption key (i.e., every member of the tenant) can decrypt it. No manual sharing is needed.
Inviting new members: When you run spky cloud team invite, the CLI prompts for your passphrase if the vault is initialized. Your passphrase decrypts the encryption key, which is re-encrypted with a server-side transit key and included in the invitation. When the new member accepts, they set their own passphrase, and the encryption key is re-encrypted for them. No variables need to be re-encrypted.
Changing your passphrase: Only your copy of the encryption key is re-wrapped. Other members and existing variables are unaffected.
Removing a member: Their copy of the encryption key is deleted from tenant_members and any API keys they created are revoked. The tenant’s keypair and encrypted variables remain unchanged.
Security Model
What the encryption protects
- Database breach. An attacker who gains access to Postgres sees only ciphertext. Without a member’s passphrase, they cannot derive the encryption key, and without the encryption key, they cannot decrypt the tenant’s private key or any variable values.
- Unauthorized access. Only authenticated tenant members with a valid passphrase can decrypt variables.
What the encryption does not protect
- Compromised server. Because variable decryption happens server-side, a compromised server process could observe plaintext values during
env loadorenv set. The encryption is at-rest protection, not end-to-end. However, the passphrase itself never reaches the server — only the derived key. - Compromised member machine. If a member caches their derived key at
~/.sp00ky/vault-derived-key, anyone with access to that machine can load variables. The derived key cannot be reversed back to the original passphrase.
Member removal
When a member is removed (spky cloud team remove):
- Their copy of the encryption key is deleted from the server
- Their API keys are revoked
- The tenant keypair and encrypted variables are unaffected
The removed member can no longer decrypt any variables — their path to the tenant’s private key is destroyed. If you suspect a removed member had access to sensitive values, rotate those values with spky cloud env set.
If all team members lose their passphrases, encrypted environment variables cannot be recovered. The passphrase is never stored on the server. Keep at least one passphrase in a secure location (e.g., a password manager).
Passphrase Management
Changing your passphrase
You’ll be prompted for your current passphrase, then a new one with confirmation. Only your copy of the encryption key is re-wrapped — existing variables and other members are unaffected.
Caching
The CLI can cache your derived key locally at ~/.sp00ky/vault-derived-key so you don’t need to enter your passphrase every time. The derived key cannot be reversed back to your passphrase. To clear the cache:
Passphrase Reset
If you forget your vault passphrase, a team admin can approve a reset so you can set a new one.
How it works:
- You request a reset — all admins are notified by email
- An admin approves the reset by entering their passphrase, which proves they have vault access. The encryption key is temporarily held on the server encrypted with a transit key.
- You set a new passphrase — the encryption key is re-encrypted with your new passphrase, and the transit-encrypted copy is deleted.
Admins can list and manage pending requests:
Reset requests expire after 7 days. Only one active request per member is allowed at a time. The admin must provide their own passphrase to approve — only someone with vault access can grant it to others.
Best Practices
- Use strong, unique passphrases. Consider a password manager. Each member’s passphrase should be independent.
- Rotate variables when compromised. If you suspect a value was leaked, run
spky cloud env setwith a new value rather than rotating passphrases. - Limit team size. Only invite members who need access to production secrets.
- Audit regularly. Use
spky cloud team listto review who has access. Remove members who no longer need it. - Keep at least one passphrase safe. If all passphrases are lost, there is no recovery path. Store at least one in a password manager.