Unit 02.00: Keys in the environment, never in the file
Keys go in the environment, and nothing that gets logged should ever contain one.
Read once, never printed
Configuration from the environment, with a fake model as the default, and the key kept out of the config object.
The code shows the split.
import os
key = os.environ.get("APP_API_KEY")
print(f"key present: {key is not None}")
print(f"key value logged: never")
config = {
"model": os.environ.get("APP_MODEL", "fake-model-for-tests"),
"max_output_tokens": int(os.environ.get("APP_MAX_TOKENS", "512")),
"prompt_version": "reply-v3",
}
print(f"\nsafe to log: {config}")
for practice, verdict in [
("key in a .env file that is gitignored", "acceptable for local dev"),
("key in the source file", "never"),
("key in a config dict you also log", "never -- it will reach the logs"),
("key in the environment, read once", "correct"),
]:
print(f" {practice:44} {verdict}")
# The default model is a fake. A misconfigured environment then fails in tests
# rather than silently calling a paid API with production settings.
The key is read into its own variable and never enters the config dict. That matters because config dicts get logged - for debugging, in error reports, in support tickets - and a secret in one reaches all three.
The fake-model default means a misconfigured environment fails in tests rather than silently calling a paid API with production settings.
The mistake this prevents
The mistake is putting the key in the same object as everything else because it is convenient. It works, and six months later someone adds a debug log of the config and the key is in your log store.
Takeaway
Read secrets into their own variable, never into an object you might log, and default the model to a fake so misconfiguration fails safely.
