Configuration
Environment variables (backend)
Section titled “Environment variables (backend)”| Variable | Default | Purpose |
|---|---|---|
JWT_SECRET |
insecure dev default | Signing key for auth tokens. Must be set in production; the install script generates one. |
HSI_SECRETS_KEY |
none | 64 hex characters. Encrypts notification connector secrets (webhook URLs and headers, SMTP passwords). Generated by the install script and never rotated automatically: losing it means re-entering those secrets. Keep it with your configuration backups. |
NATS_URL |
- | NATS server URL for talking to the root-worker. |
NATS_USER / NATS_PASS |
- | NATS credentials. |
BACKEND_PORT |
9001 |
TCP port the backend listens on. The installer passes it to systemd. HTTPS is served on this port. |
INSTALL_DIR |
- | Installation root; anchors runtime paths (e.g. the bundled server.js). |
DASHBOARD_PATH |
- | Path to the built dashboard the backend serves. In dev, point it at apps/dashboard/dist. |
WALLPAPER_DIR |
under INSTALL_DIR |
Where uploaded desktop wallpapers are stored. |
HSI_TLS_DIR |
/var/lib/hsi/tls |
Directory of the HTTPS files (see TLS files). |
NODE_ENV |
- | Standard Node environment flag. |
HSI_TELEMETRY_ENABLED |
true |
Set to false, 0, off or no to disable anonymous daily telemetry. Takes precedence over the switch in Settings > Telemetry. |
HSI_TELEMETRY_CHANNEL |
detected | Release channel reported with each heartbeat: stable, beta or dev. Detected by default: dev outside production (NODE_ENV not production, or a checkout without a version), beta for pre-release versions such as -rc.1, stable otherwise. The telemetry service does not record dev heartbeats. |
HSI_TELEMETRY_URL |
https://hsi-telemetry.theo-labs.dev/v1/heartbeat |
HTTPS heartbeat endpoint used by the best-effort telemetry client. |
HSI_VERSION |
package version | Optional explicit version reported by packaged deployments. |
If
JWT_SECRETis unset the backend logs a warning and uses an insecure default; never do this in production.
Telemetry sends only the anonymous hardware and version fields documented by
the telemetry service. Failures time out after three seconds and never block
startup or normal HSI features. The random installation UUID and last-success
timestamp are stored under INSTALL_DIR/data and survive upgrades.
Install / update options
Section titled “Install / update options”The installer (scripts/install.sh, run via the README one-liner) accepts these
environment variables:
| Variable | Default | Purpose |
|---|---|---|
VERSION |
latest release | Install/pin a specific tag, e.g. VERSION=v1.28.1. |
INSTALL_DIR |
/opt/hsi |
Installation directory. |
APP_USER |
hsi |
System user the backend runs as. |
BACKEND_PORT |
9001 |
API port. |
NATS_SERVER_VERSION |
v2.10.24 |
NATS binary version to download. |
SKIP_NGINX |
0 |
Skip the nginx site of older installations that still have one. |
SKIP_SEED |
0 |
Skip the database seed and the setup link. |
HSI_LOG_LEVEL |
info |
Root worker log level (debug, info, warn, error), kept in /etc/hsi/worker.env across updates. |
SKIP_DEPS_INSTALL |
0 |
Only check host packages; fail instead of installing missing ones with apt-get. |
HSI_ALLOW_UNSIGNED |
0 |
Same as --allow-unsigned: install a release published before signing, with no signature check. |
Re-running the installer detects an existing installation, preserves the database and all secrets, and restarts the services.
TLS files
Section titled “TLS files”HTTPS files live in /var/lib/hsi/tls/ (override with HSI_TLS_DIR). See
Secure access.
| File | Mode | Content |
|---|---|---|
ca.key |
0600 root | Private key of the local authority |
ca.crt |
0644 | Local authority certificate (public) |
server.crt |
0644 | Server certificate (local or imported) |
server.key |
0640 root:hsi | Server private key |
tls.json |
0644 | Mode, port, standard ports, trusted proxies, names the certificate was issued for |
Host packages
Section titled “Host packages”The installer checks these on every install and update. Missing required packages are
installed with apt-get (set SKIP_DEPS_INSTALL=1 to only check them).
| Package | Needed for |
|---|---|
curl, openssl |
Downloading releases, generating secrets |
rsync, openssh-client |
Backup jobs |
util-linux (runuser, lsblk, blkid) |
Running steps as the app user, disk inventory |
mdadm |
RAID arrays |
smartmontools |
S.M.A.R.T. health |
lvm2 |
LVM volumes |
parted, e2fsprogs, udev |
Partitioning and formatting |
Optional packages enable a feature and are only reported when missing:
| Package | Feature |
|---|---|
docker.io (or Docker CE) |
Apps and the App Store |
samba |
SMB sharing |
curl and root access are needed before the installer can run: use
curl … | sudo bash, or curl … | bash from a root shell when sudo is not installed.
systemd services
Section titled “systemd services”| Unit | Role |
|---|---|
hsi.target |
The whole stack; the only unit enabled at boot |
hsi-nats |
NATS JetStream message broker |
hsi-worker |
Privileged filesystem/disk worker (runs as root) |
hsi-server |
Backend API + static dashboard server |
systemctl restart hsi.targetsystemctl status hsi-server hsi-worker hsi-natssystemctl stop|start|restart hsi.target acts on the three services; each
can still be managed on its own. An active target does not mean every
component is healthy: check the services individually. The scheduled disk
checks (hsi-maintenance.timer) and the update units (hsi-update-check,
hsi-update-apply) are not part of the target.
The backend and the root worker write JSON lines to files, rotated by
/etc/logrotate.d/hsi; NATS logs to the journal.
tail -f /var/log/hsi/app.log # backend (HTTP requests, job outcomes)tail -f /var/log/hsi/root-worker.log # root worker (jobs, failed requests, commands)journalctl -u hsi-nats -fBoth files use one JSON format, one line per event:
| Field | Meaning |
|---|---|
time |
ISO 8601 timestamp |
level |
debug, info, warn, error or fatal |
component |
backend or worker |
msg |
What happened |
err |
Error details (message, stack) when there is one |
Other fields depend on the event (jobId, subject, reqId, username…).
To read them comfortably:
tail -f /var/log/hsi/root-worker.log | jq -r '"\(.time) \(.level) \(.msg) \(.error // .err.message // "")"'Output that bypasses the loggers (a Node.js warning, or a Go runtime crash dump) can still appear as plain text.
Every async operation has a jobId that appears in both files. The worker logs
each job it receives and its outcome, and every synchronous request that fails.
Set HSI_LOG_LEVEL=debug in /etc/hsi/worker.env and restart
hsi-worker to also log successful requests and every external command
the worker runs (mdadm, parted, smartctl…) with its exit code, duration
and trimmed output. Command failures are logged at debug as well: many probes
fail routinely, and a failure that matters already shows up as a failed job or
request. Passwords are only ever passed on stdin, which is never logged.
Administrators can open the log lines of a failed operation from its error
notification (View logs). HSI reads them from the end of app.log and
root-worker.log in HSI_LOG_DIR (default /var/log/hsi); lines already
rotated away are not shown.
Notifications
Section titled “Notifications”Settings > Notifications routes events to connectors (the in-app bell, a webhook such as Discord, Slack or ntfy, or email over SMTP). Rules match on the event source prefix and a minimum severity:
| Source | Events |
|---|---|
storage.smart, storage.raid, storage.disk-usage |
Alert raised, alert cleared (including manual clears) |
storage.volume |
A data volume mounted by HSI is missing, replaced by another filesystem (critical) or read-only (warning) |
backup.plan |
A backup run failed |
system.update |
An HSI update did not end on the requested version: reported 5 minutes after HSI restarts on the old version, or 1 hour after the request if HSI never restarted |
External deliveries go through a queue stored in the database: they survive a backend restart, are retried twice (after 2 s and 10 s), and each connector sends at most its configured number of messages per minute (10 by default). Extra messages wait; none are dropped unless more than 500 are waiting. Recent deliveries and their errors are listed at the bottom of the page.
Webhook URLs, header values (except Content-Type) and SMTP passwords are
encrypted with HSI_SECRETS_KEY and never shown again after saving. If the
key changes, affected connectors are flagged and their secrets must be entered
again.
HSI can only notify while it is running. A server that is powered off, frozen or
unreachable cannot report its own outage: use an external monitor (for example
an uptime service polling https://<server>:9001/health) to detect that.
On an HTTPS install, plain http:// answers a 308 redirect: trust the authority in the monitor, or skip certificate checks there. Plain http:// works directly only in Off or Proxy mode.
- 9001: backend API + dashboard, over HTTPS by default (configurable via
BACKEND_PORT) - 443 and 80: optional standard ports (443 serves HTTPS, 80 redirects to it), or the nginx site of an older installation
- NATS listens locally for the backend ↔ root-worker channel
First setup
Section titled “First setup”A new installation has no account: the installer prints a one-time link to the
setup assistant, which creates the administrator (see
Install). sudo hsi-worker setup-token
prints a new link; it only opens the assistant while no administrator exists. Older installations keep their
accounts; an admin / admin account from before must still change its password
at the next login.