-
Notifications
You must be signed in to change notification settings - Fork 0
Core Concepts
Understanding these core concepts will help you use rsenv effectively and appreciate why it works the way it does.
Development projects face three distinct but related challenges with sensitive configuration:
Different environments (local, test, staging, production) need different values. Traditional .env
files lead to:
- Massive duplication across
local.env,test.env,prod.env - Forgotten updates when shared settings change
Sensitive files (credentials, certificates, API keys) live in your project directory:
- One careless
git add .exposes secrets forever - Developers constantly worry about accidental commits
- No central place to encrypt or manage sensitive files
Development often needs temporary configuration overrides:
- Swap in custom Java Class for testing
- Use local database credentials instead of shared ones
- Risk forgetting to swap back before committing
rsenv introduces the Vault - a project-associated secure storage directory that lives outside your project but is bidirectionally linked to it.
┌─────────────────────────────────────────────────────────────────────────────┐
│ The Vault Concept │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ YOUR PROJECT ~/.rsenv/vaults/myproject-a1b2c3d4 │
│ (git-tracked) (vault - not in git) │
│ │
│ .envrc ────────── symlink ──────────► dot.envrc │
│ │ │ │
│ │ ├── # rsenv section │
│ │ │ export RSENV_VAULT=... │
│ │ │ │
│ │ └── (your original .envrc) │
│ │ │
│ └── RSENV_VAULT points back ───────► [vault root] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
One of the powers of rsenv is the bidirectional link between project and vault:
| Direction | Mechanism | Purpose |
|---|---|---|
| Project → Vault |
.envrc symlink points to dot.envrc
|
Loads vault configuration automatically |
| Vault → Project |
RSENV_VAULT variable in .envrc
|
Scripts and tools can find the vault |
This two-way connection means:
- Your project automatically loads vault configuration
- Your tools always know where the vault is
- No manual path management required
rsenv is designed for minimal interference with your project:
| What Changes | What Doesn't |
|---|---|
.envrc becomes a symlink |
No new files created |
| Guarded files become symlinks | Project structure unchanged |
| No rsenv config files in project | |
| No hidden directories added | |
| gitignore untouched |
The only visible change: .envrc symlink to the vault (you can even add it to your global
gitignore list, since in the vault its name is dot.envrc, your bespoke configuration will be part
of version control of your vault)
rsenv unifies three previously separate tools into one coherent system:
Build DRY environment configurations from simple, inheritable pieces:
# base.env - shared across all environments
export DATABASE_PORT=5432
export LOG_FORMAT=json
# local.env - inherits from base
# rsenv: base.env
export DATABASE_HOST=localhost
export LOG_LEVEL=debug
# prod.env - also inherits from base
# rsenv: base.env
export DATABASE_HOST=prod-db.internal
export LOG_LEVEL=warnKey insight: The # rsenv: base.env comment creates parent-child inheritance. Variables flow
down; children override parents.
Move sensitive files to the vault, leaving symlinks in their place:
project/ vault/guarded/
├── config/ └── config/
│ └── secrets.yaml → symlink ─────────► secrets.yaml (actual file)
└── certs/
└── private.key → symlink ─────────► certs/private.key
Guard a file: move it to vault, create symlink Unguard a file: move it back, remove symlink
The project structure stays identical. Git sees symlinks, not secrets.
Temporarily replace project files with vault versions:
Normal state: After "swap in":
project/application.yml (original) → project/application.yml (from vault)
vault/swap/application.yml (dev) vault/swap/application.yml.original (backup)
Swap in: Replace project file with vault's custom workspace version Swap out: Restore original, save changes to vault
Swap state is tracked per-hostname, preventing conflicts on shared directories.
When you initialize a project, rsenv creates:
~/.rsenv/vaults/{project-name}-{short-id}/
├── dot.envrc # The "real" .envrc (symlinked from project)
├── envs/ # Environment hierarchy files
│ ├── local.env # export RUN_ENV="local"
│ ├── test.env # export RUN_ENV="test"
│ ├── int.env # export RUN_ENV="int"
│ └── prod.env # export RUN_ENV="prod"
├── guarded/ # Sensitive files moved from project
│ └── (mirrors project structure)
└── swap/ # Development override files
└── (mirrors project structure)
Here's a typical workflow:
# 1. Initialize vault for your project
cd ~/myproject
rsenv init vault
# 2. Set up environment hierarchy in vault
# (edit vault/envs/local.env, create additional .env files)
# 3. Guard sensitive files
rsenv guard add config/secrets.yaml
rsenv guard add .credentials/api-key
# 4. Build environment for current context
rsenv env build $RSENV_VAULT/envs/local.env
# 5. Encrypt vault for backup/sharing
rsenv sops encryptEach project has exactly one vault. All sensitive configuration, environment variables, and custom development overrides live there. No scattered files, no confusion.
Multiple layers protect your secrets:
- Vault location: Outside project directory, outside project git
- Symlinks: Git commits harmless symlinks, not secrets
- SOPS encryption: Vault contents can be encrypted at rest
-
gitignore automation: SOPS commands auto-update
.gitignorein vault (only encrypted versions go into vault's Github repo)
Every rsenv operation is reversible:
-
rsenv init vault→rsenv init reset -
rsenv guard add→rsenv guard restore -
rsenv swap in→rsenv swap out
You can always return to your original project state.
- Quick Start - Get productive in 5 minutes
- Environment Variables - Master hierarchical environments
- Vault Management - Guard and manage sensitive files
- File Swapping - Development overrides
rsenv Documentation