Zopu VDS agents deployment
This directory holds the systemd unit, environment template, and Caddy reverse-proxy config that run the Zopu intelligence runtime on the VDS. It supersedes the former container deployment for the agents process.
Runtime topology
One Node process hosts the entire runtime:
zopu-agents.service
└─ node /srv/zopu/current/packages/agents/dist/server.mjs
├─ Hono server 127.0.0.1:3000
├─ Flue 2.0 agents SQLite at /srv/zopu/data/flue/flue.db
└─ AgentOS registry envoy, native runtime (in-process)
The process connects outbound to the Rivet Engine, a separate private service on 127.0.0.1:6420, managed by its own unit/compose. This unit does not start or own it.
The Hono/Flue server handles Convex callbacks and dispatches to Flue agents. The AgentOS actor is hosted in-process: the same process is the envoy that owns the native Actor Runtime Socket (Rivet Mode A). There is no separate runner process and no Rivet HTTP handler route.
Rivet endpoint
| Variable | Role | Value on this host |
|---|---|---|
RIVET_ENDPOINT |
Engine control plane this process connects to for actor metadata | http://127.0.0.1:6420 |
Files
zopu-agents.service— systemd unit. Runs/srv/zopu/current/packages/agents/dist/server.mjsas userzopu, waits for engine health before binding, hardens the service, and restarts on failure.agents.env.example— template for/etc/zopu/agents.env. Contains the full validated env schema (Hono/Flue + in-process envoy) with no secrets.../../scripts/release-agents.sh— local build, upload, and atomic release promotion command. Each release includes this directory so the unit'sDocumentation=target remains valid.Caddyfile— source-controlled public ingress foragents.zopu.puter.wtf. Terminates TLS and forwards only/health,/internal/project-setup, and/internal/agents/*to127.0.0.1:3000.
Prerequisites on the VDS
- Node 24 installed at
/opt/zopu/node/bin/node(the verified VDS Node; the unit'sExecStartandPATHare anchored on this path). The release bundle ships its ownnode_modules; only the Node binary is expected from the host. curlandbashonPATH(used by theExecStartPreengine health probe).- A dedicated service user and group:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin zopu - The two writable state trees, owned by
zopu:sudo mkdir -p /srv/zopu/data/flue /srv/zopu/workspaces sudo chown -R zopu:zopu /srv/zopu/data /srv/zopu/workspaces - The Rivet Engine running and answering
http://127.0.0.1:6420/health. - A release promoted by
scripts/release-agents.shto/srv/zopu/releases/<release-id>, with/srv/zopu/currentsymlinked to it. The release must containpackages/agents/dist/server.mjsanddeploy/vds/.
Install
# 1. Secrets
sudo install -d -m 0750 -o root -g zopu /etc/zopu
sudo cp agents.env.example /etc/zopu/agents.env
sudo chown root:zopu /etc/zopu/agents.env
sudo chmod 0600 /etc/zopu/agents.env
sudo "$EDITOR" /etc/zopu/agents.env # fill in every REPLACE-WITH-...
# 2. Unit
sudo install -m 0644 zopu-agents.service /etc/systemd/system/zopu-agents.service
sudo systemctl daemon-reload
sudo systemctl enable --now zopu-agents.service
Verify
systemctl status zopu-agents.service
curl -fsS http://127.0.0.1:3000/health # {"service":"zopu-agents","status":"ok"}
Public ingress (Caddy)
Caddy terminates TLS for agents.zopu.puter.wtf and forwards only the three paths the Convex backend calls to the internal agents worker (127.0.0.1:3000). The AgentOS actor registry runs in-process, so no /internal/rivet/* path is exposed on the agents listener. All other paths return 404.
Install or update from this checked-in artifact:
# 1. Materialize the config
sudo install -m 0644 Caddyfile /etc/caddy/Caddyfile
# 2. Validate before applying
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
# 3. Reload the running Caddy (no restart needed)
sudo systemctl reload caddy
Verify the narrow public surface:
curl -fsS https://agents.zopu.puter.wtf/health # {"service":"zopu-agents","status":"ok"}
Release switch
From the repository root on the build host, create and promote a release:
scripts/release-agents.sh <vds-host> <release-id>
The script atomically swaps /srv/zopu/current; it deliberately does not restart the service. After the upload succeeds, restart the unit:
sudo systemctl restart zopu-agents.service
Roll back by atomically replacing current with a symlink to a prior release, then restarting the service.
Optional: explicit engine ordering
The unit already gates startup on the engine health endpoint via ExecStartPre. If the engine is itself a named systemd unit on this host, you may add a hard ordering by uncommenting/adding in the [Unit] section:
After=rivet-engine.service
Wants=rivet-engine.service