In Stage 3b, Waystone learned to create a game server from scratch: its own Linux account, data directory and systemd unit. I still had to copy the Minecraft jar and write its configuration by hand, though. Stage 3c is about removing those last manual steps.
The first part is now complete. Waystone can read and update Minecraft configuration files through the real agent and helper path, without giving the agent filesystem access or running the file worker as root.
Why a separate worker
The obvious shortcut would have been to let the provisioning service write server files directly. It already has privileges, after all. But that would mean a root service handling the contents of game server directories, which is exactly what the per-server accounts were meant to avoid.
Instead, Waystone now launches a short-lived worker for each file operation.
- The agent still doesn't touch server files. It sends a typed request containing the server ID, operation ID and supported file operation. It doesn't choose arbitrary filesystem paths.
- The provisioner checks which server owns the operation. It looks up the Linux account created during provisioning and verifies that the account still belongs to that server.
- systemd runs the worker as that server's user. The worker operates inside the server's own directory, not as root. It has no network access, no Linux capabilities and a read-only view of the rest of the system.
- The worker exits when the operation finishes. There's no long-running file service with access to every game server on the machine.
The existing runtime helper still only handles start, stop and status. File operations belong to the provisioning side, so the two privileged services keep their separate responsibilities.
Writing files without losing changes
Writing a configuration file sounds simple until two things try to change it at once.
Say someone edits server.properties manually while Waystone is preparing an update. If Waystone blindly writes its version, those changes disappear.
The worker now uses a read-modify-write approach. When Waystone reads a file, it records the contents. Before writing an updated version, the worker checks the existing file against the expected SHA-256 hash. If the contents have changed, the operation is refused rather than overwriting them.
The actual write uses a temporary file in the same directory. The worker writes the new contents, sets the permissions to 0640, syncs the file and atomically renames it into place. That avoids exposing a partially written configuration file if the process stops midway through writing.
There is still a small race between the final comparison and the rename. This isn't a filesystem transaction or a lock against someone editing the file at exactly the wrong moment. It does catch the normal case where a file has changed since Waystone last read it.
Minecraft settings and the EULA
The first game profile is Minecraft.
Waystone can now manage four settings in server.properties:
server-portmotdmax-playersonline-mode
It doesn't replace the entire file with a template. Unrelated settings and comments are preserved, so a configuration with additional Minecraft options doesn't lose them just because Waystone updates the MOTD.
The parser also handles Java properties syntax, including continuation lines, and refuses certain unsupported forms rather than guessing how to rewrite them.
EULA acceptance is implemented separately. Reading a file, provisioning a server or writing properties does not automatically accept Minecraft's EULA. That requires an explicit acceptance action.
Tested through the real path
I tested the worker on the Ubuntu development machine, using a provisioned server with its own Linux account.
The tests covered reading a missing file, creating server.properties, updating managed settings while preserving an unrelated property, and attempting a write using an outdated file hash.
The stale write was rejected, and the existing file remained intact. I also checked that the written file belonged to the server's account and had the expected 0640 permissions.
One important detail came out of testing the development tool itself: reading a file must really be read-only. An earlier command-dispatch mistake caused a read operation to trigger other actions, including EULA acceptance. I corrected the dispatch, removed the unintended test files and verified the read path again.
The development tool now also writes raw file contents to stdout and diagnostic messages to stderr. That means its output can be redirected into a file without accidentally including status messages.
After the live tests, the implementation went through another round of review covering error handling, strict JSON decoding, temporary-file cleanup and configuration parsing.
Stage 3c-1 is committed as ae35244.
What's still missing
This isn't the whole of Stage 3c.
Waystone can manage supported Minecraft configuration files, but it doesn't yet download and verify the Minecraft server jar. The game installation step is still manual.
There's also no controller or web UI yet. These operations are being exercised through the development tool while the agent and helper foundations are built and tested.
Next
Stage 3c-2 tackles the jar: downloading it, verifying it and placing it into the server's directory through the same sandboxed worker design.
Once that's working, the pieces built so far can come together: provision a server, install Minecraft, configure it and start it without manually copying files onto the machine.
The goal hasn't changed. The network-facing agent stays unprivileged, game servers stay isolated under their own accounts, and privileged operations remain narrow and explicit.
Waystone is open source under AGPL-3.0.