Zopu VDS agents deployment
This directory holds the systemd unit, environment template, and Caddy reverse-proxy config that run the single Zopu agents Node process on the VDS. It supersedes the former container deployment for the agents process.
Runtime topology
One Node process (zopu-agents.service) runs the whole intelligence 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 in-process, serverless, at /internal/rivet/*
The Rivet Engine is a separate private service on 127.0.0.1:6420, managed by its own unit/compose. This unit does not start it. There is no standalone AgentOS runner: the registry handler runs inside the agents process.
The two Rivet endpoints (not interchangeable)
| Variable | Role | Value on this host |
|---|---|---|
RIVET_ENDPOINT |
Engine control plane this process connects to for actor metadata | http://127.0.0.1:6420 |
RIVET_SERVERLESS_ENDPOINT |
Address the engine calls back into this process's /internal/rivet handler |
http://127.0.0.1:3000/internal/rivet |
Pointing both at the engine, or dropping the /internal/rivet path, breaks the registry. See agents.env.example for the authoritative comments.
Files
zopu-agents.service— systemd unit. Runs/srv/zopu/current/.../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 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; the private/internal/rivet/*registry handler is never proxied.
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 in-process serverless registry at /internal/rivet/* is reachable only over loopback (the Engine's RIVET_SERVERLESS_ENDPOINT callback), never through Caddy. 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"}
curl -i https://agents.zopu.puter.wtf/internal/rivet/anything # 404 — registry stays private
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 single agents unit:
sudo systemctl restart zopu-agents.service
Roll back by atomically replacing current with a symlink to a prior release, then restarting the same 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