reconnecting

Agent Provisioning Process

Each step of a hands-off agent provision, across Solomon and deploy.enteracloud.com. The two sides run interleaved in time; the numbers below reflect the temporal order observed end-to-end on agent210 (clone → online → chat-ready in ~3 min).

LEGENDOperatorSolomonDeploy APINew Agent
1
Operator

POST /api/provision/agents

Operator (UI or API caller) submits the new-agent request to Solomon. Required body fields: name. Optional: friendly_name, ip_mode (default static), static_ip, model (default zai/glm-5.1), and any vCenter overrides (host/datastore/folder/network/template_name/cpu/ram_mb/disk_gb).

POST /api/provision/agents
{ "name": "clw-agent213",
  "ip_mode": "static",
  "static_ip": "10.8.4.213" }
2
Solomon

Mint records: Agent + Server + BootstrapToken

Solomon creates the Agent row (status=OFFLINE, gateway_token = random 32-byte URL-safe), a placeholder Server row (ip_address starts empty), and a one-time BootstrapToken (30-minute TTL) that the new VM will redeem to fetch its install script.

3
Solomon

Call deploy.enteracloud.com (clean: post_script="")

Solomon calls the external deploy API with all vCenter parameters and an EMPTY post_script. Empty post_script avoids racing with vCenter guest customization (the step that applies the static IP); bootstrap is done from Solomon afterward instead.

host=192.168.8.101 ds=101-nvme1
folder=clw-804 network=DPortGroup804
template=tpl-linux ip_mode=static
static_ip=<x> static_gateway=10.8.4.1
static_dns="1.1.1.1, 8.8.8.8"
post_script=""
4
Solomon

Return to caller; spawn hub-bootstrap thread

Solomon responds 200 { agent_id, deploy_id, vm_name, bootstrap_id } and starts _hub_bootstrap_vm on a background thread. Subsequent steps happen asynchronously.

5
Deploy API

Clone template on vCenter

Deploy connects to vCenter, locates the template (tpl-linux) and clones it to the new VM on the target host (192.168.8.101) and datastore (101-nvme1), placed in folder clw-804 on DPortGroup804.

6
Deploy API

Apply guest customization (static IP + DNS)

Deploy prepares and applies vCenter guest customization: static IP, mask 255.255.255.0, gateway, DNS. Customization runs at VM first boot via VMware tools; it may include a network restart or guest reboot.

7
New Agent

VM boots; vmware-tools applies the static IP

VM powers on. cloud-init / vmware-tools applies the network config inside the guest. When the interface (ens33) gets its static IP, the VM is ARP-reachable on the LAN. This is the step that intermittently flakes in our environment — see Known failure modes below.

8
Deploy API

Install SSH key via proxy (guest-ops)

Deploy reaches the VM via its proxy (VMware guest operations) and installs an SSH key for admin1. With post_script empty this is the deploy's only intervention on the new VM. Marks deployment complete.

9
Solomon

hub-bootstrap thread polls SSH at static_ip

Solomon's _hub_bootstrap_vm thread polls SSH on the new VM's static IP (admin1@<static_ip>) every 15s for up to 10 minutes. Once a session opens cleanly, it proceeds.

10
Solomon

Fetch install.sh via the one-time bootstrap URL

Hub SSHes in and runs curl against /api/provision/bootstrap/<bootstrap_id>/install.sh, then pipes the script to sudo bash. Fetching the URL consumes the token (it is then 410 Gone for any subsequent fetch).

curl -fsSL --connect-timeout 15 --max-time 600 \
  https://solomon/api/provision/bootstrap/<id>/install.sh \
  -o /tmp/sa-install.sh
sudo bash /tmp/sa-install.sh
11
New Agent

install.sh: prereqs → clone → venv → daemon

On the VM: apt install python3-venv git curl, clone solomon-agent (falling back to the hub-served tarball if the GitLab clone fails), python venv + pip install requirements, write /etc/solomon-agent/agent.env (with SOLOMON_URL + SOLOMON_TOKEN + OPENCLAW_BIN), install the solomon CLI to /usr/local/bin/solomon, install + enable + start the solomon-agent.service systemd unit.

