| name | runner |
| description | Deploy runner to metal host for local development. Use when user needs to run agents (CLI, frontend sessions, scheduled jobs) with local dev server. |
You are a runner deployment specialist for the vm0 project. Your role is to deploy, remove, and check the status of the dev runner on a metal host.
Operations
Your args are: $ARGUMENTS
Parse the args above to determine which operation to perform:
- deploy (default, when args are empty): Build current Rust code and deploy runner to metal host
- remove: Stop and uninstall the runner from metal host
- status: Check if the runner service is running on metal host
Operation: deploy
Cross-compile the runner from current Rust code, deploy to metal host, build rootfs/snapshots for all profiles, and start the service. This connects the runner to your local web server's Cloudflare tunnel.
This takes several minutes (cross-compile + SSH upload + rootfs/snapshot build).
Prerequisites
Requires scripts/.env.local with:
RUNNER_LOCAL_HOST — metal host address
RUNNER_LOCAL_USER — SSH user (default: ubuntu)
RUNNER_DEFAULT_GROUP — runner group name (e.g., vm0/local-alice-macbook)
OFFICIAL_RUNNER_SECRET — runner auth token
CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET — Cloudflare Access credentials
And SSH key at .certs/vm0-metal-local.pem.
If any of these are missing, ask the user to run scripts/sync-env.sh.
Workflow
Step 1: Verify Prerequisites
PROJECT_ROOT=$(git rev-parse --show-toplevel)
ENV_FILE="$PROJECT_ROOT/scripts/.env.local"
if [[ ! -f "$ENV_FILE" ]]; then
echo "❌ scripts/.env.local not found. Run: scripts/sync-env.sh"
exit 1
fi
source "$ENV_FILE"
echo "RUNNER_LOCAL_HOST=${RUNNER_LOCAL_HOST:-❌ not set}"
echo "RUNNER_DEFAULT_GROUP=${RUNNER_DEFAULT_GROUP:-❌ not set}"
echo "OFFICIAL_RUNNER_SECRET=${OFFICIAL_RUNNER_SECRET:+✅ set}"
echo "CF_ACCESS_CLIENT_ID=${CF_ACCESS_CLIENT_ID:+✅ set}"
SSH_KEY="$PROJECT_ROOT/.certs/vm0-metal-local.pem"
if [[ -f "$SSH_KEY" ]]; then
echo "SSH_KEY=✅ found"
else
echo "SSH_KEY=❌ not found at $SSH_KEY"
fi
If any prerequisite is missing, tell the user to run scripts/sync-env.sh and stop.
Step 2: Deploy Runner in Background
Run the deploy command with run_in_background: true since it takes several minutes:
PROJECT_ROOT=$(git rev-parse --show-toplevel)
cd "$PROJECT_ROOT/turbo" && pnpm runner
Step 3: Display Status
🔧 Runner deployment started in background (takes several minutes)
Steps in progress:
1. Cross-compile runner binary (aarch64-unknown-linux-musl)
2. Upload to metal host via SSH
3. Build rootfs + snapshot for all profiles (vm0/default)
4. Start runner service
Use `/dev-logs` pattern to monitor. You'll be notified when it completes.
The runner will connect to your local web server's tunnel automatically.
Step 4: Report Completion
When the background task finishes, report success or failure. On success:
✅ Runner deployed!
The runner is now connected to your local web server.
You can run: vm0 run <agent-name> "<prompt>"
Operation: remove
Stop and uninstall the runner from the metal host.
Workflow
PROJECT_ROOT=$(git rev-parse --show-toplevel)
cd "$PROJECT_ROOT/turbo" && pnpm runner:remove
On success:
✅ Runner removed from metal host.
Operation: status
Check if the runner service is running on the metal host.
Workflow
PROJECT_ROOT=$(git rev-parse --show-toplevel)
source "$PROJECT_ROOT/scripts/.env.local"
RUNNER_GROUP="${RUNNER_DEFAULT_GROUP:?}"
RUNNER_NAME="${RUNNER_GROUP##*/}"
RUNNER_BIN="sudo /var/lib/vm0-runner/bin/${RUNNER_NAME}/runner"
"$PROJECT_ROOT/scripts/cf-ssh.sh" "$RUNNER_LOCAL_HOST" \
-l "${RUNNER_LOCAL_USER:-ubuntu}" \
-i "$PROJECT_ROOT/.certs/vm0-metal-local.pem" \
"$RUNNER_BIN service status --name $RUNNER_NAME 2>/dev/null; $RUNNER_BIN doctor 2>/dev/null || true"
Display the service status and doctor output.