What we install on your server — and how to verify it yourself
Any monitoring tool asks for a lot of trust. You give it SSH access to your server, it installs its own software there, and that software reads your logs and configs. The promise “we read, we don't change, your data stays with you” sounds good, but a promise is just words.
We wanted our promise to be something you can check. So the vpsFocus code is split into two parts: closed and open. Here's where the line runs, why open code alone isn't enough, and how to compare what's running on your server with what's published.
Where the line runs
vpsFocus has three parts:
- the app on your computer — the interface, charts, password storage;
- our website and server — accounts and availability checks from outside;
- what gets installed on your server — installer files, the monitoring agent, our Caddy image and the scripts the app runs over SSH.
The first two run on your computer and on our server. The third runs on your server, often as root and right next to your sites. That's the part we've opened in full: it's in the public repository github.com/zaevlad/vpsfocus-server.
Why not open everything? The app is what we earn from and what sets us apart, and its code does nothing to your server. The code that runs on your server, though, you have every right to read before installing it. That's a sensible line: everything that touches your machine is open.
What's in the open repository
- The installer files of every version — exactly as your server receives them: install, update and rollback scripts, configuration templates.
- Every script the app runs over SSH: finding sites and certificates, backups, the security check and the rest. Each has a line in the inventory
SERVER-SIDE.md: when it runs, what it reads, what it changes. - The source code of the monitoring agent and of our Caddy build.
- A list of third-party code inside our images with licenses: Caddy modules, the link checker, Go libraries.
Analytics is Umami, the database is PostgreSQL. We don't rebuild their official images, we only pin them by exact digest.
Why open code alone isn't enough
Say the code is open, you've read it and you like it. But what reaches your server isn't source code — it's ready-made files and built Docker images. How do you know the image your server downloaded was built from the code you read? That nobody added anything between the repository and your server — neither us, nor someone who might have broken into our computer?
That's the signing problem. Open projects usually answer it with “we built it from this code, trust us”. We solved it with two signatures, each with its own job.
Signing the installer files
Every version of the installer files is signed with our release key. What gets signed is the inventory — a list of every file in the version with its permissions and SHA-256 checksum. If a single byte of any file changes, the signature stops matching.
- The app won't install a version whose signature doesn't match — it refuses with an error.
- The private signing key lives only on the machine we release from. It isn't in the cloud, on GitHub or on our server. Our server distributes the installer files but can't sign them: if it were broken into, the attacker still couldn't swap the installation on our users' servers.
- The public half of the key is in the repository, and its fingerprint
337F561861A23810appears in three independent places: the repository README,SECURITY.mdand our website. To swap the key unnoticed, someone would have to break into all three.
Build signing in GitHub Actions
Our Docker images — the agent and Caddy — aren't built by us on our own computer, but by GitHub Actions in the open repository. Every build gets a build provenance attestation: a record signed by GitHub itself, stating that the image with this digest was built by this workflow from this commit of this repository.
That changes the trust question. Before, you had to take our word for it. Now GitHub vouches for the “image ↔ code” link, and you can check it with one command — without our code and without us.
So that the build itself doesn't become the weak spot:
- third-party actions in the build workflows are pinned to a full commit hash, not a tag their author could move;
- the workflows have minimal permissions and run only on tags, never on outside pull requests;
- published tags can't be deleted or moved — not even by us. A mistake in a version is fixed by a new version, and the old one stays as it was.
How the signatures fit together
The chain looks like this:
- The install screen in the app names the version of the installer files and links to its tag in the repository.
- The version's inventory is signed with our key and lists every file.
- In the configuration template, images are referenced not by name but by exact digest (
@sha256:…). - Each of those images has a GitHub attestation naming the commit it was built from.
- The source code in that commit matches the code in the version's tag.
Every link is checked automatically on release — in the same open repository, with results visible to everyone. A red cross on a tag would be seen by everyone, not just us.
How to verify it yourself
Everything below can be done without us. The exact commands are in the repository README.
The signature and files of a version. One command checks the signature and every file of the version:
git clone https://github.com/zaevlad/vpsfocus-server
cd vpsfocus-server
go run ./tools/verify -version 0.32.1
With the -archive flag it compares the archive the app downloads against the tag, file by file. You can also check the signature with the standard minisign tool, without trusting our check.
Where an image came from. Take an image digest from the version's template and ask GitHub where it came from:
gh attestation verify oci://ghcr.io/zaevlad/vpsfocus-agent@sha256:… \
--repo zaevlad/vpsfocus-server
What's running on your server. On the server itself, this command shows the digests of the running images — they should match the ones in the version's template:
docker ps --filter name=vpsfocus --format '{{.Names}} {{.Image}}'
What open code doesn't prove
To be honest about the limits:
- The app is closed. You can see the open scripts, but you can't read the app's code to confirm it sends exactly those over SSH. On our side this is held by automated tests: the scripts sit inside the app as the very same files as in the repository, and the tests fail if the app runs a script that isn't in the inventory. On your side, you can turn on the
sudolog orauditdon the server — it shows every command that was run. - The agent can see the whole server disk, read-only. It needs logs, web server configs and disk data, which live in different places. This is visible in the open code, and so is what the agent sends out: only alerts to the channels you chose.
- Versions from 0.28.0 onward are open. If your server runs an older one, update the monitoring from the app.
License
The code is published under the Business Source License 1.1. You may read, check, modify and run it on your own servers and your clients' servers, including for a fee. You may not build a competing monitoring or analytics product on it. Four years after release, each version becomes Apache 2.0.
Found a vulnerability? Please report it privately through the form in the repository. We answer within 7 days.
In short
- What runs on your computer and on our server is closed.
- Everything that runs on your server is open.
- The installer files are signed with a key that's neither in the cloud nor on our server.
- GitHub Actions builds the images, and GitHub itself vouches for their origin.
- Every link in the chain can be checked with your own hands.