feat: consolidate project runtime deployment

This commit is contained in:
-Puter
2026-08-04 17:37:50 +05:30
parent 3fc80154e8
commit b4181a1271
23 changed files with 574 additions and 504 deletions
+3 -4
View File
@@ -13,10 +13,9 @@
# /health liveness probe
# /internal/project-setup single-call project setup coordinator
# /internal/agents/<agentId>/events agent event dispatch callback
# - The RivetKit serverless registry at /internal/rivet/* is NOT proxied. It
# is a private loopback callback the Engine reaches directly at
# http://127.0.0.1:3000/internal/rivet; exposing it publicly would widen the
# private worker protocol.
# - The AgentOS registry runs in-process as a native envoy (Rivet Mode A),
# so there is no /internal/rivet/* route to expose. Exposing a private
# worker protocol publicly would widen the attack surface needlessly.
# - Former routes (/agents/zopu/*, /internal/work-attempts/*, /workflows/*,
# /api/rivet/*) are intentionally absent: they are stale and not part of the
# current Convex→agents contract.
+13 -15
View File
@@ -1,36 +1,35 @@
# 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.
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 (`zopu-agents.service`) runs the whole intelligence runtime:
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 in-process, serverless, at /internal/rivet/*
└─ AgentOS registry envoy, native runtime (in-process)
```
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 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 two Rivet endpoints (not interchangeable)
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` |
| `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.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 with no secrets.
- `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`; the private `/internal/rivet/*` registry handler is never proxied.
- `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
@@ -73,7 +72,7 @@ curl -fsS http://127.0.0.1:3000/health # {"service":"zopu-agents","status
### 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`.
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:
@@ -92,7 +91,6 @@ Verify the narrow public surface:
```sh
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
@@ -103,13 +101,13 @@ 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:
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 same service.
Roll back by atomically replacing `current` with a symlink to a prior release, then restarting the service.
## Optional: explicit engine ordering
+16 -9
View File
@@ -13,22 +13,29 @@ HOST=127.0.0.1
PORT=3000
NODE_ENV=production
# --- Rivet Engine + in-process registry (two distinct endpoints) -------------
# --- Rivet Engine ------------------------------------------------------------
#
# RIVET_ENDPOINT is the Engine CONTROL PLANE this process connects TO for actor
# metadata/management. The engine is a separate private service on this host.
RIVET_ENDPOINT=http://127.0.0.1:6420
#
# RIVET_SERVERLESS_ENDPOINT is the address the Engine calls BACK INTO this same
# process to reach the in-process serverless AgentOS registry handler, which is
# mounted at /internal/rivet/*. It must resolve to this process's own listener
# plus that base path. Do NOT point this at the engine; do NOT drop the path.
RIVET_SERVERLESS_ENDPOINT=http://127.0.0.1:3000/internal/rivet
#
# Shared secret authenticating actor connections between the engine and this
# registry's onBeforeConnect. Must match the value configured on the engine.
# Shared secret authenticating actor connections between the in-process envoy
# and the AgentOS project workspace actor. Must match the value used by the
# engine.
RIVET_WORKSPACE_TOKEN=REPLACE-WITH-LONG-RANDOM-WORKSPACE-TOKEN
# RivetKit runtime selection. These make the native Mode A choice explicit;
# the registry still connects outbound to the private Engine.
RIVETKIT_RUNTIME_MODE=envoy
RIVETKIT_RUNTIME=native
# --- Actor versioning --------------------------------------------------------
#
# RivetKit uses this to drain actors when the envoy version changes. Bump it
# whenever the actor definition or envoy behavior changes in a way that cannot
# coexist with the previous version.
RIVET_ENVOY_VERSION=1
# --- Flue SQLite persistence -------------------------------------------------
# Canonical conversation state. Parent dir must be one of the unit's
# ReadWritePaths (/srv/zopu/data).
+10 -14
View File
@@ -1,33 +1,29 @@
# zopu-agents.service — single Node intelligence runtime for the Zopu VDS.
# zopu-agents.service — single Zopu intelligence runtime for the VDS.
#
# Runtime shape (one process, no separate runner):
# One process hosts both the Hono/Flue server and the AgentOS registry running
# in native envoy mode (Rivet Mode A):
#
# zopu-agents (this unit)
# └─ node /srv/zopu/current/packages/agents/dist/server.mjs
# ├─ Hono HTTP server on 127.0.0.1:3000
# ├─ Flue 2.0 conversation agents + SQLite at /srv/zopu/data/flue
# └─ AgentOS registry in serverless mode, served in-process at
# /internal/rivet/* (registry.handler drives the per-request
# runtime; there is no standalone AgentOS runner process).
# └─ AgentOS registry in envoy/native mode (registry.startAndWait)
#
# The AgentOS actor is hosted in-process: the Hono/Flue process is also the
# envoy that connects outbound to the Rivet Engine. There is no separate
# runner process and no Rivet HTTP handler route.
#
# 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 or own it; it only waits
# for the engine health endpoint before binding (ExecStartPre below).
#
# Two distinct Rivet endpoints (see agents.env):
# RIVET_ENDPOINT — engine control plane the registry connects to
# (http://127.0.0.1:6420). Used for actor metadata.
# RIVET_SERVERLESS_ENDPOINT — address the engine calls BACK into this same
# process's /internal/rivet handler
# (http://127.0.0.1:3000/internal/rivet).
# These are NOT interchangeable; do not point both at the engine.
# RIVET_ENDPOINT is the engine control plane (http://127.0.0.1:6420).
#
# The release is a self-contained bundle under /srv/zopu/releases/<id> with
# /srv/zopu/current as the live symlink. This unit always runs `current`; the
# release script swaps it atomically, then the operator restarts this service.
[Unit]
Description=Zopu agents intelligence runtime (Flue + in-process AgentOS registry)
Description=Zopu agents intelligence runtime (Hono + Flue + in-process envoy)
# Soft network ordering. The real gate on the separate Engine is the health
# probe in ExecStartPre. If the engine is itself a named systemd unit on this
# host, add an explicit ordering here, e.g.: