# 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: ```sh sudo useradd --system --no-create-home --shell /usr/sbin/nologin zopu ``` 4. The two writable state trees, owned by `zopu`: ```sh 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/`, with `/srv/zopu/current` symlinked to it. The release must contain `packages/agents/dist/server.mjs` and `deploy/vds/`. ## Install ```sh # 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 ```sh 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: ```sh # 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: ```sh 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: ```sh scripts/release-agents.sh ``` The script atomically swaps `/srv/zopu/current`; it deliberately does **not** restart the service. After the upload succeeds, restart the unit: ```sh 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: ```ini After=rivet-engine.service Wants=rivet-engine.service ```