Waystone just started and stopped its first Minecraft server through the real agent path. On screen it's a handful of commands in a terminal. Underneath, it's the security boundary the rest of the panel will stand on, so here's what's in that screenshot and why it's built this way.
The problem
A game panel has to do privileged things. It creates users, writes service files, and starts and stops services. The easy route is to run the node agent as root. The agent is also the part that talks to the network, though, so a bug in it would be a root bug on every machine it runs on. I didn't want that, not even as a temporary shortcut.
The split
Waystone divides the work between two programs:
- The agent runs as its own unprivileged user,
waystone-agent. It will take orders from the controller, and it has no special rights on the machine. - The helper is a tiny root program that can do a few fixed things and nothing else. Right now that's exactly three: start, stop and status.
The agent asks. The helper checks, then decides.
How the helper stays small and strict
- Only the agent can talk to it. systemd creates the helper's socket in
/run, owned by root with groupwaystone-agentand mode 0660. Other users can't even connect. - It asks the kernel who's calling. A Unix socket lets the receiving side read the caller's real user ID, and the caller can't fake that. The helper accepts exactly one user ID, the agent's. Even root gets turned away.
- The agent never sends a name or a path. It sends a server ID. The helper checks that it's a well-formed UUID and builds the service name,
waystone-srv-<uuid>.service, itself. A request like "stop sshd" is rejected before anything happens. - Typed messages, no shell. Requests are size-capped protobuf messages, and unknown fields are rejected. The helper calls
systemctlwith a fixed argument list and never goes through a shell. - It's sandboxed itself. The helper has no Linux capabilities at all, sees the system read-only and has no network. systemd's own security check rates it 1.3 "OK" (lower is better). Proving that
systemctlworks with zero capabilities was one of the open questions for this milestone, and it does. - It logs before it acts. Every request is written to the journal before anything happens, with an operation ID, the server, the action and the caller's user ID. The ID printed after each command in the screenshot is the same ID you'll find in the helper's log.
What the screenshot shows

- Status: the server is stopped.
- Start, then status: it's running. The game runs under systemd as its own user, never as a child of the agent, so it doesn't depend on the agent staying up.
- Stop, then status: stopped cleanly.
- The same command as my own user: it can't even reach the helper's socket.
- The same command as root, with sudo: it connects, and the helper refuses it as an unauthorized caller.
- Asking the agent to stop sshd: invalid request. SSH is still running.
What it doesn't do yet
- No controller or web UI yet. These commands come from a temporary development tool. It's only compiled with a special build flag, so a release build can't contain it, and it goes away once the controller drives the agent.
- Stopping isn't graceful yet. Stop currently sends a signal. A proper stop, which sends the game its own
stopcommand through a console channel so the world saves first, is planned. - The agent can still manage every server on its own machine. That's its job. What it can't do is become root or reach beyond that machine.
Next
Next, the helper provisions servers on its own. It creates each server's user, its directory under /var/lib/waystone/servers/, and its service from a template, so there are no hand-written unit files. After that comes installing the game itself: the jar, the EULA and the properties. Those are written by a short-lived worker that runs as the server's own user in a sandbox, so even the installer never touches a server's files as root.
The message format and IDs are already OS-neutral, so the same design carries over when Windows hosting arrives.
Waystone is open source under AGPL-3.0.