// The agent
What the agent reads
Everything the agent reads, the four commands it may run as root, and what it can never do.
What it is#
wellkept-agent is a single static binary. Every minute it tells WellKept it is alive. Every five minutes it sends a snapshot of the server's posture. It never changes anything on the server, and it never runs anything WellKept sends it.
This page lists what it reads, what it may run as root, and what it can never do, so you can decide whether it belongs on a client's server.
What it reads#
| Section | Where it comes from |
|---|---|
| Host | /etc/os-release, the kernel version, uptime, whether a reboot is waiting, the time zone and the host name |
| Resources | Load, memory, and disk and inode use on each real filesystem |
| Updates | apt-get -s dist-upgrade (a simulation) against the agent's own package lists, dpkg-query, and the unattended-upgrades settings |
| SSH | sshd -T, or sshd_config and its includes when that is not allowed |
| Firewall | The ufw configuration, nft list ruleset, and whether fail2ban is running |
| PHP | The installed versions, their FPM services and pools |
| Services | systemctl listings, package versions, and which addresses each service listens on |
| PM2 | PM2's saved process list and pid files, and whether it starts at boot |
| Sites | The nginx and Apache virtual hosts, then per document root the WordPress version file, Laravel's composer.lock or installed.json, package.json and the index file. For a WordPress site also the name and version at the top of each plugin and theme, read from the first 8 KiB of the plugin's main file and the theme's style.css |
It never reads .env, wp-config.php, database credentials, private keys or logs. Nothing outside the files and commands above is touched. Run sudo -u wellkept wellkept-agent collect on a server to see exactly what would be sent. It prints the payload and sends nothing.
What it may run as root#
The agent runs as the unprivileged wellkept user. /etc/sudoers.d/wellkept lets it run four commands as root, with exact arguments and no wildcards:
wellkept ALL=(root) NOPASSWD: /usr/sbin/sshd -T
wellkept ALL=(root) NOPASSWD: /usr/sbin/nft list ruleset
wellkept ALL=(root) NOPASSWD: /usr/bin/ss -ltnpH
wellkept ALL=(root) NOPASSWD: /usr/local/bin/wellkept-agent probe-pm2
sshd -Tprints the effective SSH configuration, which needs the root-only host keys. Without it the agent parses the configuration files.nft list rulesetneeds a network capability. Without it the firewall section reports the rules as unknown.ss -ltnpHnames the process behind each listening socket. Without it addresses are matched to services by their default ports.probe-pm2reads PM2's files under home directories that Ubuntu makes private. It only reads files. It never runspm2, becausepm2 jliststarts a PM2 daemon when none is running, and that would be an action on your server.
Every call is sudo -n, so a missing or edited sudoers file makes a probe fall back rather than hang. The agent refuses to start as root.
The package lists#
On Debian nothing refreshes apt's package lists unless unattended-upgrades is set up, so a count of pending security updates taken from the system's lists can be days or months old. The agent runs apt-get update once an hour into its own directory, /var/cache/wellkept/apt, as its own user. It writes nothing under /var/lib/apt and never takes the dpkg lock, so it cannot collide with unattended-upgrades or with you running apt. The lists take roughly 100 to 150 MB.
Set apt_refresh_interval = 0 in the configuration to use the system's lists instead, at the cost of accuracy.
The network#
- Outbound HTTPS only, to the configured API, verified against the system's certificate authorities, TLS 1.2 or newer.
- No listening socket, and no redirects followed.
- Nothing the API returns is executed or interpreted as a command. A heartbeat answer carries two yes or no flags (re-check, rotate the token) and two check intervals, which the agent clamps to safe limits.
Files#
| Path | Owner and mode | Holds |
|---|---|---|
/usr/local/bin/wellkept-agent |
root, 0755 | the binary |
/etc/wellkept/agent.conf |
root, 0644 | configuration |
/var/lib/wellkept/token |
wellkept, 0600 in a 0700 directory | this server's token |
/var/cache/wellkept/apt/ |
wellkept, 0700 | the agent's own package lists |
/etc/sudoers.d/wellkept |
root, 0440 | the four commands above |
/etc/systemd/system/wellkept-agent.service |
root, 0644 | the daemon |
The token never appears in a log: at most its first characters do. The service is sandboxed by systemd (read-only system, private temporary files, a syscall filter, no writable memory that is also executable).
Configuration#
/etc/wellkept/agent.conf, one key = value per line, with intervals in seconds. Restart the service after editing.
api_url = https://app.wellkept.io/api/v1
heartbeat_interval = 60
snapshot_interval = 300
apt_refresh_interval = 3600
log_level = info
Tokens and updates#
- Rotating the token. From the dashboard, the next heartbeat asks the agent to rotate. From the server, run
sudo wellkept-agent rotate-token. Either way the previous token keeps working for ten minutes, so a crash halfway never strands a server. - Updates are off by default. The agent logs when a newer release exists and never replaces itself. The optional daily update timer installs a release only if it is newer, not below the minimum the API names, fetched over HTTPS, matches its SHA-256 checksum and carries an Ed25519 signature that verifies against the public key compiled into the running binary. Anything else is refused and the download deleted.
When something fails#
- A probe that fails or runs out of time is listed as a probe error and its section is left out. The report still goes. WellKept skips the checks that read the missing section, and a failed probe never resolves a finding.
- If the server is revoked or removed in the dashboard, the agent logs it and stops, and systemd leaves it stopped.
- Network and server errors back off up to ten minutes. The daemon never exits for them. A hung agent is restarted by systemd's watchdog.
Limits#
- Ubuntu and Debian only. Other distributions get a host section and probe errors.
- PM2 restart counts are not reported, because only PM2's own daemon knows them.
- A site behind a proxy-only virtual host is reported with its proxy target as its path, unless exactly one PM2 app is saved.
- Let's Encrypt certificate files are readable only by root, so the agent usually cannot report their expiry. An SSL monitor covers that from outside.