ChromVoid Blog
Environment Variables Are Delivery, Not Isolation
Environment variables are a useful delivery interface. The problem begins when .env becomes the durable plaintext source of truth—and every worktree, IDE, backup, and agent inherits the consequences.

A new project often starts with an unremarkable command:
The example file contains names such as DATABASE_URL, API_TOKEN, and
OBJECT_STORAGE_KEY. The real file gets the values. .gitignore is meant to
keep it out of the next commit. The application starts, and the problem appears
solved.
Then the project grows a second worktree. A developer copies the file because the new checkout cannot run without it. An IDE loads the project. A backup tool sees another plaintext file. A container build receives a larger context than expected. An automation agent asks for the missing key so it can run the tests.
The secret did not leak because environment variables are inherently broken. It became difficult to reason about because a convenient delivery format also became the durable source of truth.
That leaves a more useful question than "are environment variables secure?"
Where is the durable source of truth, and who is allowed to turn it into a process environment?
The Short Version
Environment variables are a useful interface between a process and its configuration. They keep deploy-specific values out of application code, work across languages and operating systems, and let the same program run with different configuration.
A .env file is something else. It is a serialization of those values on
disk. For local development that is convenient. For secrets, it creates a
durable plaintext mirror that can be copied, indexed, backed up, included in a
build context, or handed to another tool.
Four boundaries are easy to collapse into one:
- Storage: where the secret lives while nobody is using it.
- Authorization: who may resolve it and for what purpose.
- Delivery: how the selected value reaches a process.
- Runtime: what the process and its descendants can do after delivery.
Moving a value from source code into an environment variable improves the first boundary only if the new source of truth is actually safer. It does not automatically define the other three.
ChromVoid's proposed Vault Env Runtime addresses that local-development gap.
The design keeps secret values in the encrypted vault, maps environment names
to secret references, binds them to exact device-local command presets, and
resolves them only for a freshly approved local process launch. It does not
generate a .env file, and the normal UI and agent path do not receive the
resolved environment map.
The final process still receives plaintext values. Environment variables remain delivery, not isolation.
What Environment Variables Got Right
The Twelve-Factor App made a strong and durable argument for separating config from code. Database handles, external-service credentials, hostnames, and other deploy-specific values should not be constants embedded in the repository.
Environment variables fit that job well. They are granular. They can change between deploys without changing the code. A program can read the same variable whether it was launched by a shell, an IDE, a service manager, a container orchestrator, or another supervised runtime.
That last property is both the strength and the missing piece.
The process does not need to know where API_TOKEN came from. But the
environment interface also does not say who stored the value, how it was
protected, who approved its use, whether it was rotated, or which other process
trees received it.
Non-secret configuration does not always need those answers. Credentials do: they carry reusable authority beyond the process for which they were intended.
The mistake is not using environment variables.
The mistake is treating the delivery interface as the complete secret lifecycle.
How .env Became a Plaintext Source of Truth
.env is a practical developer convention. It gives local tools a familiar
way to populate a process environment without requiring every developer to
configure a shell profile or remember a long launch command.
It is not a secret manager.
.gitignore is useful prevention, but it is not protection for the file
itself. It tells Git which untracked paths should normally stay untracked. It
does not encrypt the file, stop another local program from reading it, prevent
a backup, or erase copies made for other worktrees.
If a secret is committed, removing the file in a later commit is not enough. GitHub's own guidance starts with revoking or rotating the credential, then describes the coordination required to rewrite history and clean other clones and forks. History cleanup is incident response, not a storage policy.
The copies also multiply quietly:
- a second checkout needs its own runnable configuration;
- an IDE or development tool receives access to the project directory;
- a backup or synchronization tool sees a normal plaintext file;
- a build context includes more files than the developer intended.
Docker documents a related boundary explicitly: secrets placed in Dockerfile
ARG or ENV instructions can persist in the final image or its metadata. Its
recommended build-time mechanism is a secret mount, not a durable image layer.
None of this means every .env file immediately leaks. File permissions and
disciplined tooling reduce risk.
The architectural weakness is simpler: a reusable secret now has a durable plaintext representation whose lifecycle is mostly conventional.
And .env files have an impressive talent for growing backup copies.
Delivery Is Not Isolation
Removing the file would not make process environments magically confidential.
When an application starts with API_TOKEN in its environment, that application
receives the plaintext value. Descendant processes may inherit it. The
application can print it, write it to a cache, pass it to another program, send
it over the network, or include it in diagnostics. Debuggers, crash tooling, and
other same-user inspection paths may expose the value depending on the
operating system and execution context.
Rotation has the same boundary. Replacing a token in its source of truth can prevent future resolutions, but it does not rewrite the memory or environment of a process that is already running.
So a precise model looks like this:
source of truth
-> authorization decision
-> delivery mechanism
-> process and descendants
Each arrow needs its own claim.
Encryption at rest protects the first stage. Approval and policy govern the second. Environment injection implements the third. The fourth belongs to the approved process, the operating system, and the user's wider threat model.
Calling all four stages "secret storage" hides the moment control changes hands.
Where ChromVoid Fits: Vault Env Runtime
Architecture preview — July 21, 2026: Vault Env Runtime has an implementation behind a default-off rollout, but ADR-038 and SPEC-241 remain proposed. The required signed macOS canary and security evidence review have not been completed. This section describes the intended boundary, not general product availability.
Vault Env Runtime is designed around one rule:
Resolve selected secrets for one exact approved process tree without creating a durable
.envmirror or returning the values to the requesting agent.
The design separates portable secret state from device execution policy.
An EnvSecret is a dedicated encrypted vault record. An environment profile
maps names such as DATABASE_URL or API_TOKEN to those secret records. The
profile can move with the vault because it describes the logical environment,
not one Mac's filesystem.
A command preset is device-local. It records the canonical workspace, working
directory, absolute executable, exact argv, explicitly allowed parent
environment names, and the executable's SHA-256 identity. Those facts belong
to the machine that will launch the process, so ChromVoid does not pretend they
are portable configuration.
Every run receives a new desktop approval. The review binds the actor, profile revision, preset revision, command, paths, and environment-variable names. It shows which secrets will be used without displaying their values.
Immediately before launch, Tauri revalidates the saved facts and asks the local Core boundary to resolve the selected values. The process is spawned directly, without shell parsing or expansion:
env_clear
-> fixed macOS baseline
-> explicitly allowlisted passthrough
-> selected profile overlay
-> exact executable + argv + cwdThe profile overlay wins when an allowlisted parent variable has the same name. The temporary resolved map is dropped after spawn or failure. The WebView, Gateway responses, logs, and paired CLI do not receive that map on the normal path.
ChromVoid intentionally does not import, export, or generate .env files for
this flow. Generating the file would recreate the durable plaintext boundary
the design exists to remove.
Why Exact Commands Matter More With Agents
An automation agent changes the scale of an old question.
When a human pastes an API key into a chat or agent context, the permission is no longer "run the backend tests." It is "possess this reusable credential and decide what to do with it." The key can be copied into a shell command, saved in a scratch file, included in a tool request, or reused for a different task.
Vault Env Runtime narrows the proposed agent contract.
The product path uses a separately paired local chromvoid CLI. An agent may
discover secret metadata, profiles, and presets; propose profile or preset
changes; wait for the user's decision; and request a run using saved resources.
It cannot create, reveal, update, or delete secret values. It cannot supply an
arbitrary command or working directory at run time. It cannot approve its own
request.
That creates a meaningful difference:
reusable authority: "Here is the API key."
scoped action: "Run this saved command with this saved profile once."The user still has work to do. A changed executable makes the preset stale. Configuration proposals and every run require review. That friction is part of the authority boundary.
It is also not agent containment. An approved command can print a secret, and an attached output consumer can record it. The design prevents the normal agent API from becoming a general secret oracle; it cannot make arbitrary code safe after that code receives the value.
What Env Runtime Does Not Promise
The current v1 proposal is deliberately narrow.
It targets the macOS desktop app with ChromVoid Pro, a local Core, and an open vault. Linux, Windows, mobile, remote Core, cloud runners, shell sessions, PTYs, detached processes, scheduled runs, remembered approvals, and durable run history are outside the v1 contract.
It is not a production secret manager. It does not provide team RBAC, automatic credential rotation, deployment orchestration, or a durable user-facing audit trail. Redacted operational events are not the same thing as a complete audit product.
It does not add a deniability guarantee to command presets or runtime metadata. Secret values inherit the vault's encryption boundary, but Env Runtime has its own device-local state and documented scope.
Most importantly, an approved process receives plaintext environment values, and its descendants may inherit them. They can read, print, persist, or exfiltrate those values. Locking the vault or revoking a paired CLI identity blocks future privileged actions; it cannot erase a value already passed to a running process. A crash can leave an orphan process, and child output can disclose a secret if the child chooses to print it.
Environment injection is a narrower handoff.
It is not a sandbox, a revocation channel, or a claim that secrets never leave the vault.
What You Can Do Today
The proposed runtime is not a reason to wait on basic secret hygiene.
- Keep local secret files out of Git and review staged changes before every push. Exclude them from container build contexts as well.
- If a credential reaches repository history, rotate or revoke it first. Do not treat deletion or history rewriting as revocation.
- Give development credentials the smallest useful scope and lifetime. Do not reuse production authority for a local convenience path.
- Use deployment-native secret management for CI, containers, Kubernetes, and production. Where supported, prefer short-lived workload identity or OIDC over long-lived copied credentials.
- Treat logs, crash reports, debug output, screenshots, and agent context as separate disclosure boundaries.
ChromVoid's current desktop Mounted Vault provides the same distinction for
files on supported platform and runtime combinations. A real .env can remain
encrypted at rest and become visible only during an explicit mounted session.
That is a clearer lifecycle than leaving it permanently decrypted in a project
directory.
It is still a file boundary. While the volume is mounted, operating-system applications with sufficient access can read the plaintext view. Mounted Vault reduces the at-rest footprint; it does not provide the exact-command and agent-without-values contract proposed for Env Runtime.
The Takeaway
Environment variables solved a real problem. They separated deploy-specific configuration from application code and gave processes a standard interface.
.env solved a different problem: convenient local serialization. When it
becomes the durable source of truth for API keys and credentials, convenience
quietly becomes policy.
Vault Env Runtime is ChromVoid's proposed answer for local process launches: keep the secret in the encrypted vault, keep the mapping portable, keep command authority device-local, require a fresh approval, and deliver values only to the selected process tree.
The process can still leak them. The operating system still matters. Production secret management still matters. That is the honest boundary.
Keep environment variables as delivery. Move secret ownership back behind an explicit boundary.
Sources
- The Twelve-Factor App: Config
- GitHub Docs: Removing sensitive data from a repository
- Docker Docs: Secrets used in
ARGorENV - Docker Docs: Build context and
.dockerignore - OWASP Secrets Management Cheat Sheet
- Kubernetes: Good practices for Secrets
- GitHub Actions: OpenID Connect
- ChromVoid: Mounted Vault
ChromVoid Source Documents
This preview is grounded in specs/ADR-038-vault-env-runtime.md,
specs/SPEC-241-vault-env-runtime-v1.md, and
docs/runbooks/vault-env-runtime-macos.md.