Appearance
Secrets
Last updated
A secret is a credential stored once, encrypted, and referenced from configuration as ${secret:NAME} instead of being pasted in. Rotating a password becomes one edit here.
Open Secrets in the admin section of the sidebar. Admin only.
Create or replace a secret
- Click New secret.
- Name: uppercase letters, numbers and underscores, starting with a letter, up to 64 characters (e.g.
SMTP_PASS). Lowercase is converted as you type. If the name exists, the form title changes to Replace "NAME" and saving replaces the value. - Description: what it is for (optional).
- Value: the credential. Stored encrypted. It cannot be read back afterwards — only replaced.
- Click Save.
The list shows Name, Reference (click to copy ${secret:NAME}), and Last used — when something last resolved it and what (for example smtp), or never used.
Where references work
A reference is expanded only by code that knows to expand it. Today that is:
| Field | Used by | Expanded |
|---|---|---|
| Password on the SMTP Email card in Settings | Alarm pipeline email, scheduled report email, workflow email, and everything else sent through the SMTP path | Each time mail is sent |
| DNS provider credential on the ACME card in Certificates | Certificate renewal | Each renewal |
Any credential in a device's driver settings: a password, a token, an API key, or the password inside a URL such as rtsp://admin:${secret:CAM_PW}@camera/stream | OPC-UA, MQTT and HTTP devices, scales and scanners, and vision cameras | Each time the device connects |
| Password, connection string and connection options of a remote database | The connection pool, the historian's SQL backend, named queries, data tables, and Test Connection | Each time the pool opens, and on every test |
| Bearer token, basic-auth password, API key, any header value, and the URL of an outbound REST call | Outbound REST | Each call |
| Password of a Sparkplug connection | The MQTT broker login | Each time it connects |
Because the value is looked up at the moment it is used, rotating a credential is one edit here. Nothing has to be saved again: replace the secret, and a device picks the new value up the next time it connects, a database on the next pool it opens.
Anywhere else a reference is not expanded and would be used literally — script source, a report, an expression, and the OPC-UA server's own password, which clients authenticate against rather than something the appliance sends anywhere.
How a reference reads back
A stored credential reads back as ••••••••, for every role including viewers, and saving those dots back unchanged keeps what is stored. A reference is a name rather than a credential, so it reads back exactly as you typed it, and the field shows it instead of dots. That is how you can tell, on a device or a database connection somebody else set up, that its password comes from here. Saving a reference back unchanged keeps it too.
A reference to a secret that does not exist fails where the credential is used, and the message names the secret: Secret "DB_PWD" is not in the Secrets store. Create it under Secrets, or correct the reference. You will see it as the device's connection error, as the result under Test Connection, or as the last status of an outbound call — and nothing is sent to the far end. The SMTP path is the one exception: it leaves the reference in place, so the mail server receives the literal text ${secret:SMTP_PASS} and rejects the login.
Example: SMTP password
- Secrets → New secret → Name
SMTP_PASS, Value the mailbox password → Save. - Settings → SMTP Email → Password →
${secret:SMTP_PASS}→ Save Settings. - When IT rotates the password, replace the secret. The next alarm email uses the new value.
What the encryption protects
- Values are encrypted with AES-256-GCM. The key is derived from the appliance's JWT secret (
JWT_SECRET, or the generated fileapps/api/data/.jwt-secret). - No API returns a value — not to this page, not to exports, not to admins. The audit log records create, replace and delete, never the value.
- This protects against a stolen SD card or a copied database file. It does not protect against someone with root on a running appliance, who can read the key the same way the application does.
- Backups contain secrets in encrypted form. Restoring a backup onto a different appliance (a different JWT secret) leaves every secret undecryptable; the log reports could not be decrypted (was JWT_SECRET rotated?). Re-enter the values after such a restore.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/secrets | Names, descriptions and last-used metadata |
| POST | /api/secrets | { "name", "value", "description" } — creates or replaces |
| DELETE | /api/secrets/:id | Delete |
Tip: Check Last used before deleting. A secret resolved this morning is still wired into something.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Uppercase letters, numbers and underscores; must start with a letter. | Invalid name | Rename, e.g. MAIL_PASSWORD_2 |
Mail server rejects login; its log shows ${secret:…} as the password | The name in the reference does not match any secret | Fix the reference or create the secret |
| Secret "NAME" is not in the Secrets store on a device, a database connection or an outbound call | The name in the reference matches no secret — check spelling and case | Fix the reference, or create the secret |
| Secret shows never used although it is referenced | It is referenced from a field that does not expand secrets, or nothing has used it yet — a device reads it when it connects, not when you save it | Check the table above, then reconnect whatever uses it |
| All secrets stopped working after a restore or reinstall | Different JWT secret | Re-enter the values |