Files
zopu-code/deploy/vds

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.mjs as user zopu, 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's Documentation= target remains valid.
  • Caddyfile — source-controlled public ingress for agents.zopu.puter.wtf. Terminates TLS and forwards only /health, /internal/project-setup, and /internal/agents/* to 127.0.0.1:3000.

Prerequisites on the VDS

  1. Node 24 installed at /opt/zopu/node/bin/node (the verified VDS Node; the unit's ExecStart and PATH are anchored on this path). The release bundle ships its own node_modules; only the Node binary is expected from the host.
  2. curl and bash on PATH (used by the ExecStartPre engine health probe).
  3. A dedicated service user and group:
    sudo useradd --system --no-create-home --shell /usr/sbin/nologin zopu
    
  4. 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
    
  5. The Rivet Engine running and answering http://127.0.0.1:6420/health.
  6. A release promoted by scripts/release-agents.sh to /srv/zopu/releases/<release-id>, with /srv/zopu/current symlinked to it. The release must contain packages/agents/dist/server.mjs and deploy/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