A direnv alternative that uses CUE packages for environment configuration.
Development is only supported through the Nix flake:
# Enter the development shell
nix develop
# Build the project
cargo build
# Install directly from the GitHub repository
nix profile install github:rawkode/cuenv
# Or run without installing
nix run github:rawkode/cuenv -- --help
# Using a specific version/commit
nix profile install github:rawkode/cuenv/<commit-sha>
If you're using Home Manager, you can use the included module:
# In your flake.nix
{
inputs = {
cuenv.url = "github:rawkode/cuenv";
# ... other inputs
};
outputs = { self, nixpkgs, home-manager, cuenv, ... }: {
homeConfigurations.yourUsername = home-manager.lib.homeManagerConfiguration {
# ... your configuration
modules = [
cuenv.homeManagerModules.default
{
programs.cuenv = {
enable = true;
# Optional: specify package
# package = cuenv.packages.${pkgs.system}.default;
# Shell integrations (auto-detected based on enabled shells)
# enableBashIntegration = true;
# enableZshIntegration = true;
# enableFishIntegration = true;
# enableNushellIntegration = true; # Experimental
};
}
];
};
};
}
The module will automatically:
- Install the cuenv package
- Set up shell integration for enabled shells (bash, zsh, fish, nushell)
- Configure the shell hooks to load CUE environments automatically
# Install from crates.io
cargo install cuenv
# Clone the repository
git clone https://github.com/rawkode/cuenv
cd cuenv
# Build with Nix
nix build
# The binary will be available in ./result/bin/cuenv
Add the following to your shell configuration:
eval "$(cuenv init bash)"
eval "$(cuenv init zsh)"
cuenv init fish | source
- Create a CUE package in your project directory with
.cue
files:
package env
import "github.com/rawkode/cuenv"
env: cuenv.#Env & {
DATABASE_URL: "postgres://localhost/mydb"
API_KEY: "secret123"
DEBUG: "true"
PORT: "3000"
}
- Navigate to the directory and the environment will be automatically loaded.
cuenv
- Load CUE package from current directorycuenv load [directory]
- Manually load environment from a directorycuenv unload
- Unload the current environmentcuenv status
- Show environment changescuenv hook <shell>
- Generate shell-specific hook outputcuenv init <shell>
- Generate shell initialization scriptcuenv run <command> [args...]
- Run a command in a hermetic environment with only CUE-defined variables
- Automatic environment loading when entering directories
- CUE package-based environment loading
- Shell variable expansion support
- Support for multiple shells (bash, zsh, fish)
- Type-safe configuration with CUE
- Secret resolution from 1Password and GCP Secrets Manager (with
cuenv run
) - Automatic secret obfuscation in stdout/stderr to prevent accidental exposure
- Environment-specific configurations with inheritance
- Capability-based variable filtering for secure credential management
- Command inference for automatic capability detection
- Environment variable configuration (CUENV_ENV, CUENV_CAPABILITIES)
Your CUE package should use the cuenv package schema:
package env
import "github.com/rawkode/cuenv"
env: cuenv.#Env & {
// String values
DATABASE_URL: "postgres://user:pass@host/db"
// String representations of numbers
PORT: "3000"
TIMEOUT: "30"
// String representations of booleans
DEBUG: "true"
ENABLE_CACHE: "false"
// Shell expansion is supported
LOG_PATH: "$HOME/logs/myapp"
// CUE features are supported
BASE_URL: "https://api.example.com"
API_ENDPOINT: "\(BASE_URL)/v1" // String interpolation
HOST: "localhost"
DATABASE_DSN: "postgres://\(HOST):5432/myapp" // Computed values
}
- When you cd into a directory, cuenv checks for CUE packages (directories with
.cue
files) - It loads the CUE package from the current directory
- Environment variables are set in your shell
- When you leave the directory, the environment is restored
The run
command executes programs with only the environment variables defined in your CUE files (plus PATH and HOME for basic functionality):
# Run a command with CUE-defined environment
cuenv run node server.js
# Pass arguments to the command
cuenv run npm -- install --save-dev
# Run shell commands
cuenv run bash -- -c "echo PORT=\$PORT"
# The environment is hermetic - parent environment variables are not passed through
export PARENT_VAR=123
cuenv run bash -- -c 'echo "PARENT_VAR=$PARENT_VAR"' # Will print: PARENT_VAR=
You can configure disk and network access restrictions for tasks using the security
section in your CUE task definitions. This uses Landlock (Linux Security Module) for enforcement:
tasks: {
"secure-build": {
description: "Build the project with restricted filesystem access"
command: "echo 'Building project securely...' && sleep 1 && echo 'Build complete!'"
security: {
restrictDisk: true
readOnlyPaths: ["/usr", "/lib", "/bin"]
readWritePaths: ["/tmp", "./build"]
}
}
"network-task": {
description: "Task that needs network access but with restrictions"
command: "echo 'Downloading dependencies...' && curl --version"
security: {
restrictNetwork: true
allowedHosts: ["api.example.com", "registry.npmjs.org"]
}
}
"fully-restricted": {
description: "Task with both disk and network restrictions"
command: "echo 'Running in secure sandbox'"
security: {
restrictDisk: true
restrictNetwork: true
readOnlyPaths: ["/usr/bin", "/bin"]
readWritePaths: ["/tmp"]
allowedHosts: ["localhost"]
}
}
"unrestricted": {
description: "Task without security restrictions"
command: "echo 'Running without restrictions' && ls -la /"
}
}
Running tasks with security restrictions:
# Run a task with disk restrictions
cuenv run secure-build
# Run a task with network restrictions
cuenv run network-task
# Run a fully restricted task
cuenv run fully-restricted
Landlock Requirements:
- Linux kernel 5.13+ (for filesystem restrictions)
- Linux kernel 5.19+ (for network restrictions - basic support)
- Appropriate permissions to use Landlock LSM
Security Configuration Options:
restrictDisk
: Enable filesystem access restrictionsrestrictNetwork
: Enable network access restrictionsreadOnlyPaths
: Array of paths allowed for readingreadWritePaths
: Array of paths allowed for reading and writingdenyPaths
: Array of paths explicitly denied (overrides allow lists)allowedHosts
: Array of network hosts/CIDRs allowed for connections
Security Model: When disk restrictions are enabled, you must explicitly allow all paths your task needs access to. This includes:
- Executable paths (
/bin
,/usr/bin
) - Library paths (
/lib
,/usr/lib
,/lib64
,/usr/lib64
) - Configuration paths (
/etc
if needed) - Working directories and output paths
Note: Network restrictions are currently limited by Landlock V2 capabilities. The implementation will be enhanced in future versions to provide more granular network access control.
Note: Network and process restrictions are not yet fully implemented with Landlock. Use system-level controls or container runtimes for those restrictions.
When using cuenv run
, secret references in your CUE files are automatically resolved:
package env
import "github.com/rawkode/cuenv"
env: cuenv.#Env & {
// Regular environment variables
DATABASE_HOST: "localhost"
DATABASE_USER: "myapp"
// Secret references - 1Password format
DATABASE_PASSWORD: cuenv.#OnePasswordRef & {ref: "op://Personal/database/password"}
API_KEY: cuenv.#OnePasswordRef & {ref: "op://Work/myapp-api-key/field"}
// Secret references - Various providers
GITHUB_TOKEN: "github://myorg/myrepo/GITHUB_TOKEN"
AWS_SECRET: "aws-secret://prod/api/secret"
GCP_SECRET: "gcp-secret://myproject/db-password"
AZURE_KEY: "azure-keyvault://myvault/keys/mykey"
VAULT_TOKEN: "vault://secret/data/myapp/token"
// You can compose URLs with resolved secrets
DB_HOST: "prod.example.com"
DATABASE_URL: "postgres://\(DATABASE_USER):\(DATABASE_PASSWORD)@\(DB_HOST):5432/myapp"
}
Requirements:
- For 1Password: Install 1Password CLI and authenticate with
op signin
- For GCP Secrets: Install gcloud CLI and authenticate with
gcloud auth login
Note: Secret resolution only happens with cuenv run
. Regular cuenv load
will not resolve secrets for security reasons.
When using cuenv run
, any resolved secret values are automatically obfuscated in the command's stdout and stderr output. This prevents accidental exposure of sensitive information in logs or terminal output.
# Example: If DATABASE_PASSWORD resolves to "secret123"
cuenv run sh -c 'echo "Password is: $DATABASE_PASSWORD"'
# Output: Password is: ***********
# Secrets are obfuscated even in error messages
cuenv run sh -c 'echo "Error: Failed to connect with $API_KEY" >&2'
# Stderr: Error: Failed to connect with ***********
This obfuscation applies to all resolved secrets from 1Password and GCP Secrets Manager, helping maintain security when running commands with sensitive data.
cuenv supports environment-specific configurations and capability-based filtering:
package env
import "github.com/rawkode/cuenv"
env: cuenv.#Env & {
// Base configuration
DATABASE_URL: "postgresql://localhost:5432/myapp"
LOG_LEVEL: "info"
PORT: "3000"
// AWS capabilities - tagged with @capability
AWS_REGION: "us-east-1" @capability("aws")
AWS_ACCESS_KEY: "aws-access-key" @capability("aws")
AWS_SECRET_KEY: "aws-secret-key" @capability("aws")
// Docker capabilities
DOCKER_REGISTRY: "docker.io" @capability("docker")
DOCKER_IMAGE: "myapp:latest" @capability("docker")
// Environment-specific overrides
environment: {
production: {
DATABASE_URL: "postgresql://prod-db:5432/myapp"
LOG_LEVEL: "warn"
PORT: "8080"
AWS_REGION: "us-west-2" @capability("aws")
}
staging: {
DATABASE_URL: "postgresql://staging-db:5432/myapp"
LOG_LEVEL: "debug"
}
}
// Capability mappings for automatic inference
capabilities: {
aws: {
commands: ["terraform", "aws", "deploy"]
}
cloudflare: {
commands: ["terraform"]
}
docker: {
commands: ["deploy"]
}
}
}
Usage:
# Use production environment
cuenv run -e production -- ./app
# Enable specific capabilities
cuenv run -c aws -- aws s3 ls
# Use environment variables
CUENV_ENV=production CUENV_CAPABILITIES=aws cuenv run -- terraform apply
# Automatic capability inference from command
cuenv run -e production -- aws s3 ls # Automatically enables 'aws' capability
- Quickstart Guide - Get started quickly with cuenv
- Commands Reference - Complete command reference
- Secret Management - Secret management and security guide
- CUE Format Guide - Type-safe configuration with CUE
- Environments - Environment-specific configurations
- Capabilities - Capability-based variable filtering
- Shell Integration - Setting up shell hooks
- Configuration - Configuration options reference
- Environment Variables - Using environment variables for configuration
- Uses CUE instead of shell scripts for configuration
- Type-safe configuration files
- No need for
direnv allow
(can be added if needed) - Simpler mental model - just key-value pairs in CUE format