From 9f7dd3c1a62ccbbb5d5952f5a8102685d8cee5ec Mon Sep 17 00:00:00 2001
From: -Puter <22245429+puterhimself@users.noreply.github.com>
Date: Wed, 29 Jul 2026 20:49:52 +0530
Subject: [PATCH 1/2] fuck them garbage deploys
---
deploy/dokploy/agent.Dockerfile | 20 --
deploy/dokploy/backend.compose.yml | 31 --
deploy/dokploy/frontend.compose.yml | 15 -
deploy/dokploy/runner.Dockerfile | 23 --
deploy/dokploy/runner.compose.yml | 26 --
deploy/dokploy/web.Dockerfile | 21 --
deploy/zopu-runtime/.env.template | 86 -----
deploy/zopu-runtime/README.md | 306 ------------------
deploy/zopu-runtime/bootstrap.sh | 290 -----------------
deploy/zopu-runtime/caddy/Caddyfile | 23 --
.../zopu-runtime/caddy/Caddyfile.production | 25 --
.../cloudflared/config.production.yml | 13 -
deploy/zopu-runtime/convex/docker-compose.yml | 32 --
deploy/zopu-runtime/runner/Dockerfile | 23 --
deploy/zopu-runtime/runner/docker-compose.yml | 26 --
deploy/zopu-runtime/scripts/disk-monitor.sh | 63 ----
deploy/zopu-runtime/scripts/docker-cleanup.sh | 36 ---
deploy/zopu-runtime/scripts/health-check.sh | 122 -------
deploy/zopu-runtime/scripts/rollback.sh | 104 ------
deploy/zopu-runtime/scripts/update.sh | 113 -------
.../zopu-runtime/systemd/zopu-agent.service | 40 ---
.../zopu-runtime/systemd/zopu-daemon.service | 42 ---
.../systemd/zopu-docker-cleanup.service | 7 -
.../systemd/zopu-docker-cleanup.timer | 9 -
.../zopu-runtime/systemd/zopu-health.service | 11 -
deploy/zopu-runtime/systemd/zopu-health.timer | 10 -
deploy/zopu-runtime/systemd/zopu-web.service | 26 --
27 files changed, 1543 deletions(-)
delete mode 100644 deploy/dokploy/agent.Dockerfile
delete mode 100644 deploy/dokploy/backend.compose.yml
delete mode 100644 deploy/dokploy/frontend.compose.yml
delete mode 100644 deploy/dokploy/runner.Dockerfile
delete mode 100644 deploy/dokploy/runner.compose.yml
delete mode 100644 deploy/dokploy/web.Dockerfile
delete mode 100644 deploy/zopu-runtime/.env.template
delete mode 100644 deploy/zopu-runtime/README.md
delete mode 100755 deploy/zopu-runtime/bootstrap.sh
delete mode 100644 deploy/zopu-runtime/caddy/Caddyfile
delete mode 100644 deploy/zopu-runtime/caddy/Caddyfile.production
delete mode 100644 deploy/zopu-runtime/cloudflared/config.production.yml
delete mode 100644 deploy/zopu-runtime/convex/docker-compose.yml
delete mode 100644 deploy/zopu-runtime/runner/Dockerfile
delete mode 100644 deploy/zopu-runtime/runner/docker-compose.yml
delete mode 100755 deploy/zopu-runtime/scripts/disk-monitor.sh
delete mode 100755 deploy/zopu-runtime/scripts/docker-cleanup.sh
delete mode 100755 deploy/zopu-runtime/scripts/health-check.sh
delete mode 100755 deploy/zopu-runtime/scripts/rollback.sh
delete mode 100755 deploy/zopu-runtime/scripts/update.sh
delete mode 100644 deploy/zopu-runtime/systemd/zopu-agent.service
delete mode 100644 deploy/zopu-runtime/systemd/zopu-daemon.service
delete mode 100644 deploy/zopu-runtime/systemd/zopu-docker-cleanup.service
delete mode 100644 deploy/zopu-runtime/systemd/zopu-docker-cleanup.timer
delete mode 100644 deploy/zopu-runtime/systemd/zopu-health.service
delete mode 100644 deploy/zopu-runtime/systemd/zopu-health.timer
delete mode 100644 deploy/zopu-runtime/systemd/zopu-web.service
diff --git a/deploy/dokploy/agent.Dockerfile b/deploy/dokploy/agent.Dockerfile
deleted file mode 100644
index f669a6d..0000000
--- a/deploy/dokploy/agent.Dockerfile
+++ /dev/null
@@ -1,20 +0,0 @@
-FROM oven/bun:1.3.14 AS build
-
-WORKDIR /app
-
-COPY . .
-RUN bun install --frozen-lockfile
-RUN bun run --filter @code/agents build
-
-FROM node:24-bookworm-slim
-
-ENV NODE_ENV=production
-ENV PORT=3000
-
-WORKDIR /app
-
-COPY --from=build /app /app
-
-EXPOSE 3000
-
-CMD ["node", "packages/agents/dist/server.mjs"]
diff --git a/deploy/dokploy/backend.compose.yml b/deploy/dokploy/backend.compose.yml
deleted file mode 100644
index 7541348..0000000
--- a/deploy/dokploy/backend.compose.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-services:
- backend:
- build:
- context: ../..
- dockerfile: packages/agents/Dockerfile
- environment:
- AGENT_BACKEND_URL: ${AGENT_BACKEND_URL}
- AGENT_MODEL_API: ${AGENT_MODEL_API}
- AGENT_MODEL_API_KEY: ${AGENT_MODEL_API_KEY}
- AGENT_MODEL_BASE_URL: ${AGENT_MODEL_BASE_URL}
- AGENT_MODEL_CONTEXT_WINDOW: ${AGENT_MODEL_CONTEXT_WINDOW}
- AGENT_MODEL_MAX_TOKENS: ${AGENT_MODEL_MAX_TOKENS}
- AGENT_MODEL_NAME: ${AGENT_MODEL_NAME}
- AGENT_MODEL_PROVIDER: ${AGENT_MODEL_PROVIDER}
- CONVEX_URL: ${CONVEX_URL}
- DAEMON_ID: zopu-agent-backend
- FLUE_DB_TOKEN: ${FLUE_DB_TOKEN}
- GITEA_TOKEN: ${GITEA_TOKEN:-}
- GITEA_URL: ${GITEA_URL:-https://git.openputer.com}
- PORT: "3000"
- RIVET_ENDPOINT: ${RIVET_ENDPOINT}
- RIVET_PUBLIC_ENDPOINT: ${RIVET_PUBLIC_ENDPOINT}
- RIVET_WORKSPACE_TOKEN: ${RIVET_WORKSPACE_TOKEN}
- networks:
- - default
- - dokploy-network
- restart: unless-stopped
-
-networks:
- dokploy-network:
- external: true
diff --git a/deploy/dokploy/frontend.compose.yml b/deploy/dokploy/frontend.compose.yml
deleted file mode 100644
index f2df55c..0000000
--- a/deploy/dokploy/frontend.compose.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-services:
- frontend:
- build:
- context: ../..
- dockerfile: apps/web/Dockerfile
- args:
- VITE_AUTH_URL: ${VITE_AUTH_URL}
- VITE_CONVEX_URL: ${VITE_CONVEX_URL}
- environment:
- HOST: 0.0.0.0
- NODE_ENV: production
- PORT: "3000"
- VITE_AUTH_URL: ${VITE_AUTH_URL}
- VITE_CONVEX_URL: ${VITE_CONVEX_URL}
- restart: unless-stopped
diff --git a/deploy/dokploy/runner.Dockerfile b/deploy/dokploy/runner.Dockerfile
deleted file mode 100644
index 9e96f73..0000000
--- a/deploy/dokploy/runner.Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-FROM oven/bun:1.3.14 AS bun
-
-FROM node:24-bookworm-slim
-
-COPY --from=bun /usr/local/bin/bun /usr/local/bin/bun
-
-RUN apt-get update \
- && apt-get install -y --no-install-recommends ca-certificates g++ git make python3 \
- && rm -rf /var/lib/apt/lists/*
-
-WORKDIR /opt/zopu-source
-
-COPY . .
-RUN bun install --frozen-lockfile
-
-ENV AGENT_WORKSPACE_ROOT=/var/lib/zopu/workspaces
-ENV BUN_EXECUTABLE=/usr/local/bin/bun
-ENV DAEMON_ID=zopu-agentos-runner
-ENV ZOPU_SOURCE_REPOSITORY=/opt/zopu-source
-
-VOLUME ["/var/lib/zopu/workspaces"]
-
-CMD ["bun", "packages/agents/src/runner.ts"]
diff --git a/deploy/dokploy/runner.compose.yml b/deploy/dokploy/runner.compose.yml
deleted file mode 100644
index fe000c9..0000000
--- a/deploy/dokploy/runner.compose.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-services:
- runner:
- build:
- context: .
- dockerfile: deploy/dokploy/runner.Dockerfile
- environment:
- AGENT_MODEL_API: ${AGENT_MODEL_API}
- AGENT_MODEL_API_KEY: ${AGENT_MODEL_API_KEY}
- AGENT_MODEL_BASE_URL: ${AGENT_MODEL_BASE_URL}
- AGENT_MODEL_CONTEXT_WINDOW: ${AGENT_MODEL_CONTEXT_WINDOW}
- AGENT_MODEL_MAX_TOKENS: ${AGENT_MODEL_MAX_TOKENS}
- AGENT_MODEL_NAME: ${AGENT_MODEL_NAME}
- AGENT_MODEL_PROVIDER: ${AGENT_MODEL_PROVIDER}
- CONVEX_URL: ${CONVEX_URL}
- DAEMON_ID: zopu-agentos-runner
- FLUE_DB_TOKEN: ${FLUE_DB_TOKEN}
- RIVET_ENDPOINT: ${RIVET_ENDPOINT}
- RIVET_PUBLIC_ENDPOINT: ${RIVET_PUBLIC_ENDPOINT}
- RIVET_WORKSPACE_TOKEN: ${RIVET_WORKSPACE_TOKEN}
- ZOPU_SOURCE_REPOSITORY: /opt/zopu-source
- volumes:
- - runner-workspaces:/var/lib/zopu/workspaces
- restart: unless-stopped
-
-volumes:
- runner-workspaces:
diff --git a/deploy/dokploy/web.Dockerfile b/deploy/dokploy/web.Dockerfile
deleted file mode 100644
index f8eac6a..0000000
--- a/deploy/dokploy/web.Dockerfile
+++ /dev/null
@@ -1,21 +0,0 @@
-FROM oven/bun:1.3.14 AS build
-
-WORKDIR /app
-
-COPY . .
-RUN bun install --frozen-lockfile
-RUN bun run --filter web build
-
-FROM node:24-bookworm-slim
-
-ENV HOST=0.0.0.0
-ENV NODE_ENV=production
-ENV PORT=3000
-
-WORKDIR /app
-
-COPY --from=build /app /app
-
-EXPOSE 3000
-
-CMD ["node", "apps/web/node_modules/.bin/react-router-serve", "apps/web/build/server/index.js"]
diff --git a/deploy/zopu-runtime/.env.template b/deploy/zopu-runtime/.env.template
deleted file mode 100644
index 10f67d8..0000000
--- a/deploy/zopu-runtime/.env.template
+++ /dev/null
@@ -1,86 +0,0 @@
-###############################################################################
-# Zopu Runtime Deployment — Environment Template
-#
-# Copy to .env and fill in real values. Never commit .env to the repository.
-# This file documents every environment group required by the single-node
-# execution plane. Lines marked REQUIRED must be set before first start.
-###############################################################################
-
-# ---------------------------------------------------------------------------
-# 1. Convex (control plane) — REQUIRED
-# Public URLs for the self-hosted Convex deployment.
-# ---------------------------------------------------------------------------
-CONVEX_URL=https://your-deployment.convex.cloud
-CONVEX_SITE_URL=https://your-deployment.convex.site
-SITE_URL=http://localhost:13100
-VITE_AUTH_URL=http://localhost:13100
-VITE_CONVEX_URL=https://your-deployment.convex.cloud
-VITE_FLUE_URL=http://localhost:3583
-VITE_ZOPU_SERVER_URL=http://localhost:3590
-
-# Self-hosted Convex origins used by convex/docker-compose.yml
-CONVEX_CLOUD_ORIGIN=https://your-deployment.convex.cloud
-CONVEX_SITE_ORIGIN=https://your-deployment.convex.site
-CONVEX_INSTANCE_NAME=zopu-production
-CONVEX_INSTANCE_SECRET=
-
-# ---------------------------------------------------------------------------
-# 2. Model gateway — REQUIRED
-# All model calls route through this OpenAI-compatible endpoint.
-# ---------------------------------------------------------------------------
-AGENT_MODEL_PROVIDER=cheaptricks
-AGENT_MODEL_NAME=glm-5.2
-AGENT_MODEL_API=openai-completions
-AGENT_MODEL_BASE_URL=https://ai.example.invalid/v1
-AGENT_MODEL_API_KEY=replace-with-model-gateway-key
-AGENT_MODEL_CONTEXT_WINDOW=262000
-AGENT_MODEL_MAX_TOKENS=131072
-
-# ---------------------------------------------------------------------------
-# 3. AgentOS / Rivet Engine — REQUIRED for the execution runner
-# The runner creates isolated worktrees from ZOPU_SOURCE_REPOSITORY and
-# connects to AgentOS through the public engine endpoint.
-# ---------------------------------------------------------------------------
-RIVET_ENDPOINT=https://default:@rivet.example.com
-RIVET_PUBLIC_ENDPOINT=https://default@rivet.example.com
-RIVET_RUNNER_VERSION=1
-RIVET_WORKSPACE_TOKEN=replace-with-a-long-random-workspace-token
-ZOPU_SOURCE_REPOSITORY=/opt/zopu-source
-# ---------------------------------------------------------------------------
-# 4. Zopu agent service (Flue)
-# FLUE_DB_TOKEN authenticates the Flue persistence adapter.
-# zopu-agent.service pins the Flue Node server to port 3583.
-# ---------------------------------------------------------------------------
-FLUE_DB_TOKEN=replace-with-long-random-token
-AGENT_BACKEND_URL=https://zopu-agent.example.com
-
-# ---------------------------------------------------------------------------
-# 5. Daemon identity
-# ---------------------------------------------------------------------------
-DAEMON_ID=zopu-dedicated
-DAEMON_NAME=Zopu-Dedicated-Server
-DAEMON_VERSION=0.0.0
-DAEMON_HEARTBEAT_MS=15000
-DAEMON_COMMAND_LEASE_MS=60000
-
-# ---------------------------------------------------------------------------
-# 7. Docker sandbox
-# The zopu service user is added to the docker group during bootstrap.
-# Orb sandboxes will use Docker for full-system isolation, but the Orb
-# lane contract has not landed yet. Docker access is provisioned now so
-# the boundary is ready; no Docker-backed sandbox code is wired today.
-# ---------------------------------------------------------------------------
-# No env vars required; Docker socket access is via group membership.
-
-# ---------------------------------------------------------------------------
-# 8. Service authentication / secrets
-# These tokens authenticate inter-service calls. Generate strong randoms.
-# ---------------------------------------------------------------------------
-# Better Auth / Convex JWT secret (if the agent service needs to mint tokens):
-#AUTH_SECRET=replace-with-64-char-hex
-
-# ---------------------------------------------------------------------------
-# 9. Private networking (Tailscale) — OPTIONAL
-# When Tailscale is available, set the hostname for private DNS.
-# ---------------------------------------------------------------------------
-#TAILSCALE_HOSTNAME=zopu-runtime
diff --git a/deploy/zopu-runtime/README.md b/deploy/zopu-runtime/README.md
deleted file mode 100644
index 305909e..0000000
--- a/deploy/zopu-runtime/README.md
+++ /dev/null
@@ -1,306 +0,0 @@
-# Zopu Single-Node Runtime Deployment
-
-Deployment artifacts for the complete Zopu stack on a single Debian dedicated server: the React Router web app, a persistent self-hosted Convex control plane, the daemon (Bun/Effect + AgentOS/RivetKit), the Flue agent service, Docker Engine, and supporting infrastructure.
-
-## Architecture
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ Debian Dedicated Server │
-│ ~12 CPU cores · ~40 GB RAM · single-node │
-│ │
-│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
-│ │ zopu-web │ │ zopu-agent │ │ Docker │ │
-│ │ (systemd) │ │ (systemd) │ │ Engine │ │
-│ │ │ │ │ │ │ │
-│ │ React Router │ │ Flue Node 22 │ │ Convex │ │
-│ │ :13100 │ │ server.mjs │ │ backend │ │
-│ │ │ │ :3583 │ │ :3210/:3211 │ │
-│ └──────┬───────┘ └──────┬───────┘ └──────────────┘ │
-│ │ │ │
-│ ┌──────▼───────┐ │
-│ │ zopu-daemon │ Bun + Effect + RivetKit :6420 │
-│ └──────────────┘ │
-│ │
-│ systemd timers: health-check (60s), docker-cleanup (daily) │
-│ cron: disk-monitor (daily 06:00) │
-│ ufw: deny-incoming, SSH + tailscale0 │
-│ Tailscale: optional private overlay │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### Single-node RivetKit topology
-
-The daemon calls `registry.start()` from `rivetkit`, which boots an **in-process RivetKit engine** (envoy mode) backed by a **native Rust sidecar** binary (`@rivet-dev/agentos-sidecar`, platform-resolved). The engine listens on `RIVET_ENDPOINT` (default `http://localhost:6420`). The daemon then calls `createClient(RIVET_ENDPOINT)` to connect back to its own in-process engine for actor dispatch.
-
-Evidence: RivetKit source `chunk-YDUQHING.js` line 4751 — `DEFAULT_ENDPOINT = "http://localhost:6420"`. The `Registry.start()` method calls `#startEnvoy()` → `runtime.serveRegistry()` for serverful mode (Mode A). The `createClient()` function reads `RIVET_ENDPOINT` env or defaults to the same `http://localhost:6420`.
-
-**No separate Rivet Engine process is required.** The engine, actor envoy, and sidecar all run inside the daemon process. A future multi-node deployment would externalize the engine, but that is out of scope.
-
-### Docker / Orb boundary
-
-Docker Engine is installed and the `zopu` service user is in the `docker` group. The daemon's systemd unit includes `SupplementaryGroups=docker`. However, **no Docker-backed sandbox code is currently wired**. The Orb sandbox lane contract has not landed; Docker access is provisioned now so the boundary is ready. The current agent uses the in-process AgentOS VM (Wasm/V8) sandbox, not Docker.
-
-## Files
-
-```
-deploy/zopu-runtime/
-├── bootstrap.sh # One-shot Debian installer
-├── .env.template # Environment template (all groups documented)
-├── README.md # This file (runbook)
-├── systemd/
-│ ├── zopu-daemon.service # Daemon systemd unit
-│ ├── zopu-agent.service # Agent systemd unit
-│ ├── zopu-web.service # React Router web systemd unit
-│ ├── zopu-health.service # Health check oneshot
-│ ├── zopu-health.timer # Health check every 60s
-│ ├── zopu-docker-cleanup.service
-│ └── zopu-docker-cleanup.timer
-├── scripts/
-│ ├── health-check.sh # TCP/process health probes
-│ ├── update.sh # Update to branch or commit
-│ ├── rollback.sh # Roll back to previous commit
-│ ├── docker-cleanup.sh # Prune stopped containers/images/networks
-│ └── disk-monitor.sh # Disk usage alerting
-└── caddy/
- └── Caddyfile # Optional reverse proxy config (documentation)
-```
-
-## Fresh install
-
-```bash
-# 1. SSH into the fresh Debian 12 server as root.
-
-# 2. Set environment overrides (optional):
-export ZOPU_REPO_URL="ssh://git@git.openputer.com:2222/puter/zopu-code.git"
-export ZOPU_REPO_BRANCH="dogfood/v0"
-# export TAILSCALE_AUTHKEY="tskey-..."
-# export TAILSCALE_HOSTNAME="zopu-runtime"
-
-# 3. Run the bootstrap script:
-bash bootstrap.sh
-
-# 4. Edit .env with real values:
-nano /opt/zopu/.env
-
-# 5. Start services:
-systemctl start zopu-web zopu-daemon
-sleep 3
-systemctl start zopu-agent
-
-# 6. Enable timers:
-systemctl enable --now zopu-health.timer zopu-docker-cleanup.timer
-
-# 7. Verify:
-/opt/zopu/deploy/zopu-runtime/scripts/health-check.sh
-```
-
-## Start / stop / restart
-
-```bash
-# Start all services
-systemctl start zopu-web zopu-daemon zopu-agent
-
-# Stop all services
-systemctl stop zopu-agent zopu-daemon zopu-web
-
-# Restart (daemon first — it owns the RivetKit engine)
-systemctl restart zopu-web zopu-daemon && sleep 3 && systemctl restart zopu-agent
-
-# Enable on boot
-systemctl enable zopu-web zopu-daemon zopu-agent
-
-# Disable on boot
-systemctl disable zopu-web zopu-daemon zopu-agent
-```
-
-## Log inspection
-
-All service logs go to journald with `SyslogIdentifier` tags.
-
-```bash
-# Daemon logs (live follow)
-journalctl -u zopu-daemon -f
-
-# Agent logs (live follow)
-journalctl -u zopu-agent -f
-
-# Last 100 lines of daemon
-journalctl -u zopu-daemon -n 100
-
-# Logs since boot
-journalctl -u zopu-daemon -b
-
-# Health check timer logs
-journalctl -u zopu-health.service -n 50
-
-# Docker cleanup logs
-journalctl -u zopu-docker-cleanup.service -n 50
-
-# Disk monitor logs (cron → file)
-tail -100 /var/log/zopu/disk-monitor.log
-
-# All Zopu syslog identifiers
-journalctl -t zopu-daemon -t zopu-agent --since "1 hour ago"
-```
-
-## Health checks
-
-```bash
-# Manual health check (prints all probes)
-/opt/zopu/deploy/zopu-runtime/scripts/health-check.sh
-
-# Quiet mode (exit code only)
-/opt/zopu/deploy/zopu-runtime/scripts/health-check.sh --quiet
-
-# Check systemd timer is running
-systemctl status zopu-health.timer
-systemctl list-timers zopu-health.timer
-```
-
-The health check probes:
-
-1. `zopu-daemon` systemd unit is active
-2. `zopu-agent` systemd unit is active
-3. RivetKit engine port (default 6420) accepts TCP connections
-4. Flue agent port (default 3583) accepts TCP connections
-5. Docker daemon responds to `docker info`
-
-No HTTP health endpoints are assumed. Flue does not expose one by design (per Flue docs: "Flue does not add a health endpoint"). RivetKit's health route is internal to the registry runtime and not documented as publicly addressable on the engine endpoint.
-
-## Update to commit
-
-```bash
-# Update to latest of dogfood/v0 (default)
-/opt/zopu/deploy/zopu-runtime/scripts/update.sh
-
-# Update to a specific branch
-/opt/zopu/deploy/zopu-runtime/scripts/update.sh dogfood/runtime-deploy
-
-# Update to a specific commit
-/opt/zopu/deploy/zopu-runtime/scripts/update.sh abc123def456
-```
-
-The update script:
-
-1. Records current HEAD to `.last-deployed-sha`
-2. Fetches, resolves branch-or-commit, checks out
-3. `bun install`, builds daemon and agent
-4. Restarts daemon, waits, restarts agent
-5. Runs health check; reports failure and rollback instructions
-
-## Rollback
-
-```bash
-# Roll back to the previously deployed commit
-/opt/zopu/deploy/zopu-runtime/scripts/rollback.sh
-
-# Roll back to a specific commit
-/opt/zopu/deploy/zopu-runtime/scripts/rollback.sh abc123def456
-```
-
-Rollback reads `.last-deployed-sha` (written by `update.sh`), checks out that commit, rebuilds, and restarts services. The pre-rollback SHA is saved to `.pre-rollback-sha` for re-rollback if needed.
-
-## Docker cleanup
-
-```bash
-# Manual cleanup
-/opt/zopu/deploy/zopu-runtime/scripts/docker-cleanup.sh
-
-# Check Docker disk usage
-docker system df
-
-# Timer runs daily; check its schedule
-systemctl list-timers zopu-docker-cleanup.timer
-```
-
-Cleanup prunes:
-
-- Stopped containers older than 24 hours
-- Dangling (untagged) images
-- Unused networks
-
-Named volumes and running containers are never removed.
-
-## Disk-space monitoring
-
-```bash
-# Manual check
-/opt/zopu/deploy/zopu-runtime/scripts/disk-monitor.sh
-
-# Custom threshold (90%)
-/opt/zopu/deploy/zopu-runtime/scripts/disk-monitor.sh --warn-percent 90
-```
-
-A cron job runs at 06:00 daily and writes to `/var/log/zopu/disk-monitor.log`. Default alert threshold is 80%.
-
-## Firewall and private networking
-
-The firewall (`ufw`) is deny-by-default:
-
-- SSH (port 22) is allowed on all interfaces
-- All traffic on `tailscale0` is allowed (Tailscale private overlay)
-- All other incoming traffic is denied
-
-The RivetKit engine (`:6420`) and Flue agent (`:3583`) ports are **not** exposed on public interfaces. Reachability options:
-
-1. **Tailscale** (recommended): bootstrap runs `ufw allow in on tailscale0` so all ports are reachable over the private overlay. Set `TAILSCALE_AUTHKEY` before running bootstrap to configure automatically. Other Tailscale-connected machines can reach the agent at `http://zopu-runtime:3583` and the engine at `http://zopu-runtime:6420`.
-2. **Custom private interface**: if you have a non-Tailscale private network (e.g. a VLAN or wireguard interface), add an explicit rule:
- ```bash
- ufw allow in on eth1 # or your private interface name
- ```
- Do NOT assume direct private IP access works by default — the deny-incoming policy blocks it until an interface-specific rule is added.
-3. **Caddy** (optional): install Caddy and use the annotated Caddyfile in `caddy/` if you need TLS termination or a public ingress point.
-
-## Environment groups
-
-See [`.env.template`](./.env.template) for the full annotated template. The eight required groups:
-
-| Group | Variables |
-| --- | --- |
-| Convex | `CONVEX_URL`, `CONVEX_SITE_URL`, `SITE_URL` |
-| Gitea | `GITEA_URL`, `GITEA_TOKEN` |
-| Model gateway | `AGENT_MODEL_*` |
-| AgentOS/RivetKit | `RIVET_ENDPOINT` (optional) |
-| Zopu agent | `FLUE_DB_TOKEN` (`zopu-agent.service` sets port 3583) |
-| Daemon | `DAEMON_ID`, `DAEMON_NAME`, `DAEMON_VERSION`, `DAEMON_HEARTBEAT_MS`, `DAEMON_COMMAND_LEASE_MS` |
-| Docker sandbox | group membership (no env vars) |
-| Service auth | `AUTH_SECRET` (if needed) |
-
-## Public single-node routes
-
-The checked-in Caddy example assumes Cloudflare Tunnel terminates TLS and sends the four Zopu hosts to Caddy on loopback:
-
-- `zopu.sai-onchain.me` → React Router web app on `127.0.0.1:13100`
-- `zopu-api.sai-onchain.me` → Convex API on `127.0.0.1:3210`
-- `zopu-site.sai-onchain.me` → Convex HTTP actions on `127.0.0.1:3211`
-- `zopu-agent.sai-onchain.me` → Flue on `127.0.0.1:3583`
-
-Self-hosted Convex state lives in the `zopu-convex-data` Docker volume. Generate the CLI admin key after the backend is healthy:
-
-```bash
-docker compose \
- --env-file /opt/zopu/.env \
- -f /opt/zopu/deploy/zopu-runtime/convex/docker-compose.yml \
- exec backend ./generate_admin_key.sh
-```
-
-## What is NOT deployed
-
-- **Kubernetes**: no container orchestration.
-- **PostgreSQL for Rivet**: the in-process RivetKit engine uses its own storage; no external PostgreSQL is required.
-- **Multi-node coordination**: single-node only.
-- **Public administration endpoints**: no admin HTTP surface.
-- **Secrets in source**: `.env` is never committed; `.env.template` contains only placeholder values.
-- **Docker-backed Orb sandboxes**: Docker is installed and access is provisioned, but no Orb sandbox code is wired. This is a boundary prepared for the Orb lane, not a working feature.
-
-## Reproducibility
-
-The deployment does not require the developer's MacBook to remain online. Once bootstrap completes and `.env` is filled in:
-
-1. Services run under systemd with `Restart=always`.
-2. Logs persist in journald.
-3. Health checks run every 60 seconds via systemd timer.
-4. Docker cleanup runs daily.
-5. Disk usage is monitored daily.
-6. Unattended-upgrades handles Debian security patches.
diff --git a/deploy/zopu-runtime/bootstrap.sh b/deploy/zopu-runtime/bootstrap.sh
deleted file mode 100755
index ec9d30e..0000000
--- a/deploy/zopu-runtime/bootstrap.sh
+++ /dev/null
@@ -1,290 +0,0 @@
-#!/usr/bin/env bash
-#
-# bootstrap.sh — One-shot installer for the Zopu single-node execution plane.
-#
-# Run as root on a fresh Debian 12 host:
-#
-# bash bootstrap.sh
-#
-# Environment overrides (set before running):
-# ZOPU_REPO_URL — SSH clone URL (default: ssh://git@git.openputer.com:2222/puter/zopu-code.git)
-# ZOPU_REPO_BRANCH — branch to deploy (default: dogfood/v0)
-# ZOPU_INSTALL_DIR — install path (default: /opt/zopu)
-# ZOPU_SERVICE_USER — system user (default: zopu)
-# TAILSCALE_AUTHKEY — if set, configure Tailscale
-# TAILSCALE_HOSTNAME — Tailscale hostname (default: zopu-runtime)
-#
-# Installs: Docker Engine, Node.js 22, Bun, clones the repo, runs bun install, builds the
-# web app, daemon, and agent, creates a non-root service user, installs systemd
-# units, and configures firewall/Tailscale defaults.
-
-set -euo pipefail
-
-# ---------------------------------------------------------------------------
-# Configuration
-# ---------------------------------------------------------------------------
-REPO_URL="${ZOPU_REPO_URL:-ssh://git@git.openputer.com:2222/puter/zopu-code.git}"
-REPO_BRANCH="${ZOPU_REPO_BRANCH:-dogfood/v0}"
-INSTALL_DIR="${ZOPU_INSTALL_DIR:-/opt/zopu}"
-SERVICE_USER="${ZOPU_SERVICE_USER:-zopu}"
-DEPLOY_DIR="${INSTALL_DIR}/deploy/zopu-runtime"
-
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-RED='\033[0;31m'
-NC='\033[0m'
-
-log() { echo -e "${GREEN}[bootstrap]${NC} $*"; }
-warn() { echo -e "${YELLOW}[bootstrap]${NC} $*"; }
-err() { echo -e "${RED}[bootstrap]${NC} $*" >&2; }
-
-# runuser is part of util-linux (essential on Debian) and always available.
-# sudo is NOT assumed on minimal Debian installs.
-run_as_service() {
- runuser -u "$SERVICE_USER" -- "$@"
-}
-
-# ---------------------------------------------------------------------------
-# Pre-flight
-# ---------------------------------------------------------------------------
-if [[ "$EUID" -ne 0 ]]; then
- err "This script must be run as root."
- exit 1
-fi
-
-if [[ -f /etc/debian_version ]]; then
- log "Detected Debian $(cat /etc/debian_version)"
-else
- warn "This script targets Debian 12. Other distributions may need manual adjustments."
-fi
-
-# ---------------------------------------------------------------------------
-# 1. System packages
-# ---------------------------------------------------------------------------
-log "Updating apt and installing base packages..."
-apt-get update -y
-apt-get install -y \
- ca-certificates \
- curl \
- gnupg \
- ufw \
- git \
- jq \
- netcat-openbsd \
- openssh-client \
- unattended-upgrades \
- rsyslog
-
-# ---------------------------------------------------------------------------
-# 2. Docker Engine
-# ---------------------------------------------------------------------------
-if ! command -v docker &>/dev/null; then
- log "Installing Docker Engine..."
- install -m 0755 -d /etc/apt/keyrings
- curl -fsSL https://download.docker.com/linux/debian/gpg \
- -o /etc/apt/keyrings/docker.asc
- chmod a+r /etc/apt/keyrings/docker.asc
-
- echo \
- "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
- https://download.docker.com/linux/debian \
- $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
- > /etc/apt/sources.list.d/docker.list
-
- apt-get update -y
- apt-get install -y \
- docker-ce \
- docker-ce-cli \
- containerd.io \
- docker-buildx-plugin \
- docker-compose-plugin
-else
- log "Docker Engine already installed: $(docker --version)"
-fi
-
-systemctl enable --now docker
-
-# ---------------------------------------------------------------------------
-# 3. Node.js 22 (required by the Flue Node target)
-# ---------------------------------------------------------------------------
-NODE_MAJOR=$(node --version 2>/dev/null | sed -n 's/^v\([0-9][0-9]*\).*/\1/p')
-if [[ -z "$NODE_MAJOR" || "$NODE_MAJOR" -lt 22 ]]; then
- log "Installing Node.js 22..."
- curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
- bash /tmp/nodesource_setup.sh
- apt-get install -y nodejs
-else
- log "Node.js already installed: $(node --version)"
-fi
-
-# ---------------------------------------------------------------------------
-# 4. Bun
-# ---------------------------------------------------------------------------
-if ! command -v bun &>/dev/null; then
- log "Installing Bun..."
- curl -fsSL https://bun.sh/install | bash
- install -m 0755 /root/.bun/bin/bun /usr/local/bin/bun
-else
- log "Bun already installed: $(bun --version)"
-fi
-
-# ---------------------------------------------------------------------------
-# 5. Service user
-# ---------------------------------------------------------------------------
-if ! id "$SERVICE_USER" &>/dev/null; then
- log "Creating service user: $SERVICE_USER"
- useradd -r -m -d "/home/$SERVICE_USER" -s /bin/bash "$SERVICE_USER"
-fi
-
-if ! id -nG "$SERVICE_USER" | grep -qw docker; then
- usermod -aG docker "$SERVICE_USER"
- log "Added $SERVICE_USER to docker group"
-fi
-
-# ---------------------------------------------------------------------------
-# 6. Clone or update repository
-# ---------------------------------------------------------------------------
-if [[ -d "$INSTALL_DIR/.git" ]]; then
- log "Repository exists at $INSTALL_DIR, fetching latest..."
- cd "$INSTALL_DIR"
- git fetch origin
- git checkout "$REPO_BRANCH"
- git reset --hard "origin/$REPO_BRANCH"
-else
- log "Cloning $REPO_URL (branch $REPO_BRANCH) into $INSTALL_DIR..."
- git clone --branch "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR"
- cd "$INSTALL_DIR"
-fi
-
-# ---------------------------------------------------------------------------
-# 6b. Hand ownership of the checkout to the service user
-# ---------------------------------------------------------------------------
-log "Setting ownership of $INSTALL_DIR to $SERVICE_USER..."
-chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR"
-
-# ---------------------------------------------------------------------------
-# 7. Install dependencies and build
-# ---------------------------------------------------------------------------
-log "Running bun install..."
-run_as_service bun install
-
-log "Building web app..."
-run_as_service bun run --cwd apps/web build
-
-log "Validating daemon production build..."
-run_as_service bun run build:daemon
-
-log "Building agent service..."
-run_as_service bun run build:agents
-
-# ---------------------------------------------------------------------------
-# 8. Environment file
-# ---------------------------------------------------------------------------
-ENV_FILE="$INSTALL_DIR/.env"
-if [[ ! -f "$ENV_FILE" ]]; then
- log "Copying .env.template to .env — EDIT BEFORE STARTING SERVICES"
- cp "$DEPLOY_DIR/.env.template" "$ENV_FILE"
- chown "$SERVICE_USER":"$SERVICE_USER" "$ENV_FILE"
- chmod 600 "$ENV_FILE"
- warn "Edit $ENV_FILE with real values before starting services."
-else
- log ".env already exists at $ENV_FILE"
- # Ensure correct ownership and permissions on existing .env
- chown "$SERVICE_USER":"$SERVICE_USER" "$ENV_FILE"
- chmod 600 "$ENV_FILE"
-fi
-
-# ---------------------------------------------------------------------------
-# 9. Persistent log directory
-# ---------------------------------------------------------------------------
-LOG_DIR="/var/log/zopu"
-mkdir -p "$LOG_DIR"
-chown "$SERVICE_USER":"$SERVICE_USER" "$LOG_DIR"
-
-# ---------------------------------------------------------------------------
-# 10. Install systemd units (substitute placeholders)
-# ---------------------------------------------------------------------------
-log "Installing systemd units..."
-for unit in zopu-web.service zopu-daemon.service zopu-agent.service \
- zopu-health.timer zopu-health.service \
- zopu-docker-cleanup.timer zopu-docker-cleanup.service; do
- SRC="$DEPLOY_DIR/systemd/$unit"
- DST="/etc/systemd/system/$unit"
- if [[ -f "$SRC" ]]; then
- sed \
- -e "s|__INSTALL_DIR__|$INSTALL_DIR|g" \
- -e "s|__SERVICE_USER__|$SERVICE_USER|g" \
- "$SRC" > "$DST"
- log " installed $unit"
- fi
-done
-systemctl daemon-reload
-
-# ---------------------------------------------------------------------------
-# 11. Firewall (deny-by-default, explicit allow for Tailscale)
-# ---------------------------------------------------------------------------
-log "Configuring firewall..."
-if ! ufw status 2>/dev/null | grep -q "Status: active"; then
- ufw allow 22/tcp
- ufw default deny incoming
- ufw default allow outgoing
-
- # Allow all traffic on the Tailscale interface (if present)
- # This lets the agent and engine ports be reached over the private overlay.
- ufw allow in on tailscale0 || warn "tailscale0 not present yet; rule will activate when interface appears"
-
- ufw --force enable
- log "Firewall enabled: SSH (22) allowed, tailscale0 allowed."
- warn "Agent and RivetKit ports are NOT exposed on public interfaces."
- warn "Reachability is via Tailscale (tailscale0) only."
-else
- log "Firewall already active. Ensuring tailscale0 rule..."
- ufw allow in on tailscale0 2>/dev/null || true
-fi
-
-# ---------------------------------------------------------------------------
-# 12. Tailscale (optional)
-# ---------------------------------------------------------------------------
-if [[ -n "${TAILSCALE_AUTHKEY:-}" ]]; then
- log "Installing and configuring Tailscale..."
- if ! command -v tailscaled &>/dev/null; then
- curl -fsSL https://tailscale.com/install.sh | sh
- fi
- tailscale up --authkey "$TAILSCALE_AUTHKEY" \
- --hostname "${TAILSCALE_HOSTNAME:-zopu-runtime}" \
- --accept-routes
- log "Tailscale configured: $(tailscale ip -4 2>/dev/null || echo 'waiting for IP')"
-
- # Re-apply the tailscale0 firewall rule now that the interface exists
- ufw allow in on tailscale0 2>/dev/null || true
-else
- warn "TAILSCALE_AUTHKEY not set — skipping Tailscale setup."
- warn "Without Tailscale, services are reachable only via localhost."
- warn "To use a private network interface, add an explicit UFW rule:"
- warn " ufw allow in on