Environment variables
Write-only values, encrypted at rest under a key outside the database, scrubbed from build logs, and never written to disk.
Write-only, by contract
You can set a variable and you can delete it. You cannot read one back — not through the portal, not through the API, not as an operator.
GET /applications/:id/variables returns the key, whether it is a build argument, and the
value's length. There is no field in the response contract that could carry a plaintext value,
so no handler can leak one by forgetting to strip it. valueLength exists so the portal can
render "32 characters" and confirm a value is set without revealing it.
Keys
^[A-Za-z_][A-Za-z0-9_]*$Letters, digits, and underscores, starting with a letter or underscore, up to 128 characters.
That is the shape a shell will actually export. Compose interpolates ${NAME} using this
character set, and a key outside it produces a variable your container cannot read — a failure
that looks like the platform dropped the value.
Values are up to 32 KB.
Runtime values and build arguments
| Runtime | Build argument | |
|---|---|---|
| Flag | isBuildTime: false (default) | isBuildTime: true |
| Reaches | The running container's environment | The image build |
| Baked into the image layer | No | Yes |
| Injected as | ${ST_ENV_<KEY>} | ${ST_ARG_<KEY>}, on the exposed service's build |
A build argument is baked into the image layer. Anyone who can pull or inspect the image can read it. Put credentials in runtime values, never in build arguments.
How a value reaches your container
At rest. AES-256-GCM, keyed from SECRET_ENCRYPTION_KEY_FILE (preferred) or
SECRET_ENCRYPTION_KEY. The stored form is v1.<nonce>.<tag>.<ciphertext>, base64url.
Into the rendered compose file. As a reference — ${ST_ENV_DATABASE_URL} — never as the
value.
Into Docker. Through the environment of the docker compose process itself. The plaintext
is never written to any file on the server.
Out of the logs. Values are scrubbed at the single point every build-log line passes through, so a build that echoes its environment does not publish it.
The master key
The key lives outside the database, in a file the installer writes at mode 0600.
openssl rand -hex 32 > /etc/sthora/master.key && chmod 600 /etc/sthora/master.key
# then set SECRET_ENCRYPTION_KEY_FILE=/etc/sthora/master.keyBack it up off the box
Losing the master key makes every stored value unrecoverable — variables, deploy-key private halves, webhook secrets, and managed-service credentials. The database alone will not restore them.
The file form is preferred over SECRET_ENCRYPTION_KEY because an environment variable is
readable through /proc, through docker inspect, and out of any crash dump that captures
the environment.
With neither set, the API still starts and every route that does not touch a secret keeps working. The ones that do say exactly what is missing.
Changing a value
Setting a variable does not restart anything. The new value reaches the container on the next
deployment — or on a restart, which re-runs docker compose up with the current environment.