Skip to main content

Approval Flow

The approval flow is the core of env.cat's zero-trust design. Secrets are never sent to the CLI until you explicitly approve the request.

How It Works

Step 1: CLI Requests Secrets

Run the CLI command:

envcat get --bundle dev/api --api-base https://env.cat

What happens:

  1. CLI generates ephemeral keypair (Ed25519)
  2. Sends public key to server via POST /api/v1/requests
  3. Server stores request in Redis (TTL: 5 minutes)
  4. Server returns approval URL

Important: At this stage, NO secrets have been accessed yet.

Step 2: Approval Prompt

The CLI displays:

┌─────────────────────────────────────────┐
│ Approval Required │
│ │
│ Open this URL in your browser: │
│ https://env.cat/approve/abc123xyz │
│ │
│ Or scan this QR code: │
│ ████ ▄▄▄▄▄ █▀█ █▄▀▀▀█▄█ ▄▄▄▄▄ ████ │
│ ████ █ █ █▀▀ █ ▀▀▀ █ █ █ ████ │
│ ████ █▄▄▄█ ▀ █▄▀█▄█▄▀█ █▄▄▄█ ████ │
│ ████▄▄▄▄▄▄▄█ ▀ █ ▀ █ ▀▄▄▄▄▄▄▄████ │
│ │
│ Waiting for approval... │
│ (expires in 4:32) │
└─────────────────────────────────────────┘

Desktop: Click the URL
Mobile: Scan the QR code with your camera

What happens:

  • CLI polls GET /api/v1/requests/:id/wait every 3 seconds
  • Waits up to 5 minutes for approval
  • Shows countdown timer

Step 3: Approve in Browser

Open the approval URL. You'll see:

Bundle Selector:

  • If CLI specified --bundle, it's pre-selected
  • Otherwise, choose from your bundles

Key Selector:

  • All keys in selected bundle shown
  • Values masked (DATABASE_URL: ••••••••)
  • Checkboxes to select which keys to approve
  • Filter/search if bundle has many keys

Approve Button:

  • Click "Approve & Encrypt"
  • Server fetches key values from database
  • Server decrypts values (in memory only)
  • Server encrypts to CLI's public key (NaCl sealed box)
  • Encrypted payload stored in Redis

What happens:

  1. Browser calls POST /api/v1/requests/:id/encrypt-from-bundle
  2. Server loads selected keys from bundle
  3. Server decrypts key values (AES-256-GCM)
  4. Server encrypts to CLI public key (sealed box)
  5. Server stores ciphertext in Redis
  6. Server marks request as "ready"

Security note: Server never logs plaintext values. Decryption happens in memory only.

Step 4: CLI Receives Secrets

CLI polling detects "ready" status:

What happens:

  1. CLI receives ciphertext from server
  2. CLI deletes request (single-use) via server
  3. CLI decrypts with private key (only CLI can decrypt)
  4. CLI parses JSON: {"DATABASE_URL": "postgres://...", ...}
  5. CLI outputs shell exports or writes .env

Output (shell format):

✓ Approved! Received 3 variable(s)

export DATABASE_URL='postgres://user:pass@localhost:5432/db'
export API_KEY='sk_live_abc123xyz'
export PORT='3000'

Step 5: Inject into Shell

Method 1: Eval (Recommended)

eval "$(envcat get --bundle dev/api --api-base https://env.cat)"

Variables now available in current shell:

echo $DATABASE_URL
# postgres://user:pass@localhost:5432/db

echo $API_KEY
# sk_live_abc123xyz

Method 2: Write to .env

envcat get --bundle dev/api --api-base https://env.cat --file .env

Then source it:

set -a && source .env && set +a

Security Model

Zero-Trust Principles

1. No implicit trust

  • Every secret request requires explicit approval
  • No automatic approvals
  • No standing access tokens

2. Principle of least privilege

  • Approve only the keys you need
  • Request specific keys with --keys flag
  • Don't approve "all secrets" by default

3. End-to-end encryption

  • Secrets encrypted in database (at-rest)
  • Encrypted to CLI public key (end-to-end)
  • Only CLI can decrypt (sealed box)

4. Single-use requests

  • Each request deleted after use
  • Cannot replay approval
  • New request for each CLI invocation

Encryption Layers

Layer 1: At-Rest (Database)

  • Algorithm: AES-256-GCM
  • Key: BUNDLE_KMS_KEY environment variable
  • Scope: All key values in database

Layer 2: In-Transit (HTTPS)

  • Protocol: TLS 1.3
  • Certificates: Let's Encrypt (production)
  • Scope: All API requests

