SSH host tests
Verify the SSH Compute Driver against a disposable Linux host with real OpenClaw and systemd. The default real-host run checks readiness only; the model selector below adds provider execution.
Local conformance and startup
Run these checks without a disposable host:
node --test tests/conformance/ssh-compute.test.mjsnode --test tests/integration/ssh-compute-startup.test.mjsThey execute the actual helper with transport, account-management, and systemd fixtures. See local conformance boundaries for what those fixtures cover. Use the real-host suite below to verify SSH, systemd, and OS account isolation on the selected disposable host.
SSH raw hosts
The checks-baseline CI lane runs SSH conformance and startup coverage. The
ssh-host lane selects the real-host test with required operator-provided SSH
settings. It is not part of the ci or full groups because those jobs do not
provision an SSH host.
Prepare the disposable rig below before selecting this lane; missing inputs or
skipped tests fail the lane.
The bundled SSH Compute Driver has an opt-in
real-host integration. Use a disposable Linux systemd host only. The test
checks gateway readiness, two-revision cutover, state persistence, retirement,
and Namespace deletion over real SSH. It also checks distinct Agent UID/GID
assignments and sibling state/configuration read denial using Linux runuser. It makes no model call and needs no
model credential. The ordinary local test command reports an explicit skip:
node --test tests/integration/ssh-compute-real.test.mjsThe fixture image builds on the runtime image's docker.io/library/node:24-bookworm base and
adds systemd as PID 1, sshd, an openclaw system user, and the pinned
OpenClaw/Codex assembly from
deploy/runtime/Dockerfile, including the same
workspace templates as the Console. Build that runtime image first; it builds on amd64
and arm64. Start it on a Docker Engine with privileged systemd/cgroup support
(Docker Desktop on Apple silicon works). This privileged container is a
disposable test rig, not production packaging. If the Engine cannot run
systemd, use an explicitly selected disposable Linux VM instead; do not
substitute the conformance fixture and call it host proof.
SSH_RIG=$(mktemp -d)chmod 700 "$SSH_RIG"ssh-keygen -q -t ed25519 -N '' -f "$SSH_RIG/id_ed25519"docker build -f deploy/runtime/Dockerfile -t localhost/openclaw-runtime:local .docker build --build-arg RUNTIME_IMAGE=localhost/openclaw-runtime:local \ -t oce-ssh-host:local tests/fixtures/ssh-compute/hostdocker run -d --name oce-ssh-host --privileged --cgroupns=host \ --tmpfs /run --tmpfs /run/lock \ -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ --mount "type=bind,src=$SSH_RIG/id_ed25519.pub,dst=/run/occ-authorized_keys,readonly" \ -p 127.0.0.1:22222:22 oce-ssh-host:localdocker exec oce-ssh-host install -o root -g root -m 600 \ /run/occ-authorized_keys /root/.ssh/authorized_keysdocker exec oce-ssh-host systemctl is-active sshdocker exec oce-ssh-host cat /etc/ssh/ssh_host_ed25519_key.pub \ | awk '{ print "[127.0.0.1]:22222 " $1 " " $2 }' > "$SSH_RIG/known_hosts"chmod 600 "$SSH_RIG/known_hosts"The read-only /run/occ-authorized_keys mount is the fixture's public-key
input; copying it gives sshd's /root/.ssh/authorized_keys the required root
ownership and mode. Root login permits keys only (PermitRootLogin prohibit-password). The known-host entry above comes directly from this
task-owned container, without disabling strict host-key verification. Wait for
systemctl is-active ssh to report active before selecting the suite.
OCC_TEST_SSH_REAL=1 \OCC_TEST_SSH_ADDRESS=127.0.0.1 \OCC_TEST_SSH_PORT=22222 \OCC_TEST_SSH_USER=root \OCC_TEST_SSH_IDENTITY_FILE="$SSH_RIG/id_ed25519" \OCC_TEST_SSH_KNOWN_HOSTS_FILE="$SSH_RIG/known_hosts" \OCC_TEST_SSH_NODE_PATH=/usr/local/bin/node \OCC_TEST_SSH_OPENCLAW_PATH=/opt/openclaw/current/dist/index.js \OCC_TEST_SSH_RUNTIME_USER=openclaw \OCC_TEST_SSH_ROOT=/var/lib/openclaw-enterprise \OCC_TEST_SSH_UNIT_DIRECTORY=/etc/systemd/system \node --test tests/integration/ssh-compute-real.test.mjsThe gateway port range is 18800–18899; it is checked on the host through
SSH and does not need a published container port. The suite creates unique
Namespace and Agent identities and removes only its Namespace and units. When
selected, missing settings, unreachable SSH, failed systemd, or unready OpenClaw
fail the test. Successful local conformance or an unselected skip does not
establish real-host proof; rerun this suite after changing the Driver, helper,
or fixture.
After testing, remove only the rig and its generated keys:
docker rm -f oce-ssh-hostrm -r "$SSH_RIG"See SSH test settings
for every input and default. Inspect the Agent's exact unit with journalctl -u
inside the container when readiness fails; keep logs free of credential values.
Runtime credential model proof
With the disposable SSH host inputs above, also set OCC_TEST_SSH_MODEL=1,
OCC_TEST_OPENAI_API_KEY_FILE to a protected local file containing an authorized
OpenAI API key, and optionally OCC_TEST_OPENAI_MODEL (default gpt-6-astra). Run the
same tests/integration/ssh-compute-real.test.mjs file. This selector requires
the SSH inputs and fails if the host or credential is unavailable; it never
substitutes the local systemd fixture.
The test checks the key/model against the provider first, then provisions the
host's protected <agentDir>/env as an operator via SSH stdin. It deploys with
{ "method": "runtime" }, verifies readiness and a real model turn, and counts
provider requests through a loopback forwarding server. Startup and readiness
must make zero model requests. After replacing the key with a synthetic invalid
one and restarting the existing revision, readiness must still succeed while a
real model request fails. Redeployment, retirement, and stop must preserve the
operator file's bytes, mode, inode, and modification time. Cleanup stops the
forwarder and deletes only the test Namespace.
This test exercises the real SSH Driver/helper, systemd, OpenClaw, and provider; API admission, immutable PostgreSQL snapshots, and worker reauthorization have separate integration coverage. It is not a whole-controller production rollout.
SSH real-host test environment
node --test tests/integration/ssh-compute-real.test.mjs is selected only by
OCC_TEST_SSH_REAL=1 or OCC_TEST_SSH_MODEL=1. Otherwise it explicitly skips and lists its inputs. When
selected, missing inputs or unavailable hosts fail; there is no fixture fallback.
| Variable | Meaning |
|---|---|
OCC_TEST_SSH_REAL |
Set to 1 for a disposable Linux systemd/sshd host with real OpenClaw. |
OCC_TEST_SSH_ADDRESS |
Required host address. |
OCC_TEST_SSH_PORT |
Required SSH port, 1–65535. |
OCC_TEST_SSH_USER |
Required; currently root. |
OCC_TEST_SSH_IDENTITY_FILE |
Required absolute private-key path on the test worker. |
OCC_TEST_SSH_KNOWN_HOSTS_FILE |
Required absolute verified known-hosts path on the test worker. |
OCC_TEST_SSH_NODE_PATH |
Required absolute Node.js 24 executable path on the host. |
OCC_TEST_SSH_OPENCLAW_PATH |
Required absolute OpenClaw entrypoint path on the host. |
OCC_TEST_SSH_RUNTIME_USER |
Required prefix for Driver-managed per-Agent Unix accounts. |
OCC_TEST_SSH_ROOT |
Optional host state root, default /var/lib/openclaw-enterprise. |
OCC_TEST_SSH_UNIT_DIRECTORY |
Optional host unit directory, default /etc/systemd/system. |
The suite uses ports 18800–18899, creates unique Namespace/Agent identities,
verifies readiness through SSH, cuts over two revisions, checks private Agent
UID/GID isolation and state persistence, retires the first snapshot, and deletes its Namespace.
The readiness-only selector requires no model credential and proves no model turn;
the model selector adds the credential proof above. Use the
container rig or a disposable host of your own.
