# PR#166: The values of a build's variables no longer stand on the command line of werkdock and docker Version: patch > **WARNING:** This document describes only the change applied in this PR. > It may already be outdated once the next PR is merged. > Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation. ## Problem Werkator passes each variable of a sandboxed build as `-e KEY=VALUE` to `werkdock run`, and as `--env KEY=VALUE` to `docker run`. So every secret of a build stands in the argv of that process, e.g. the tokens of the ai-review build of werkrevisor. Each process that can read `/proc//cmdline` sees them, and so does each `ps`, crash report or log of an argv. On mih09 on 2026-10-20 they leaked from `pgrep -af` into a transcript. ## Non-Goals - No change of what a build sees: the same variables with the same values, the same precedence of `werkdock.env`/`docker.env`. - No change of `bwrap`'s command line; werkdock passes the values on as `bwrap --setenv` up to 0.13.1, and werkdock PR#17 ends that. - No rotation of the leaked secrets; that is the human's. ## Specification ### Feature: a build's variables reach the sandbox off the command line #### Background - werkdock takes a bare `-e KEY` from its own environment and omits the variable when that has none (since 0.2.0). - docker reads `--env-file`: one `KEY=VALUE` per line, before the `--env` operands, a later entry winning. #### Scenario#166.01: the werkdock command line names a build's variables, werkdock's environment holds their values … so that no `ps` and no log of an argv shows a secret of a build. - **Given** a build with a token and a whole configuration file in `werkdock.env` - **and** a server with a secret of its own in its environment - **When** Werkator starts the build - **Then** the argv of `werkdock run` names each variable as a bare `-e KEY`, and holds none of the values - **and** werkdock's environment holds the values, and of the server's environment only what werkdock reads itself ##### Verified by - [WerkdockBuildRunnerUnitTest](../../src/test/kotlin/de/javagil/werkator/build/WerkdockBuildRunnerUnitTest.kt) - [RuntimeEnvironmentUnitTest](../../src/test/kotlin/de/javagil/werkator/build/RuntimeEnvironmentUnitTest.kt) #### Scenario#166.02: a variable that steers werkdock itself stays on the command line … so that a build cannot change how werkdock runs outside the sandbox. - **Given** a build that sets `PATH`, `WERKDOCK_HOME` and `LD_PRELOAD` in `werkdock.env` - **When** Werkator starts the build - **Then** the command line holds each as `-e KEY=VALUE`, as before - **and** werkdock runs with the server's values of these three ##### Verified by - [WerkdockBuildRunnerUnitTest](../../src/test/kotlin/de/javagil/werkator/build/WerkdockBuildRunnerUnitTest.kt) - [RuntimeEnvironmentUnitTest](../../src/test/kotlin/de/javagil/werkator/build/RuntimeEnvironmentUnitTest.kt) #### Scenario#166.03: the build still sees the values … so that a build that needs its secrets keeps working. - **Given** a build with a key and an empty value in `werkdock.env` - **When** Werkator starts the build - **Then** the command in the sandbox sees both values - **and** werkdock sees none of the server's own secrets ##### Verified by - [WerkdockBuildRunnerIntegrationTest](../../src/test/kotlin/de/javagil/werkator/build/WerkdockBuildRunnerIntegrationTest.kt) #### Scenario#166.04: a Docker build's values reach the container through a file only the server's account reads … so that neither `ps` nor a program the docker client starts on the host sees them. - **Given** a build with a token in `docker.env` - **When** Werkator starts the build - **Then** the command line of `docker run` holds no value of the build - **and** only the server's account reads the file that holds them, and only until the build ends - **and** a value that docker's file format cannot hold stays on the command line, as before ##### Verified by - [DockerBuildRunnerUnitTest](../../src/test/kotlin/de/javagil/werkator/build/DockerBuildRunnerUnitTest.kt) - [DockerEnvFileUnitTest](../../src/test/kotlin/de/javagil/werkator/build/DockerEnvFileUnitTest.kt) ## Solution - werkdock: `RuntimeEnvironment` splits a build's variables into the `-e` operands and the environment of `werkdock run`, which `WerkdockBuildRunner` starts with exactly that environment, cleared first. It holds the server's values of werkdock's own names, then the build's values; before, werkdock inherited all of the server's environment. - werkdock's own names: what werkdock, the Go runtime or glibc reads, e.g. `PATH`, `HOME`, `WERKDOCK_HOME`, `LD_PRELOAD`, `GCONV_PATH`; the full list is in `docs/configuration.md`. A build variable of such a name keeps `KEY=VALUE` on the command line, as before. In werkdock's environment it would steer werkdock: a branch could choose the `bwrap` it runs, its image store, or a library loaded into a process outside the sandbox. A name with `=` or an empty name stays on the command line, since no environment can hold it. - The environment route is safe for werkdock alone: `werkdock run` starts no host program but itself and `bwrap`, and werkdock PR#18 starts `bwrap` with an empty environment. - Docker: `DockerEnvFile` writes the variables, after the container defaults `HOME` and `GRADLE_USER_HOME`, to a `0600` file in `.git/werkator/docker-env/` (`0700`), which the build container sees masked. The file goes when the client ends; a file older than an hour, left by a killed Werkator, goes with the next Docker build. The docker client keeps Werkator's environment, as before, and gets no build value in it. - Why not the environment for Docker too: the Codex review found that the docker client starts host programs with it, `ssh` for an `ssh://` daemon (`SSH_ASKPASS`) and credential helpers such as `pass` (`BASH_ENV`). A list of forbidden names never covers every helper; the human chose the file (2026-10-20). - docker's file format holds one line per variable, shorter than 64 KiB: a value with a line break, a longer line, or a name docker would misread stays `--env KEY=VALUE` on the command line, after the file, as before. - `SecretFiles.createDirectoriesOwnerOnly` takes a directory that another thread created in the meantime, so two Docker builds that start together both get their file (review of the Codex agent). - Red/green: with `KEY=VALUE` operands back, the tests of Scenario#166.01 fail. - Werkator and werkdock can be rolled out in either order; the leak ends with both. - The native runner already puts the variables into the build's environment, and the follow-up builds run through the same runners. - Version 1.43.6 with a release-notes entry. ## Open Questions - [x] Does anything else of Werkator put a build's value on a command line? No: the image builds, `werkdock images`/`import` and `curl` carry no build variable. ## Additional Changes - `docs/configuration.md`, the architecture skill and `docs/werkator.werkbaum` describe the change. ## Prerequisite PRs - None; werkdock has taken a bare `-e KEY` since 0.2.0. ## Follow-up PRs - [werkdock PR#17](https://git.javagil.de/mi/werkdock/pulls/17): bwrap reads the sandbox's environment from a pipe, not from its command line; an instance's files are readable by the owner only. - [werkdock PR#18](https://git.javagil.de/mi/werkdock/pulls/18): bwrap starts with an empty environment.