Files
werkator/docs/prs/2026-10-20-PR#166-build-env-off-argv.md
claude-code 9e6dff6bbc
werkator build succeeded after 02:48
werkator/deploy build succeeded after 00:12
werkator/mutation build succeeded after 27:54
The values of a build's variables no longer stand on the command line of werkdock and docker (v1.43.6) (#166)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-21 21:26:20 +02:00

7.4 KiB

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/<pid>/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

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

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

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

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

  • 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: 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: bwrap starts with an empty environment.