Environment Variables
Last updated on October 21, 2026
Environment variables keep values out of your repository. They do not keep them away from the code that runs on a station. This page states who can read what. A Config environment variable feeds a config: key, a Secret feeds a secrets: key.
Whoever can run code on a station can read its secrets
A phase or a plug receives the Secret values it declares, in clear, so it can use them. Code that can use a secret can also print it, write it to a file or send it elsewhere. TofuPilot does not try to stop that.
So the people who can read a station's secrets are the people who can put code on it:
- anyone who can merge to the branch that deploys to it automatically,
- anyone who can push or roll back a build to it,
- anyone with an OS login on the machine.
The boundary is the station, not the procedure
Every procedure on a station runs as the same OS user as the CLI. A procedure can read the files, the environment and the cache of another procedure on the same station. Treat all procedures of one station as one trust domain: a procedure you do not trust belongs on another station.
The station keeps its values in an encrypted cache whose key lives in the OS credential store (Credential Manager on Windows, Keychain on macOS) or, on Linux and without one, in a 0600 file. That protects a copied state file or a backup. It does not protect against code running as the station's user, which can read both.
Secrets follow the deployment environment
A station runs one environment's secrets at a time: those of the build it is meant to run for the procedure, the one last pushed, promoted or rolled back to it.
- A production build receives production secrets. A preview build receives preview secrets only.
- A secret set on the station itself reaches production builds only.
- When the station asks for its values, it names the build it has installed. Secrets go only to a build of the same environment as the build it is meant to run: an older production build still installed because the update failed or is deferred keeps its production secrets. A build of another environment (a preview build waiting to be replaced by a production one) gets the Config values of its own environment and no Secret.
A preview build pushed to a production station therefore never receives that station's production secrets, not even after the station is switched back to production while the preview build is still installed. Keep production and preview credentials separate so this rule means something.
Deploying to a station that holds production secrets is restricted
Station values count as production. Pushing, promoting or rolling back a build to a station that receives production or station secrets, or linking a procedure to it (which pushes the procedure's current production build), requires:
- a user login with the Developer role or above (an API key is refused),
- and, for a member of access groups, a station in one of those groups.
Developers can therefore read production secrets through the code they deploy. The controls are the audit trail of who deployed what, and keeping environments separate. Editing Secrets, station variables or anything that targets production requires Admin, so a developer cannot redirect MES_URL on a station to capture MES_TOKEN.
The automatic production deployment from your repository's production branch is not gated by role: protect that branch in your git host.
The check runs when the build is pushed, not afterwards. A production or station secret added to a station later reaches the build already running there, whoever pushed it. Before adding a secret to a station, check who deployed the build it runs (the station's deployment history).
Access groups and suppliers
A station in an access group receives the same environment variables as any other station: its own, the procedure's and the Shared ones. A supplier running your procedure on their own bench receives the procedure and Shared Secrets of that environment too, so set on their station anything that must differ for them, and keep credentials they must not hold out of the procedure and Shared levels.
What keeps a supplier off your own stations is the access group checks on a member of access groups (below Admin):
- They push, promote or roll back production builds only to their groups' stations (see above).
- They link procedures only to their groups' stations.
- They create stations inside one of their groups only, never unassigned.
- They cannot assign a station outside their groups (an unassigned one included) to a group. Removing a station from its access group, or moving it to another, requires Admin or Owner.
Config is not confidential
A Config value is shown on every run that used it, to everyone who can read runs, suppliers included. Any value you would not paste in a run report is a Secret. The dashboard flags a Config whose key looks like a secret under Needs Attention.
Masking prevents accidents only
Secret values of 8 characters or more are replaced by •••• in everything that leaves the CLI: logs, the terminal UI, the kiosk, --json, the live stream and the run upload. It catches the token printed in a traceback. It does not catch a value that is shorter, encoded, split or sent elsewhere by a phase.
What TofuPilot stores
| Config | Secret | |
|---|---|---|
| In the database | Encrypted (AES-256-GCM) | Encrypted (AES-256-GCM) |
| In the dashboard and the CLI | The value | A short digest only, specific to that row: two rows holding the same value show different digests |
| On each run | The value | The stored value's ID and version, never the value |
| On the station | Encrypted cache | Encrypted cache |
In tofupilot env pull | Development values | Only Secrets that target development |
A Secret never goes back to a browser or the CLI after it is saved. To change one, use Rotate in the dashboard or tofupilot env update.
How is this guide?