Config Environment Files Are Not Shell Scripts
This record defines the startup contract for MINIO_CONFIG_ENV_FILE and explains the compatibility repair committed in SILO as ce456dba0.
Status on 2026-08-28: implementation, focused tests, the complete
cmdandinternalsuites, tagged tests, race tests, vet, lint, generated-file checks, rebrand guards, build, and an independent local Fable Max review are complete. The server commit exists locally; push, remote CI, merge, tag, package, image, deployment, and production verification remain separate gates.
Scope: environment-file parsing and named-target discovery only. No configuration key, subsystem, value, precedence, storage format, or client API changes.
Compatibility rule: the file is a SILO input format. Supporting an optionalexportprefix does not make it a POSIX shell program.
Too Long; Didn’t Read (TL;DR)
SILO can load startup variables from a file:
The parser accepts assignments such as:
The last two names are important. Multi-target configuration appends the target name verbatim after an underscore. The configuration subsystem does not restrict a target to a shell identifier; names containing -, ., :, digits, or printable Unicode can be discovered and resolved exactly.
A hardening change accidentally validated every key as [A-Za-z_][A-Za-z0-9_]*. It made my-hook invalid and stopped the server during restart even though the previous loader and the configuration target model accepted it. The repair validates what SILO actually needs instead:
- the name is non-empty, valid UTF-8, and made of visible non-whitespace characters;
=and NUL are not allowed in a name;- NUL is not allowed in a value;
- invalid input reports file and line without reporting the value;
- the complete file is parsed before any assignment is applied.
Why the regression was real
The environment-file loader calls os.Setenv after parsing. An operating-system environment is a list of strings, not a shell variable namespace. Shell assignment syntax is narrower because the shell must tokenize and expand variable names in its own language.
Named SILO configuration targets are built differently:
For example:
Target discovery lists variables by the fixed parameter prefix and treats the remaining suffix as the target name. Target lookup reconstructs the same name without uppercasing or sanitizing that suffix. Rejecting - in the file parser therefore broke a valid discover-to-resolve path; it did not protect a shell evaluation path because no shell evaluates the file.
The failure is operationally sharp. MINIO_CONFIG_ENV_FILE is loaded only at startup. A server can continue running with an old process environment, then fail on its next restart after the file or binary changes. Startup must fail on malformed input, but it must not invent a narrower target grammar than the configuration system.
The file grammar
Lines and comments
- blank lines are ignored;
- a line whose first non-whitespace character is
#is ignored; - an optional standalone
exportfollowed by whitespace is removed; exportFOO=valueremains the keyexportFOO; it is not mistaken for the prefix;- the first
=separates key and value, so additional=characters remain part of the value.
The file is not a shell. It does not perform variable expansion, command substitution, backslash processing, or inline-comment interpretation.
Keys
Surrounding whitespace around the key is removed. The remaining key must:
- be non-empty valid UTF-8;
- contain only Unicode graphic characters;
- contain no whitespace,
=, NUL, control, or invisible format characters.
This preserves OS-compatible names and multi-target suffixes while rejecting visually empty or structurally ambiguous keys. A key beginning with a digit or punctuation is accepted by the parser; SILO still reads only the exact names used by its configuration and runtime components.
Values and quoting
Unquoted values are trimmed. To retain leading or trailing spaces, quote the complete value with matching single or double quotes:
The parser removes one matching outer quote pair. It does not interpret escapes inside the quoted value. NUL is always rejected because it cannot be represented in an environment entry.
Failure and secrecy contract
Syntax errors stop startup. Diagnostics include the file path, line number, and the invalid key or error class, but never the value. A password on a malformed line must not be copied into logs.
Parsing is all-or-nothing: a syntax error returns no entries, and assignment starts only after the complete file has parsed. If the operating system rejects a validated assignment, SILO also stops startup and identifies the key and file. Since the process exits, it never serves requests with a partially loaded environment.
The file itself remains a privileged secret-bearing input. Operators must protect it with appropriate ownership and mode; parser validation is not a substitute for filesystem permissions.
Regression matrix
The committed tests cover:
- spaces and tabs around
=; - quoted values with significant spaces;
- standalone
export, including Unicode whitespace after it; - keys beginning with
_, a digit, or punctuation; - named targets using
-,.,:, and Unicode; - exact named-target discovery through the configuration subsystem;
- empty keys, whitespace, NUL, and invisible format characters;
- NUL values;
- multiple
=characters in URLs and tokens; - file-and-line diagnostics that redact values;
- all-or-nothing parse results.
The implementation passed the complete local server verification matrix and a read-only adversarial review. Windows-specific os.Setenv behavior has not been exercised on a Windows runner; unsupported platform rejection remains fail-fast rather than silent.
Compatibility and delivery
No configuration migration is required. Existing ordinary environment names behave unchanged. Files using shell-style whitespace become more predictable, and previously accepted named targets work again.
The visible compatibility changes are intentional:
- invalid or invisible names now fail instead of being silently ignored;
- unquoted surrounding value whitespace is trimmed; quote it when significant;
- malformed input stops startup with a redacted location-aware error;
- a valid punctuation-bearing target is no longer rejected merely because a shell could not assign it with
NAME=valuesyntax.
This record describes a source commit, not a delivered release. Until the commit is pushed, tested remotely, merged, tagged, packaged, imaged, and deployed, operators must not assume a public SILO binary contains this parser contract.
Conclusion
Configuration compatibility depends on validating the format SILO actually consumes. MINIO_CONFIG_ENV_FILE borrows a small amount of dotenv-like syntax for operator convenience, but it is not executed by a shell. The repair restores named-target compatibility while retaining strict NUL, invisibility, redaction, and fail-fast guarantees.