Secrets & encryption
HeliosLogs protects the control plane at rest and signs sessions with two independent secrets, each stored in its own file. This page explains what they are, where they live, and how to handle them — especially in a cluster.
The two key files
| Secret | Env var | Default path | Protects |
|---|---|---|---|
| Control-plane key | HELIOS_CONTROL_KEY_PATH | ./secret-control.json | Encrypts all control-plane files (users, settings, LLM keys, saved searches, dashboards, monitors, alerts, conversations). |
| JWT signing secret | HELIOS_JWT_SECRET_PATH | ./secret-jwt.json | Signs and validates login session tokens. |
Both are auto-generated on first boot if the file doesn't exist, and loaded on every subsequent boot. They are deliberately separate so they can be rotated on different schedules.
Where they must (not) live
Never put secrets under --data-dir
The data directory is a per-node cache — with a shared store it may be wiped and rebuilt at any time. If your keys live there, they can vanish. Keep them on persistent storage at a stable path (the Docker image defaults them to a separate /app/secret volume).
For a single node, the defaults in the working directory are fine — just back them up.
For a cluster, every node must load the same key material (a shared mount, or identical files distributed by your config management). If the keys differ:
- a node can't decrypt the shared control plane, and
- tokens minted on one node are rejected by another.
Control-plane encryption
The control plane is stored as AES-256-GCM-encrypted JSON envelopes. Each file has a small plaintext header (schema version + nonce) over an encrypted body; no user data sits in plaintext on disk or in replication.
Encryption is on by default. Toggle it with HELIOS_CONTROL_ENCRYPTION:
| Value | Effect |
|---|---|
unset, or 1/true/yes/on | Encryption on (default). |
0/off/false/no | Encryption off — control files are written as readable JSON. |
Even with encryption off, HeliosLogs can still read previously-encrypted files (migrate-on-read), so toggling it is safe. Keep the value consistent across nodes in a cluster.
Key loss = data loss
If you lose secret-control.json (and encryption was on), the encrypted control plane can never be decrypted again — every user, setting, dashboard, and monitor is unrecoverable. The log data itself is separate, but the metadata that makes it usable is gone. Back the control key up like a database.
Losing the JWT secret is far less severe: existing sessions become invalid and everyone simply logs in again. A regenerated JWT secret is why "everyone got logged out after a restart" usually means the secret wasn't persisted or shared.
Rotation
- JWT secret — replace the file and restart. All active sessions are invalidated; users log in again. Low risk.
- Control-plane key — higher stakes, since existing files are encrypted under the current key. Rotate during a maintenance window: run once with encryption off (which rewrites files as readable JSON on next write), then swap in a new key and re-enable. Always keep the old key until you've confirmed everything re-reads cleanly under the new one.
Quick checklist
- [ ] Secrets stored outside
--data-dir, on persistent storage. - [ ] Both files backed up securely.
- [ ] In a cluster: identical key material and the same
HELIOS_CONTROL_ENCRYPTIONon every node. - [ ] Permissions restricted to the HeliosLogs user.