12
New Agent

Daemon dials home (WebSocket)

solomon_agent.py connects to ws://10.8.4.220:8000/api/agents/ws?token=<gateway_token>. The hub's ConnectionManager accepts it and flips agent.status to ONLINE.

13
Solomon

_configure_openclaw_vm: install Node + openclaw

Hub SSHes in again (idempotent) and installs Node 22 from NodeSource if missing, then npm install -g openclaw --prefix /home/admin1/.npm-global (matching OPENCLAW_BIN).

14
New Agent

openclaw onboard --non-interactive --accept-risk --mode local

Creates ~/.openclaw/openclaw.json with baseline config, the workspace, sessions folder, and the default "main" agent that the solomon-agent daemon dispatches into.

15
Solomon

openclaw config patch (per-provider, from OPENCLAW_PROVIDERS)

Hub builds a JSON5 patch from the OPENCLAW_PROVIDERS registry based on the agent's model (e.g. zai/glm-5.1 → zai provider def + agents.defaults.model.primary). It is delivered to the VM via base64 and applied with openclaw config patch --file.

{ models: { mode: "merge",
    providers: { zai: { baseUrl, api, models } } },
  agents: { defaults: { model: { primary: "zai/glm-5.1" } } } }
16
Solomon

openclaw models auth paste-token (key via stdin)

Hub reads the provider API key from Solomon's vault (e.g. api/zai.api_key) and pipes it into paste-token over the SSH stdin. The key never appears in argv, server logs, or the JSON config in cleartext.

openclaw models auth paste-token \
  --provider zai --profile-id zai:default
  # token read from stdin
17
New Agent

openclaw config validate

Final sanity check on the assembled config; returns Config valid: ~/.openclaw/openclaw.json.

18
New Agent

Chat-ready: openclaw agent --agent main returns model output

A turn dispatched from Solomon flows: hub → daemon WS → solomon_agent.run_openclaw_turn → openclaw agent --agent main --message <prompt> --json → provider (e.g. zai) → JSON result with finalAssistantVisibleText. End-to-end demonstrated on clw-agent210 hands-off (~3 min from POST to first model response).

Known failure modes

Customization race: static IP never applies
VM clones, deploy reports complete, but the new VM does not ARP-announce its static IP. ip neigh shows INCOMPLETE. Caused by vCenter guest customization racing with the deploy's SSH-key-install guest-ops step. Empty post_script (current default) reduced but did not eliminate this. Workaround: destroy and re-deploy. Permanent fix would be at the deploy layer (have it wait for customization before any guest-ops).
Token unfetched after VM up
The VM is SSH-reachable but the hub-bootstrap curl never ran. Look at journalctl -u openclaw-hub for hub-bootstrap log lines; check the VM's /tmp/sa-install.sh existence and bootstrap_tokens.fetched flag in the DB.
install.sh ran but daemon offline
Check systemctl status solomon-agent on the VM. Common causes: bad SOLOMON_URL or SOLOMON_TOKEN in /etc/solomon-agent/agent.env, or the daemon's WebSocket connect failing (hub down, wrong port).
Chat fails with ENOENT on OPENCLAW_BIN
The agent is online but openclaw is not installed or configured. Re-run _configure_openclaw_vm or, equivalently, run the openclaw install/onboard/config patch/paste-token/validate sequence over SSH. Look at hub journal for openclaw-config: lines.
paste-token says profile created but turn errors with auth
Provider entry missing or mismatched in OPENCLAW_PROVIDERS. Check the model id format (provider/model_id) and that the vault has a key at the registry's vault_path. openclaw models list should show the model with Auth: yes.

Endpoint reference

POST /api/provision/agents — create + deploy + bootstrap
GET /api/provision/agents/<id>/status — poll deploy + connection state
GET /api/provision/bootstrap/<id>/install.sh — one-time install fetch
POST /api/provision/destroy — vCenter destroy + DB cleanup (auto-provisioned only)
GET /api/provision/solomon-agent.tar.gz — tarball fallback when GitLab clone fails
Provider registry: OPENCLAW_PROVIDERS in provision.py. Vault paths: api/<provider>.api_key.