Layer 3: End-to-End (Sealed Box)

  • Algorithm: NaCl sealed box (X25519 + XSalsa20-Poly1305)
  • Keypair: Ephemeral (generated per CLI request)
  • Scope: Approval payload (server → CLI)

Threat Model

What env.cat protects against:

✅ Server compromise (secrets encrypted at-rest)
✅ Network eavesdropping (TLS + E2E encryption)
✅ Database leaks (values encrypted, keys rotatable)
✅ Logs exposure (no plaintext in logs)
✅ Accidental commits (CLI-only, no .env in repos)

What env.cat does NOT protect against:

❌ Compromised CLI host (malware can read decrypted secrets from memory)
❌ Shoulder surfing (approval UI shows bundle names)
❌ Social engineering (attacker tricks you into approving)
❌ Post-injection attacks (secrets visible in shell history, env vars)

Full threat model →

Advanced Scenarios

Mobile Approval

Use case: Approve from your phone while working on desktop

How:

  1. CLI on desktop shows QR code
  2. Scan with phone camera
  3. Approve on phone browser
  4. Desktop CLI receives secrets

Benefits:

  • No copy-paste of approval URLs
  • Quick approval via biometric auth (Face ID/Touch ID)
  • Works across devices on same network

Partial Approval

Use case: Bundle has 20 keys, you only need 3

How:

envcat get --bundle prod/api --keys DATABASE_URL,REDIS_URL,API_KEY --api-base https://env.cat

Approval UI shows only requested keys (pre-filtered).

Benefits:

  • Reduce secrets exposure
  • Faster approval (less to review)
  • Clearer intent

Expiration & Timeouts

Request TTL: 5 minutes (300 seconds)

If not approved within 5 minutes:

  • Request deleted from Redis
  • CLI shows: "Request expired after 300 seconds"
  • Must create new request

Polling interval: 3 seconds

CLI checks server every 3 seconds. Server may return:

  • {status: "pending"} - Still waiting
  • {status: "ready", ciphertext: "..."} - Approved!
  • 404 Not Found - Expired or invalid request ID

Rate Limiting

Protection against abuse:

  • Max 10 requests per minute per user
  • Max 100 approval checks per minute
  • Exponential backoff on repeated failures

If rate limited:

Error: Too many requests. Please wait 60 seconds and try again.

Troubleshooting

Request Expired

Issue: CLI shows "Request expired after 300 seconds"

Solution:

  • Run envcat get again (creates new request)
  • Approve faster next time (5-minute window)

Approval URL 404

Issue: Browser shows "Request not found"

Solution:

  • Request may have expired (check CLI)
  • URL may be incomplete (copy full URL)
  • Run envcat get again

Can't Decrypt Payload

Issue: CLI shows "Failed to decrypt payload"

Solution:

  • Server may have used wrong public key (retry request)
  • Ciphertext corrupted in transit (check network)
  • Report as bug (should never happen)

No Variables Received

Issue: CLI shows "Received 0 variable(s)"

Solution:

  • You approved but didn't select any keys (rerun and select keys)
  • Bundle is empty (attach keys to bundle first)
  • Network error during approval (check server logs)

Best Practices

Security

1. Approve only what you need

# Good: Specific keys
envcat get --bundle prod/api --keys DATABASE_URL,API_KEY

# Bad: All keys when you only need one
envcat get --bundle prod/api

2. Review before approving

  • Check bundle name matches your intent
  • Verify environment (dev vs prod)
  • Select only required keys

3. Use ephemeral sessions

# Good: Eval in subshell
(eval "$(envcat get --bundle dev/api)" && npm start)

# Bad: Export to parent shell and leave
eval "$(envcat get --bundle dev/api)"

Workflow

1. Request from trusted device

  • Use CLI on your work laptop, not shared machines
  • Don't approve from public/shared computers

2. Approve quickly

  • Don't let approval URLs sit in browser history
  • Complete flow within 5 minutes

3. Use .env for long-running processes

# Good: Write to .env with restrictive permissions
envcat get --bundle dev/api --file .env
chmod 600 .env
docker-compose up

# Bad: Eval in long-running shell
eval "$(envcat get --bundle dev/api)"
# (secrets remain in shell for hours/days)

Next Steps

Now that you understand the approval flow:

  1. Try It Out - Test the flow in your browser
  2. CLI Reference - Master CLI commands
  3. Security Model - Deep dive into encryption
  4. API Reference - Approval API endpoints

Need Help?