One typed config for your whole project. Define your environment variables, secrets, tasks, and CI in CUE; cuenv validates them, resolves secrets at runtime, runs your tasks, and generates your pipelines.
Warning
Rapid iteration in progress. I'm actively exploring the right APIs and schema to handle everything cuenv needs to do. Expect breaking changes between releases during this period. If you're using cuenv, be prepared for things to break.
Most projects accumulate a pile of loosely related config. A .env file (or an
.envrc) for variables. A Makefile or justfile for tasks. A hand-written
.github/workflows/*.yml that tries to stay in sync with both. Nothing
validates any of it, secrets end up committed in .env files, and the three
slowly drift apart.
cuenv replaces that pile with a single env.cue. You describe your project once
in CUE — a typed configuration language — and cuenv handles the rest:
- Validates every value against a schema before anything runs.
- Resolves secrets at runtime from your provider, without writing them to disk.
- Runs your tasks with dependency ordering, parallelism, and optional caching.
- Generates CI workflows from the same task definitions.
It's written in Rust with a Go bridge to the CUE evaluator. The project moves fast (see the warning above), but the environment, task, and GitHub CI paths are in daily use.
Everything cuenv offers today, with an honest status. Stable is safe to rely on, Partial works within documented limits, and Preview exists in the schema but isn't usable yet. The schema status page is the authoritative source.
| Capability | Status | Notes |
|---|---|---|
| Typed environment variables | Stable | Enums, bounds, regex, defaults, interpolation, per-environment overrides |
cuenv exec — run a command in the resolved environment |
Stable | |
| Secrets: env vars, custom CLI, 1Password, Infisical | Stable | Resolved at runtime, redacted from output |
| Secrets: AWS, GCP, Vault | Preview | Defined in the schema; runtime resolvers not registered yet |
Tasks: parallel groups, ordered sequences, dependsOn |
Stable | Dependencies are CUE references, checked at evaluation |
| Task parameters & output references | Stable | Pass CLI args; wire one task's stdout into another |
| Content-addressed task caching | Stable | Opt-in via cache.mode with inputs/outputs |
timeout, retry, continueOnError, group maxConcurrency |
Partial | Schema-visible; not fully enforced yet |
| Shell integration & directory hooks | Stable | Auto-load on cd, with an approval gate (cuenv allow) |
CI: GitHub Actions generation (cuenv sync ci) |
Stable | |
CI: run pipelines locally (cuenv ci) |
Stable | |
| CI: contributors (auto-injected setup tasks) | Stable | |
| CI: Buildkite export | Partial | |
| CI: GitLab | Preview | cuenv sync ci rejects it until an emitter exists |
Formatting (cuenv fmt) |
Stable | rust, nix, go, cue |
Code generation (cuenv sync codegen) |
Partial | Generates files; not all file types fully enforced |
Rules → .gitignore, .dockerignore, .editorconfig, CODEOWNERS |
Partial | From .rules.cue via cuenv sync |
Multi-source tools (cuenv tools) |
Stable | Nix, GitHub releases, Rustup, URL, OCI |
Lockfile resolution (cuenv sync lock) |
Stable | Pins OCI image digests into cuenv.lock |
VCS dependencies (cuenv sync vcs) |
Stable | Sparse single-subdirectory checkout |
| Runtimes: Nix, devenv | Stable | Provision tools for tasks and exec |
| Runtimes: OCI binaries, Dagger | Partial | |
| Runtime: container | Preview | |
Container image build (cuenv build) |
Preview | Lists configured images; build backend not implemented |
Long-running services (cuenv up/ps/down/logs/restart) |
Partial | Session state works; task/image dependencies not yet |
Changesets (cuenv changeset) |
Stable | |
Release automation (cuenv release) |
Partial | version/publish/binaries; config-driven parts incomplete |
Here is a small project — a typed environment with a runtime secret, plus a few
tasks. Save it as env.cue:
package cuenv
import "github.com/cuenv/cuenv/schema"
schema.#Project & {
name: "checkout-api"
}
env: {
// Only these three values are accepted; defaults to "development".
NODE_ENV: "development" | "staging" | "production" | *"development"
// Ordinary values, with CUE interpolation.
HOST: "127.0.0.1"
PORT: "8080"
URL: "http://\(HOST):\(PORT)"
// Resolved at runtime from 1Password. Never written to disk or your shell.
DATABASE_PASSWORD: schema.#OnePasswordRef & {
ref: "op://Engineering/checkout-db/password"
}
}
tasks: {
// The three children run in parallel.
check: schema.#TaskGroup & {
type: "group"
lint: schema.#Task & {command: "npm", args: ["run", "lint"]}
types: schema.#Task & {command: "npm", args: ["run", "typecheck"]}
test: schema.#Task & {command: "npm", args: ["test"]}
}
// Waits for `check`; only re-runs when its inputs change.
build: schema.#Task & {
command: "npm"
args: ["run", "build"]
dependsOn: [check]
inputs: ["src/**", "package.json"]
outputs: ["dist/**"]
cache: mode: "read-write"
}
}List what the project defines:
$ cuenv task
Tasks from env.cue:
├─ build [1]
└─ check
├─ check.lint
├─ check.test
└─ check.types
(5 tasks, 0 groups, 0 cached)Print the resolved environment. Secrets are resolved from your provider at print time and shown redacted, never in the clear:
cuenv env printRun a task. check runs its three children in parallel; build waits for them
to pass and is skipped on a cache hit when nothing changed:
cuenv task buildRun any command inside the same validated environment:
cuenv exec -- npm start
cuenv exec -e production -- ./deploy.shA .env file is just strings, so nothing catches NODE_ENV=prodction until
something breaks at runtime. CUE is a typed configuration
language: you describe the shape of valid config, and evaluation fails loudly
when a value doesn't fit.
env: {
NODE_ENV: "development" | "staging" | "production" | *"development" // an enum, default development
PORT: >0 & <65536 | *3000 // a bounded number, default 3000
TIMEOUT: string | *"30s" // any string, default 30s
}Each field declares what counts as valid and what it defaults to. Set
NODE_ENV: "prod" and cuenv refuses to run, pointing at env.NODE_ENV. CUE also
composes: you can import shared definitions and reuse them across services in
a monorepo, instead of copy-pasting .env files. Patterns (=~"^https://") and
many other constraints work the same way.
New to CUE? The CUE site is the best starting point; you only need the basics to be productive with cuenv.
# Nix
nix profile install github:cuenv/cuenv
# Cargo
cargo install cuenvSee Install cuenv for other platforms, shell integration, and verification steps.
cuenv exec -- <command> # run a command in the validated environment (alias: x)
cuenv task [name] # list tasks, or run one (alias: t)
cuenv env print # show the resolved environment (secrets redacted)
cuenv env list # list available environments
cuenv shell init <shell> # print shell integration for bash/zsh/fish
cuenv allow / deny # approve or revoke hook execution for a directory
cuenv sync ci # generate CI workflows from your pipelines
cuenv fmt # format CUE and other configured languagesCommon flags: -e/--env <name> selects an environment, -p/--path <dir> points
at the directory holding your CUE files, and -L/--level <level> sets log
verbosity. Full details live in the CLI reference.
| Area | Where it stands |
|---|---|
| CUE evaluation engine | Solid — fast evaluation through the Go bridge |
Environments & exec |
Solid |
Tasks (task) |
Solid for groups, sequences, deps, params, output refs, caching |
| Secrets | env / exec / 1Password / Infisical / AWS / GCP work; Vault is schema-only |
| Shell integration & hooks | Solid |
| CI generation | GitHub works; Buildkite partial; GitLab schema-only |
| Tools, codegen, rules, release | In progress — see the schema status page |
| Services, container/Dagger | Partial — see the schema status page |
cuenv overlaps with several tools at once. Roughly:
| Capability | cuenv | Make | Taskfile | direnv | dotenv |
|---|---|---|---|---|---|
| Typed/validated config | yes (CUE) | no | no | no | no |
| Environment management | yes, typed | no | no | yes | yes |
| Runtime secrets | yes | no | no | no | no |
| Task dependencies | yes | yes | yes | no | no |
| Parallel execution | yes, by default | -j |
limited | no | no |
| Content-addressed cache | yes, opt-in | no | no | no | no |
| CI generation | yes (GitHub today) | no | no | no | no |
| Shell integration | yes | no | no | yes | no |
The point isn't to win every row — it's that cuenv keeps environments, tasks, and CI in one validated source instead of three that drift.
Contributions are welcome. cuenv is licensed under AGPL-3.0.
git clone https://github.com/cuenv/cuenv
cd cuenv
# Enter the dev environment (or `direnv allow` if you use direnv)
nix develop
# Project automation lives in cuenv itself
cuenv task fmt.check
cuenv task lint
cuenv task test.unit
cuenv task buildSee Develop cuenv and Contribute for the full workflow.
cuenv/
├── crates/
│ ├── cuengine/ # CUE evaluation engine (Go FFI bridge)
│ ├── core/ # Shared types, task execution, caching
│ ├── cuenv/ # CLI and TUI
│ ├── events/ # Event system for UI frontends
│ ├── workspaces/ # Monorepo and package-manager detection
│ ├── ci/ # CI pipeline integration
│ ├── release/ # Version management and publishing
│ ├── codegen/ # CUE-based code generation
│ ├── ignore/ # Ignore-file generation
│ ├── codeowners/ # CODEOWNERS generation
│ ├── github/ # GitHub provider
│ ├── gitlab/ # GitLab provider
│ ├── bitbucket/ # Bitbucket provider
│ └── dagger/ # Dagger task execution backend
├── schema/ # CUE schema definitions
├── examples/ # Runnable CUE configurations
└── docs/ # Documentation site (cuenv.dev)
Licensed under the GNU Affero General Public License v3.0.
Why AGPL? It keeps cuenv open source while leaving room for a sustainable business: modifications and hosted services built on cuenv stay open source too.
- Documentation: cuenv.dev
- CUE language: cuelang.org
- Discussion: GitHub Discussions