Hostveil / Docs / Troubleshooting

Troubleshooting

What the less obvious messages mean, and how to find out what Hostveil actually did.

See every command Hostveil runs

Most questions about a wrong-looking result come down to one command answering differently than expected. Set HOSTVEIL_DEBUG=1 to trace them:

HOSTVEIL_DEBUG=1 hostveil scan

Each line names the command, how long it took, and whether it failed — including the binary lookups that decide whether a domain runs at all. The trace goes to stderr, so --json and redirects are unaffected, and it is the right thing to attach to a bug report.

Command output is deliberately never logged. docker inspect reports the resolved environment of every container, so a trace that included it would routinely be a credential leak.

A domain says “Degraded”

The checker ran and covered part of its ground. The findings it reported are real and are scored; what it could not examine is named in the reason. Common causes: a Match block in sshd_config, an Included sshd config it cannot read, a compose file it cannot parse, images Trivy could not pull, or containers it could not enumerate.

A Match block is the newest of those and the one most hosts hit. Hostveil stops reading sshd_config at the first Match, because sshd applies what follows only to connections that match — a directive there does not describe the host the way a global one does. So Match User git or Match Address sends the SSH domain to Degraded, and the reason names the file. The findings above the block are real and are scored; what is inside it was not read. This is deliberate: Match Address 0.0.0.0/0 followed by PasswordAuthentication yes re-enables passwords for every connection there is, and a scan that reported that host as fully audited would be saying something it had not checked.

Degraded is deliberately different from Skipped (a dependency is absent — the domain is excluded from scoring entirely, not given full marks) and from Error (the checker failed outright).

I applied fixes and the score did not move

On a host with containers this is the expected outcome of fix --all, and it is the honest one.

A fix Hostveil applies is not always a change the host has seen. A Compose file is read when the container is recreated; a systemd drop-in when the unit is reloaded; a sysctl drop-in at the next boot or at sysctl --system. Until then the file on disk is correct and the running system is exactly as it was — so the finding stays charged in the score, and the row stays on the list marked PEND in the terminal and Applied — not in force yet on the dashboard.

fix --all says how many are waiting, and each fix names the command that puts it in force. Run it, scan again, and the number moves. The score is deliberately not a receipt for work done: it is a statement about the host, and the host had not changed yet.

See Scoring for the rule, and Fixing for what the re-check after a fix can and cannot establish.

Rollback says the file was edited externally

The file no longer matches anything Hostveil recorded writing, so someone changed it after the fix ran. Rollback keeps no backup of its own, so restoring would discard those edits irreversibly — it declines instead.

If you are sure you want the backup back, hostveil rollback <id> --force overwrites. Note that --force will not push through a backup whose checksum does not match what was stored: there is nothing correct on the other side of a damaged backup.

“No fixes have been applied yet” after applying fixes

Almost always the state directory. Hostveil stores checkpoints in /var/lib/hostveil when running as root and ~/.local/share/hostveil otherwise, so a fix applied under sudo is invisible to an unprivileged hostveil history. Re-run with sudo. This most often bites when HOSTVEIL_NO_SUDO=1 is set, or on a host with no sudo installed.

A scheduled scan exits 3

A detection domain failed outright, so the scan covered less of the host than it should have — a result that looks clean but is not trustworthy. An unreachable Docker socket is the usual cause. A domain merely skipped for a missing dependency, or degraded to partial coverage, does not produce this. See exit codes.

A check reports that a command “did not respond”

Hostveil bounds every command it runs, so a daemon that accepts a connection and never answers — a wedged Docker is the everyday case — fails that check instead of hanging the scan. The affected domain is reported honestly rather than scored as clean. Check the daemon named in the message; HOSTVEIL_DEBUG=1 shows exactly which call stalled.

An SSH fix refuses to apply

Before writing, Hostveil runs the config it would produce through sshd -t. If sshd rejects it, the fix is abandoned and your file is left untouched — nothing was written, so there is nothing to roll back. This exists because sshd keeps serving from the config it already loaded: a broken file would look like nothing at all until the next restart, when sshd refuses to start and repairing it needs the SSH access it just removed.

Ctrl-C does not seem to stop it

The first interrupt cancels the work in progress and lets it wind down cleanly — stopping the commands a scan has running, draining the dashboard's in-flight requests. Press it a second time to exit immediately. A batch fix stops between fixes rather than during one, and tells you how many were never attempted; whatever did apply is in hostveil history.