The DataMind Installer juggles three kinds of secret, and they are stored in three different places:
.env.On first boot the installer creates a named Docker volume (called delamain_secrets for the
production stack, delamain_secrets_dev for the dev stack) and writes its generated secrets into it
as files. The backend and the migration init job mount that volume at /usr/src/app/secrets; the
Postgres container mounts the same volume at /run/secrets.
| File | Generated by | Value | Used as |
|---|---|---|---|
jwt.secret | Installer backend, on first boot | 64 random bytes, hex-encoded | JWT_SECRET — signs access and refresh tokens |
encr.key | Installer backend, on first boot | 32 random bytes, base64 | NEST_ENCR_KEY — encrypts stored secret values at rest |
curato.token | Installer backend, on first boot | 48 random alphanumeric characters | CURATO_SERVICE_TOKEN — the shared secret the curato service presents to drive deployments |
pg.pass | Postgres container entrypoint, on first boot | 24 random bytes, hex-encoded | POSTGRES_PASS — the database password |
Each file is written with owner-only permissions (0600). On every subsequent start the installer
loads the existing files instead of regenerating them, so secrets are stable across restarts and
updates. If pg.pass is missing, the backend refuses to start — it will not invent a database
password the Postgres container does not agree with.
These four values are not set in .env. The one exception is that the value of
CURATO_SERVICE_TOKEN is additionally written into the deployed stack's environment file, which
is a different file from the installer's own .env. It is written directly from the generated
token rather than from a database row, so no operator edit can drift it away from the value the
installer compares against.
To back up the generated secrets, back up the volume. Losing the volume means losing the JWT signing secret, the encryption key and the database password.
The operator supplies two values, both stored in the installer's database and encrypted with the
NEST_ENCR_KEY key material:
| Secret | How it is entered | How it is exposed |
|---|---|---|
AZURE_CLIENT_SECRET | Advanced configuration → System secrets, or a direct API call to /api/system-secrets/azure | Never returned by any endpoint. The API exposes only whether it is configured and whether the stored value still validates against Azure AD. The UI shows a Configured / Not set badge and an empty password field — the stored value is never echoed back. |
VM_USER | Migration screen (/api/migrate/vm-user) | Returned to administrators as the stored VM user; used to replace <USER> in the deployed stack's environment file. |
Encryption uses AES-256-GCM with a random per-value initialisation vector, serialised with a v2:
prefix. A row is encrypted when it is marked secret or when its source is generated.
The configuration endpoint returns the deployed stack's rows with a secret flag. Its behaviour
depends on the caller's role:
••••••.The Advanced configuration screen is read-only for non-administrators and requires an administrator
account to edit. Rows flagged as secret are shown with a key icon and a password-style field with a
reveal toggle. The AZURE_CLIENT_SECRET field is handled separately and never displays the stored
value, only its configured status and validation result.
The masking above protects the deployed stack's configuration rows. It does not mean secrets are absent from the API: an administrator sees decrypted values. Treat administrator access to the installer as access to every secret it manages.
Two independent paths carry secrets to running containers:
delamain_secrets volume at
/usr/src/app/secrets. They are exported into the backend's process environment at startup
(JWT_SECRET, NEST_ENCR_KEY, CURATO_SERVICE_TOKEN, POSTGRES_PASS) and never written back to
a file.deployment/.env), which is passed to every docker compose invocation with --env-file and is
referenced by the services' env_file entries. The deployed stack and the installer also share an
external Docker network named unistream, which is created once by the operator and owned by
neither compose project.Because the deployed stack's secret rows are stored in the installer's database and rendered into that file on every generation, rotating them means editing the value in the UI and then recreating the affected services. See Rotating values.