First Run
Now that EnvCat is installed, let's create your first bundle and experience the device-approval flow.
Overview
In this guide, you'll:
- Seed an example bundle with sample environment variables
- Build the CLI binary
- Request secrets using device-approval flow
- Inject secrets into your shell
Time to complete: ~10 minutes
Step 1: Seed Example Bundle
EnvCat includes a seed script that creates a bundle named dev/example with edge-case test values.
docker compose exec api python seed.py
Output:
Created bundle: dev/example
Added 8 keys to bundle
✓ Seed complete
What Got Created?
The dev/example bundle contains:
| Key | Value | Purpose |
|---|---|---|
HELLO | world | Simple string |
SPACEY | hello world | Value with spaces |
DOLLAR_VAL | cost is $5 | Dollar sign (not variable) |
QUOTE_MIX | He said "hi" then 'bye' | Mixed quotes |
HASHY | value with # hash char | Hash character (not comment) |
MULTILINE | line1\nline2 | Multi-line value |
URL | postgres://user:pa$$@localhost:5432/db | Database URL with special chars |
UNICODE | κόσμε | Unicode characters |
Why these values? They test the CLI's shell-safe quoting implementation.
Verify Bundle Created
Via API:
curl http://localhost:8888/api/v1/bundles | jq
Expected:
[
{
"id": "...",
"name": "dev/example",
"description": "Example bundle with edge-case values",
"tags": "dev,example",
"created_at": "...",
"updated_at": "..."
}
]
Via Web UI:
open http://localhost:8888/bundles
You should see the dev/example bundle listed.
Step 2: Build the CLI
The CLI is written in Go and needs to be compiled.
cd cli
go build -o envcat ./cmd/constants
Output:
go: downloading github.com/mdp/qrterminal/v3 ...
go: downloading github.com/skip2/go-qrcode ...
First build: Takes 30-60 seconds (downloads dependencies).
Verify CLI built:
./constants --help
Expected:
constants - Local-first environment variable management
Usage:
constants request [flags]
Flags:
--api-base string API base URL (default "http://localhost:8888")
--bundle string Bundle ID or name
--keys strings Specific keys to request
--format string Output format: sh, fish, .env (default "sh")
--write string Write to file instead of stdout
Step 3: Request Secrets (Device-Approval Flow)
Now for the fun part! Experience the device-approval flow.
Terminal: Initiate Request
./constants request --bundle dev/example
What happens:
- CLI generates ephemeral keypair (Ed25519)
- CLI sends request to API with public key
- CLI displays QR code and approval URL
- CLI polls
/api/v1/requests/{id}/wait(every 3 seconds)
You'll see:
🔐 Environment Variable Request
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📱 Scan this QR code or open the URL below:
[ASCII QR CODE]
🔗 http://localhost:8888/approve/abc123xyz
⏳ Waiting for approval...
Keep this terminal open! The CLI is polling for approval.
Browser: Approve Request
- Open the URL in your browser (or scan QR with phone)
- You'll see the approval page at
http://localhost:8888/approve/abc123xyz
Approval UI shows:
- Request ID
- Suggested bundle:
dev/example - All available bundles (dropdown)
- Keys to send (checkboxes)
Steps:
- ✓ Bundle
dev/exampleis pre-selected - ✓ All keys are pre-checked
- Click "✓ Approve 8 key(s)"
What Happens on Approval?
Behind the scenes:
-
Web UI calls
/api/v1/requests/{id}/encrypt-from-bundle- Sends
{bundle_id: "...", include_keys: ["HELLO", "SPACEY", ...]}
- Sends
-
API (Flask):
- Loads bundle items from SQLite
- Decrypts values at-rest (using
BUNDLE_KMS_KEY) - Encrypts to CLI's public key (libsodium sealed box)
- Stores ciphertext in Redis with
status: "ready"
-
CLI receives response:
- Polling returns
{status: "ready", ciphertext_base64: "..."} - CLI decrypts using private key
- CLI parses JSON:
{"HELLO": "world", ...} - CLI outputs shell-safe exports
- Polling returns
Terminal: Receive Secrets
Back in your terminal, you'll see:
✓ Approved! Received 8 variable(s)
export HELLO=world
export SPACEY='hello world'
export DOLLAR_VAL='cost is $5'
export QUOTE_MIX='He said "hi" then '"'"'bye'"'"''
export HASHY='value with # hash char'
export MULTILINE='line1
line2'
export URL='postgres://user:pa$$@localhost:5432/db'
export UNICODE=κόσμε
Notice: The CLI correctly quoted all edge cases!
Step 4: Use the Secrets
Now that you have the exports, let's use them.
Option A: Inject into Current Shell
eval "$(./constants request --bundle dev/example)"
Then verify:
echo $HELLO
# world
echo $SPACEY
# hello world
echo $DOLLAR_VAL
# cost is $5
echo $URL
# postgres://user:pa$$@localhost:5432/db
Why eval? A child process (CLI) cannot modify its parent shell's environment. You must use eval to execute the export statements in the current shell.
Option B: Write to .env File
./constants request --bundle dev/example --write .env
Check file:
cat .env
Expected (.env format):
HELLO=world
SPACEY="hello world"
DOLLAR_VAL="cost is $5"
QUOTE_MIX="He said \"hi\" then 'bye'"
HASHY="value with # hash char"
MULTILINE="line1\nline2"
URL="postgres://user:pa$$@localhost:5432/db"
UNICODE="κόσμε"
File permissions: Automatically set to 0600 (read/write owner only) for security.
Option C: Fish Shell
If you use Fish shell:
./constants request --bundle dev/example --format fish | source
Output format:
set -x HELLO world
set -x SPACEY "hello world"
...
Step 5: Create Your Own Bundle
Now let's create your own bundle for a real project.
Via Web UI
- Open
http://localhost:8888/bundles - Click "+ New Bundle"
- Fill in:
- Name:
dev/myproject - Description:
Development environment for my project - Tags:
dev,myproject
- Name:
- Click "Create"
- Click "Manage" on your new bundle
- Click "+ Add Key":
- Key:
API_KEY - Value:
sk_test_12345 - Is Secret: ✓ (checked)
- Key:
- Repeat for other keys
- Or click "Import" to paste a
.envfile
Via API
# Create bundle
curl -X POST http://localhost:8888/api/v1/bundles \
-H "Content-Type: application/json" \
-d '{
"name": "dev/myproject",
"description": "Development secrets",
"tags": "dev"
}' | jq
# Save bundle ID from response
BUNDLE_ID="..."
# Add keys
curl -X POST "http://localhost:8888/api/v1/bundles/$BUNDLE_ID/items" \
-H "Content-Type: application/json" \
-d '{
"key": "API_KEY",
"value": "sk_test_12345",
"is_secret": true
}'
Import from .env File
Have an existing .env file? Import it:
# Get bundle ID
BUNDLE_ID=$(curl -s http://localhost:8888/api/v1/bundles | jq -r '.[] | select(.name=="dev/myproject") | .id')
# Import .env
curl -X POST "http://localhost:8888/api/v1/bundles/$BUNDLE_ID/import" \
-H "Content-Type: application/json" \
-d "{\"format\":\"env\",\"payload\":\"$(cat .env)\"}"
Common First-Run Issues
CLI Not Found
Error: bash: ./constants: No such file or directory
Solution: Make sure you're in the cli/ directory and built the binary:
cd cli
go build -o envcat ./cmd/constants
./constants --help
Request Expired
Error: CLI shows Error: request expired or not found (404)
Cause: You took longer than 5 minutes to approve.
Solution: Requests have a 5-minute TTL. Just create a new request:
./constants request --bundle dev/example
Bundle Not Found
Error: Bundle 'dev/example' not found
Solution: Seed the example bundle:
docker compose exec api python seed.py
QR Code Garbled
Issue: QR code looks weird or unreadable.
Solution: The ASCII QR requires color terminal support. Just use the URL shown below the QR code.
Variables Not in Shell
Issue: Ran ./constants request but echo $HELLO shows nothing.
Solution: You must use eval:
# Wrong (child process can't modify parent)
./constants request
# Right (eval executes in current shell)
eval "$(./constants request)"
Next Steps
Congratulations! You've successfully:
- ✓ Installed EnvCat
- ✓ Seeded example bundle
- ✓ Built CLI binary
- ✓ Experienced device-approval flow
- ✓ Injected secrets into your shell
- ✓ Created your own bundle
Explore More
- Architecture Overview - Understand how it works under the hood
- Security Model - Learn about encryption and threat model
- CLI Reference - Full CLI command documentation
- API Reference - API endpoint documentation
- Guides - How-to guides and best practices
Use in Your Workflow
# Add to your shell profile (.bashrc, .zshrc, etc.)
alias secrets='eval "$(path/to/constants request)"'
# Then in new terminals:
secrets --bundle dev/myproject
Team Usage
- Share bundles: Use Web UI to manage team bundles
- Per-environment bundles:
dev/myproject,staging/myproject,prod/myproject - Per-client bundles:
client-acme,client-globex(for consultants)
Need help? → Troubleshooting Guide