DEV Community

Jordan Huang
Jordan Huang

Posted on

FAQ: Which Process Got the Secret?

Did your last agent session leak a live key?
The chat log said the environment was ready.
I do not trust that sentence anymore today.

Why would I distrust a confident chat log?
Because an agent loop hides several live processes.
The language model is not one of them.

Why this FAQ exists

I keep watching the same failure on free boxes.
A tool call prints a full export command.
The next command still dies on missing auth.

Does that sound familiar from your last session?
The myth is not about model quality.
The myth is about Unix process memory.

Here is the corrected picture for agents.
Environment variables live inside one process only.
A chat transcript is never that process itself.

Start with a probe, not a debate

Do not argue with the model about env.
Ask the parent process, then the child.

Treat the next script as a labeled probe.
I am not citing production metrics for it.

# probe_env.sh — proposed example, not a vendor benchmark
#!/usr/bin/env bash
set -euo pipefail

echo "pid=$$ ppid=$PPID cwd=$(pwd)"
echo "HOME=${HOME-<unset>}"

python3 - <<'PY'
import os, json
keys = [
    "PATH",
    "HOME",
    "APP_TOKEN",
    "CLOUD_KEY",
    "DOTENV_LOADED",
]
out = {k: ("set" if os.environ.get(k) else "missing") for k in keys}
print(json.dumps(out, indent=2))
PY
Enter fullscreen mode Exit fullscreen mode

Run that probe in three different ways.
Use the parent shell first, then a child.
Then source a local .env and retry.

chmod +x probe_env.sh
./probe_env.sh
bash -lc './probe_env.sh'
set -a
# proposed: only source a file you created
[ -f ./.env ] && . ./.env
set +a
./probe_env.sh
Enter fullscreen mode Exit fullscreen mode

What did you expect the child to print?
What did that child process actually print instead?

Did the export in chat actually stick?

The claim is simple, and it is wrong.
People say the agent exported it already.

The evidence is usually one extra spawned process.
Most agent runtimes start a new shell.
Each tool call gets a fresh address space.

Hold this corrected mental model in mind.
An export dies with that shell process.
The next tool call cannot see it.

Try the next command after a "successful" export.

printenv APP_TOKEN || echo "child never saw it"
ps -o pid,ppid,cmd
Enter fullscreen mode Exit fullscreen mode

Did the child print an empty value anyway?
Then the chat lied about process state.

Does a .env file load itself?

Does a file named .env inject keys by itself?
Only if some loader reads those bytes.
A filename never wakes a sleeping process here.

Node dotenv is not your bash session.
Python os.environ is not Docker Compose though.

Hold a stricter mental model for this file.
A .env file is only bytes on disk.
Zero processes care until a loader runs.

Use this decision table before you trust a key.

Mechanism Reads .env alone? Inherits parent env? Writes secrets to disk?
bash export KEY=... no yes, this shell no
set -a; . ./.env only if sourced yes no
Node dotenv/config often yes yes no
docker compose run compose rules filtered no
systemd Environment= no no no

Pin the loader in your own runbook.
Do not trust a filename by itself.

Here is a tiny inert-file check you can run.

# proposed example; use a throwaway directory
printf 'DOTENV_LOADED=1\n' > .env
python3 -c 'import os; print(os.environ.get("DOTENV_LOADED", "missing"))'
Enter fullscreen mode Exit fullscreen mode

Did that small Python one-liner print missing anyway?
The file stayed inert without a loader then.

Is the chat a secret store?

People paste keys into the prompt anyway.
Then they ask the model to keep them private.

Where did those secret bytes actually go though?
They hit the prompt, logs, maybe scratch files.
They may also hit shell history on the box.

Correct that mental model before the next paste.
A secret in a prompt is a logged secret.
Redaction in a UI is not erasure.

I run this probe on a throwaway box.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Free model access and a free server are enough for the probe.
The model can draft probe_env.sh from this article.
The server runs it away from my laptop history.

If you already have a free box, start there.
Still never paste a production root key there.
Use a disposable token with no prod scope.

Who should skip this whole approach for production?
Skip it if you hold customer data or cloud roots.

Do Compose and the agent shell share env?

The agent ran docker compose up for you.
You assumed the container inherited that shell env.

Did the container really inherit your export though?
Compose has its own env file rules.
Those env_file and environment keys filter the map.

Keep this corrected mental model for the boundary.
Containers start with a filtered environment map only.
Your interactive export may never cross it.

Probe inside the service if you already use Compose.
Skip the commands if you have no file.

# proposed; skip if you have no compose file
docker compose run --rm app printenv APP_TOKEN || true
docker compose run --rm app env | sort
printenv APP_TOKEN || echo "host missing"
Enter fullscreen mode Exit fullscreen mode

Is the host set and the container empty?
That mismatch is normal Unix, not a model bug.

Did "redacted" keep the bytes off disk?

The model printed a masked token for you.
You relaxed, then you shipped the branch.

Check three places before you relax though.

  1. Search the repo working tree, including ignored files.
  2. Read recent shell history on the box.
  3. Scan agent tool-call transcripts you still retain.
git status --ignored
git grep -nE 'sk-|AKIA|BEGIN PRIVATE' || true
tail -n 50 ~/.bash_history 2>/dev/null || true
find . -name '.env*' -o -name '*credentials*'
Enter fullscreen mode Exit fullscreen mode

Keep this corrected mental model for redaction features.
Redaction is a renderer trick on output.
Disk and git can still store the raw bytes.

If someone calls the box ephemeral, still grep it.
Ephemeral is a vendor claim, not a measurement.
I do not know any vendor's wipe clock here.

Write a marker and prove the filesystem exists.
Then list it before you trust a wipe story.

echo probe > /tmp/env-probe-marker
ls -l /tmp/env-probe-marker
df -h .
Enter fullscreen mode Exit fullscreen mode

No wipe timing lives in this article.
Verify retention on your own box instead.

A workflow that survives these myths

Here is the loop I actually want.
It is a checklist, not a product demo.

  1. Create a disposable token, never a root key.
  2. Put it in a loader you can name.
  3. Run probe_env.sh in that same process tree.
  4. Run the child command that needs the secret.
  5. Grep the tree for leaked bytes afterward.
  6. Rotate the token when you destroy the box.

Process tree order matters a lot here.
Use the same shell first, then an explicit child.
Then probe the container, if you have one.

# proposed workflow, labeled as unexecuted example
export DOTENV_LOADED=1
./probe_env.sh
bash -lc './probe_env.sh'
set -a
[ -f ./.env ] && . ./.env
set +a
./probe_env.sh
Enter fullscreen mode Exit fullscreen mode

The model can draft the script quickly.
You still own the loader choice yourself.

Limitations, and who should not bother

This FAQ is about Unix env inheritance only.
It is not a vault design document.
It is not a cloud IAM review either.

Windows runners follow a different environment inheritance story.
WSL adds another userland boundary on top.
Some GUI agents wrap a persistent PTY instead.

I am not giving advice for attacking boxes.
I am giving a checklist for boxes you own.

Do not use this workflow when you cannot rotate.
Skip it for compliance audits due today.
Skip it on boxes shared with strangers.

Skip it for tokens that can move money.
Free boxes are useful for probes like this.
They are not a secret manager, period.

What I want you to remember

Ask one question after every "it is set".
Which process holds that map in memory?

If you cannot name the pid, you do not know.
Print the map with printenv, then run the child.

The chat is a story about intent.
printenv is evidence about one real process.

Name the process before you trust the key.
Then rotate the token when the session ends.

Top comments (0)