Store datasource credentials
A standalone server can keep datasource credentials itself, encrypted in its database. Use this when there is no ModularIoT to hold them. With ModularIoT, credentials live in ModularIoT’s Credentials screen instead, and this guide does not apply.
How it is organized
| Record | Holds | Who can read it |
|---|---|---|
| Datasource | Name, type, target address, and the name of its credential | Members of the scope |
| Credential | The secret, encrypted with AES-256-GCM | Nobody. The API only returns a summary: the kind and the last characters. |
No dashboard, API answer or log line contains the secret.
1. Turn on the credential store
Generate a key and keep it with your other secrets:
openssl rand -base64 32Start the server with it and a database store:
MIOT_DASHBOARD_STORE=sqlite
MIOT_DASHBOARD_CREDENTIALS_KEY=<the key>The key must have at least 32 characters, and the store must be sqlite or postgres. The server refuses to start otherwise.
If you lose the key, every stored credential becomes unreadable. If you change it, load the credentials again.
2. Store a credential
This is the only request that carries a secret:
curl -X PUT https://dashboards.example.com/tenants/acme/scopes/ops/credentials/fleet-api \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"kind":"BEARER","token":"the-api-token"}'kind | Fields | Sent to the target as |
|---|---|---|
NONE | none | Nothing |
BEARER | token | Authorization: Bearer … |
API_KEY_HEADER | header, value | The named header |
API_KEY_QUERY | param, value | A URL parameter |
BASIC | username, password | Authorization: Basic … |
SERVICE_ACCOUNT | projectId, clientEmail, privateKey | A Google access token, for BigQuery |
A misspelled field is refused with 400 rather than stored empty.
3. Create the datasource
curl -X POST https://dashboards.example.com/tenants/acme/scopes/ops/datasources \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{
"name": "Fleet",
"type": "POSTGREST",
"isActive": true,
"target": "https://data.acme.example/rest/v1",
"credentialRef": "fleet-api"
}'type | target |
|---|---|
POSTGREST | The PostgREST base URL. Must be https and must not contain user:password@. |
BIGQUERY | project.dataset. With a service account, the dataset name alone is enough. |
4. Test it
curl -X POST https://dashboards.example.com/tenants/acme/scopes/ops/datasources/{id}/test \
-H "Authorization: Bearer $TOKEN"{"data":{"testable":true,"success":true,"status":200,"testedAt":"2026-10-05T12:00:00.000Z","message":"The target answered"}}| Result | Means |
|---|---|
success: true | The target answered with the credential |
status: 401 or 403 | The target refused the credential |
status: 404 | The target does not exist, or the credential cannot see it |
No status | The target could not be reached in time |
To test values before saving them, send the same body to .../datasources/test. A redirect counts as a failure, because following it would let the target choose where the credential goes. The answer never includes the target’s response body, which could echo the credential.
Let another system keep the credentials
If a different system already owns credentials, set MIOT_DASHBOARD_CREDENTIALS_URL instead of the key. The server then asks that system which headers to send, and stores nothing. See Configuration. Set the URL or the key, never both.