BlogEngineering

How ox Treats Security When It Holds Root on Your Server

ox has root on your VPS. How it keeps that trust: signed agent builds, one Linux user per app, secrets out of logs, and a security review of every change.

On this page

When you add a server to ox, you give it root on that server. It also reads the GitHub repos you pick and handles your app's secrets on every deploy. That is a lot of trust to hand a tool run by one person. This post is how I think about that trust, and what ox does to keep it.

What ox holds, and why that sets the bar

A small program called the agent runs on your server as root. It installs packages, writes service files, starts your app and takes backups. The hosted part of ox, where the dashboard lives, tells it what to do. So I ask one question about every change: if someone took over one piece of this, what could they reach? The answer should be as little as possible.

A small footprint you can look at

Of ox's own pieces, only two keep running on your server: the agent and Caddy, which serves your sites over HTTPS. There is no Docker. Each app is a plain systemd service, its scheduled jobs are systemd timers, and its variables sit in a plain env file. Less running software means less to attack, and you can read every piece with the Linux tools you already know. sudo ox doctor --inventory lists everything ox could own on the server.

The agent connects out to ox over HTTPS. ox never connects in, holds no SSH key for your server, and needs no open port for the agent.

Leaving is part of security too. When you delete your account, the agent removes itself and your apps keep running as they are. If ox ever shuts down, one command on the server does the same with no help from ox. The security page in the docs says what stays and what to copy.

Your server installs only agent builds I signed

The agent updates itself when I ship a new version. Whoever controls that update path controls every server, so every agent build carries two signatures: one from ox's hosted side, and one from a release key that never lives on ox's servers or on yours. The agent refuses a build that is missing either one.

So even someone who took over ox's hosted side could not push a program of their own onto a server you already added. The docs page spells out what such a person could still do.

Each app runs as its own user

Every project gets its own Linux user, and its services run in a systemd sandbox with no extra privileges. An app can write only to its own folders. It cannot see other users' processes, load kernel modules or trace other programs. Only Caddy and the app's own project can reach its ports. Your variables sit in files that only root and that one project can read.

Root also never acts inside a project's own folders, because the app's code owns them and could plant a link there that tricks root into touching the wrong file. ox does that work as the project's own user.

When ox sets up a server, it also hardens it: SSH takes keys only, the firewall refuses incoming connections except SSH, the web ports and any rule you already had, and Ubuntu's security updates install themselves. sudo ox install --audit prints a pass or fail row for each setting and changes nothing. An up-to-date server also runs this audit every day, and its page in ox shows any row that drifted, with the fix.

Secrets stay off command lines and out of logs

A secret usually leaks in boring ways. Other users on a machine can see a program's command line, and a log line gets copied into a chat. So ox hands secrets to a program through its environment or its input, never on the command line. Before a deploy's log line is written, ox hides your variables' values in it, along with common encoded forms of them, and tests fail if any form shows up.

The hosted side of ox keeps your variable names, and the values live on your server. When you view or save them, they pass through ox to your screen with no copy kept. Your server also writes a line to its own log each time ox reads a value, deploys or takes a backup, and ox's hosted side cannot rewrite that record.

Every change is reviewed, and every review covers security

Code changes reach ox through small pull requests, each read by a separate reviewer that did not write it. That reviewer is an AI agent started fresh for that one review, with no part in writing the code. It looks for bugs, security problems and claims the code does not back, and checks each finding against the code before reporting it.

Security is part of every review, whatever the change is about. Does a new page check who is asking? Can one account ever reach another account's servers or data? A security problem found in a review is fixed before the change merges.

The ship script refuses a version of ox that has not passed a full test suite on fresh real servers, made for that run and deleted after it.

Learning from other people's incidents

When a company in our industry publishes a security incident, I read it and ask what the same attack would get from ox. Sometimes it shows a gap, and the fix goes through the same review and gate as any other change.

I also run security reviews over the whole code on a regular basis. A second reviewer checks each finding, and the fix lands through the same pull request, review and release gate. A written threat model of what each piece of ox could do if taken over is updated with every review.

No system is perfect, ox included. I read every report and fix its cause.

How to report a problem

If you find a security problem in ox, please write to me. The address is on the privacy page, and ox also publishes it in a standard security.txt file that tools and researchers know to look for.

Key takeaways

  • Only the agent and Caddy keep running on your server, and the agent only connects out.
  • Your server installs only agent builds signed by both ox and a release key kept off ox's servers.
  • Each app runs as its own Linux user, and secrets are hidden from logs before a line is written.
  • Every change gets a security review from a reviewer that did not write it, and the ship script refuses a release that has not passed tests on fresh servers.

Frequently asked questions

Does ox keep an SSH key to my server?

No. The agent on your server connects out to ox over HTTPS, and ox never connects in, so your server opens no port for it and ox holds no SSH key. Anyone with an SSH key for your own login account is still root on the server, so who holds those keys stays your choice.

Where do my app's secrets live?

On your server, in files that only root and that one project can read. ox keeps the names of your variables, and the values pass through it only when you view, save or copy them, with no copy kept. Your server writes a line to its own log each time a value is read.

What if ox's own servers were taken over?

The attacker could not install a program of their own on a server you already added, because the agent needs a second signature from a release key kept off ox's servers. They could still run jobs as your projects' users and read your projects' variables, as ox does to deploy. The security docs page explains this in full.

Can I check what ox changed on my server?

Yes. Run sudo ox install --audit on the server for a pass or fail row per security setting; it changes nothing. sudo ox doctor --inventory lists everything ox could own there. An up-to-date server also audits itself every day, and its page in ox shows any setting that drifted and how to fix it.

Try it

ox is free for your first project. The quickstart takes you from a fresh server to a live app, and this post tells why I built ox.

New to running apps on your own server? How ox runs your apps shows what ox sets up on your server.

Keep reading

Deploy to a server you own

ox sets up systemd, Caddy and PostgreSQL on your Ubuntu server and deploys on every push. Free for your first project.

Sign up with GitHub Read the docs