cuenv
Two commands. Type-safe environments. Secrets that never leak. Tasks that run in parallel.
Status: Alpha - Core evaluation engine complete, CLI and task runner in active development
The Problem
You've been here before:
- Secrets in
.envfiles that accidentally get committed, logged, or shared - "Works on my machine" because environment variables differ between developers
- Build scripts that can't run in parallel so your CI takes forever
- Copy-paste task definitions across projects with no validation
cuenv fixes this with two powerful primitives.
Two Primitives, Infinite Possibilities
cuenv exec -- <command>: Run Anything, Securely
Every command runs with:
- Validated environment - CUE constraints ensure
NODE_ENVis actually"development" | "staging" | "production", not a typo - Secrets resolved at runtime - Pulled from 1Password, AWS, GCP, Vault—never stored in files, never in git history
- Environment-specific overrides - Switch from dev to production with
-e production
env: {
NODE_ENV: "development" | "staging" | "production"
PORT: >0 & <65536 & *3000
// Secrets are resolved at runtime, redacted from logs
DB_PASSWORD: schema.#OnePasswordRef & {
ref: "op://vault/database/password"
}
}
Why this matters: Your production credentials are never on disk. They're fetched when needed, used, and forgotten. cuenv env print shows [SECRET] instead of values. Shell exports exclude secrets entirely.
cuenv task <name>: Orchestrated, Parallel, Cached
Every task runs with:
- Automatic dependency resolution -
buildwaits forlintandtestif configured - Parallel execution - Independent subtasks run simultaneously
- Content-aware caching - Skip tasks when inputs haven't changed
- Same secret + environment benefits as
exec
tasks: {
// Parallel: unit, integration, and lint run at the same time
test: {
unit: { command: "npm", args: ["run", "test:unit"] }
integration: { command: "npm", args: ["run", "test:e2e"] }
lint: { command: "npm", args: ["run", "lint"] }
}
// Sequential: each step waits for the previous
deploy: [
{ command: "docker", args: ["build", "-t", "myapp", "."] }
{ command: "docker", args: ["push", "myapp"] }
{ command: "kubectl", args: ["apply", "-f", "k8s/"] }
]
// Dependencies: build won't start until test completes
build: {
command: "npm"
args: ["run", "build"]
dependsOn: ["test"]
inputs: ["src/**/*", "package.json"]
outputs: ["dist/**/*"]
}
}
Why this matters: Your test suite runs in parallel. Your CI is faster. If nothing changed, cached results are used. And every task inherits your validated environment and resolved secrets.
Quick Start
# Install cuenv
# or: cargo install cuenv
# Create configuration
# Run commands with your secure environment
# List available tasks
Use Cases
Secure Your Secrets
Stop committing .env files. Define secrets with any provider—they're resolved only when needed:
env: {
// 1Password
DB_PASSWORD: schema.#OnePasswordRef & { ref: "op://vault/db/password" }
// AWS Secrets Manager
API_KEY: schema.#AWSSecretRef & { region: "us-west-2", name: "api-key" }
// HashiCorp Vault
STRIPE_KEY: schema.#VaultRef & { path: "secret/stripe", field: "key" }
// Or define your own resolver for any CLI
CUSTOM_SECRET: schema.#ExecResolver & {
command: "my-secret-tool"
args: ["fetch", "my-secret"]
}
}
Secrets are never written to disk, never exported to your shell, and redacted from logs.
Validate Before You Run
Catch configuration errors before they become runtime failures:
env: {
// Constrained to valid values only
NODE_ENV: "development" | "staging" | "production"
LOG_LEVEL: "debug" | "info" | "warn" | "error"
// Must match patterns
DATABASE_URL: string & =~"^postgresql://"
API_ENDPOINT: string & =~"^https://"
// Numeric bounds
PORT: >0 & <65536
// Defaults that can be overridden
TIMEOUT: string | *"30s"
}
If someone sets NODE_ENV: "prod" instead of "production", cuenv tells them immediately.
Run Tasks in Parallel
Object keys run in parallel. Arrays run sequentially. Dependencies are respected automatically:
tasks: {
// These three run at the same time
lint: {
check: { command: "eslint", args: ["src/"] }
types: { command: "tsc", args: ["--noEmit"] }
format: { command: "prettier", args: ["--check", "."] }
}
// These run one after another
deploy: [
{ command: "npm", args: ["run", "build"] }
{ command: "docker", args: ["build", "-t", "app", "."] }
{ command: "docker", args: ["push", "app"] }
{ command: "kubectl", args: ["rollout", "restart", "deployment/app"] }
]
// This waits for lint to complete first
build: {
command: "npm"
args: ["run", "build"]
dependsOn: ["lint"]
}
}
Share Environments Across a Monorepo
CUE configurations compose naturally. Define once, use everywhere:
myproject/
├── env.cue # Global settings
├── shared/
│ └── database.cue # Shared DB config
├── services/
│ ├── api/
│ │ └── env.cue # Inherits global + adds API-specific
│ └── web/
│ └── env.cue # Inherits global + adds web-specific
// services/api/env.cue
import "github.com/myorg/shared/database"
env: database.#Config & {
SERVICE_NAME: "api"
PORT: 8080
}
Automatic Shell Integration
When you cd into a cuenv project, your shell is configured automatically:
# Add to .zshrc / .bashrc
# Now just cd into your project
# → Environment loaded automatically
# → Nix packages available (if configured)
# → Ready to work
CLI Reference
# Execute commands with your validated environment + resolved secrets
# Run named tasks with dependencies, parallelism, caching
# View environment (secrets are redacted)
# Shell integration
# Security approval for configurations
| Option | Description |
|---|---|
--env, -e |
Environment to use (dev, staging, production) |
--cache |
Cache mode (off, read, read-write, write) |
--output-format |
Output format (tui, spinner, simple, tree) |
How It Compares
| Feature | cuenv | Make | Bazel | Taskfile | direnv |
|---|---|---|---|---|---|
| Type Safety | ✅ CUE constraints | ❌ | ✅ BUILD files | ❌ | ❌ |
| Monorepo Support | ✅ Native | ⚠️ Basic | ✅ Excellent | ⚠️ Basic | ⚠️ Per-directory |
| Environment Management | ✅ Typed + Secrets | ❌ | ❌ | ❌ | ✅ Basic |
| Task Dependencies | ✅ Smart | ✅ | ✅ Advanced | ✅ Basic | ❌ |
| Parallel Execution | ✅ | ⚠️ -j flag | ✅ | ⚠️ Limited | ❌ |
| Caching | ✅ Content-aware | ❌ | ✅ Advanced | ❌ | ❌ |
| Security Isolation | 📋 Planned | ❌ | ✅ Sandboxing | ❌ | ❌ |
| Shell Integration | 🚧 | ❌ | ❌ | ❌ | ✅ |
Status
| Component | Status |
|---|---|
| CUE Evaluation Engine | ✅ Complete |
| CLI + Task Runner | 🚧 Development |
| Secret Management | 🚧 Development |
| Shell Integration | 🚧 Development |
| Security Isolation | 📋 Planned |
Contributing
We welcome contributions! cuenv is licensed under AGPL-3.0, ensuring it remains open source.
Development Setup
# Clone the repository
# Enter development environment
# or with direnv: direnv allow
# Project automation (this repo)
Architecture Overview
cuenv/
├── crates/
│ ├── cuengine/ # Core CUE evaluation engine
│ │ ├── src/
│ │ ├── bridge.go # Go FFI bridge
│ │ └── tests/
│ ├── core/ # Shared types and domain logic
│ └── cuenv/ # CLI
├── examples/ # CUE configuration examples
└── docs/ # Documentation
Testing
- Unit tests:
cuenv task test.unit - BDD tests:
cuenv task test.bdd - Coverage:
cuenv task coverage
License
Licensed under the GNU Affero General Public License v3.0.
Why AGPL? We believe in keeping cuenv open source while building a sustainable business. The AGPL ensures that any modifications or hosted services using cuenv remain open source, benefiting the entire community.
Links
- Documentation: cuenv.dev 🚧
- CUE Language: cuelang.org
- Discussion: GitHub Discussions
Built in 🏴 w for the open source community