Files
computer-playbook/roles/web-app-openclaw

OpenClaw

Description

OpenClaw is an open-source personal AI agent that runs on hardware you control. It ships a gateway process with a browser Control UI next to its CLI and TUI, keeps persistent memory and a workspace on disk, and performs browser and filesystem tasks inside its own sandbox.

Overview

This role deploys OpenClaw as a gateway container behind the reverse proxy on its own canonical domain. Its compose service is pinned to the isolating runtime that the Kata & gVisor role selects for the host, its gateway configuration file is rendered from the role, and its state directory is kept in a persistent volume. Access is fronted by an OAuth2 proxy with Keycloak sign-in when Keycloak is part of the deployment.

Cosmos

The diagram places OpenClaw in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks).

flowchart LR
    subgraph deps [Dependencies]
        dep_svc_ai_litellm["svc-ai-litellm 🐳🐝"]
        dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"]
        dep_svc_net_tor["svc-net-tor 🐳🐝"]
        dep_svc_virt_kata["svc-virt-kata 💻"]
        dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
        dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
        dep_web_app_matomo["web-app-matomo 🐳🐝"]
        dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
        dep_web_svc_css["web-svc-css 💻"]
        dep_web_svc_logout["web-svc-logout 🐳🐝"]
    end
    subgraph role [web-app-openclaw 🐳🐝]
        svc_openclaw["openclaw"]
        svc_kata["kata"]
        svc_sso["sso"]
        svc_litellm["litellm"]
        svc_logout["logout"]
        svc_dashboard["dashboard"]
        svc_matomo["matomo"]
        svc_css["css"]
        svc_prometheus["prometheus"]
        svc_tor["tor"]
        svc_container_backup["container_backup"]
    end
    subgraph dependents [Dependents]
        dpt_svc_ai_robot["svc-ai-robot 💻"]
        dpt_svc_db_qdrant["svc-db-qdrant 🐳🐝"]
        dpt_web_app_baserow["web-app-baserow 🐳🐝"]
        dpt_web_app_checkmk["web-app-checkmk 🐳🐝"]
        dpt_web_app_fider["web-app-fider 🐳🐝"]
        dpt_web_app_gitea["web-app-gitea 🐳🐝"]
        dpt_web_app_gitlab["web-app-gitlab 🐳🐝"]
        dpt_web_app_homeassistant["web-app-homeassistant 🐳🐝"]
        dpt_web_app_jellyfin["web-app-jellyfin 🐳🐝"]
        dpt_web_app_jenkins["web-app-jenkins 🐳🐝"]
        dpt_web_app_listmonk["web-app-listmonk 🐳🐝"]
        dpt_web_app_mattermost["web-app-mattermost 🐳🐝"]
        dpt_more["..."]
    end
    dep_svc_ai_litellm -. "0..1" .-> svc_litellm
    dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
    dep_svc_net_tor -. "0..1" .-> svc_tor
    dep_svc_virt_kata -- "1:1" --> svc_kata
    dep_web_app_dashboard -. "0..1" .-> svc_dashboard
    dep_web_app_keycloak -. "0..1" .-> svc_sso
    dep_web_app_matomo -. "0..1" .-> svc_matomo
    dep_web_app_prometheus -. "0..1" .-> svc_prometheus
    dep_web_svc_css -. "0..1" .-> svc_css
    dep_web_svc_logout -. "0..1" .-> svc_logout
    svc_openclaw -- "1:1" --> dpt_more
    svc_openclaw -. "0..1" .-> dpt_svc_ai_robot
    svc_openclaw -. "0..1" .-> dpt_svc_db_qdrant
    svc_openclaw -. "0..1" .-> dpt_web_app_baserow
    svc_openclaw -. "0..1" .-> dpt_web_app_checkmk
    svc_openclaw -. "0..1" .-> dpt_web_app_fider
    svc_openclaw -. "0..1" .-> dpt_web_app_gitea
    svc_openclaw -. "0..1" .-> dpt_web_app_gitlab
    svc_openclaw -. "0..1" .-> dpt_web_app_homeassistant
    svc_openclaw -. "0..1" .-> dpt_web_app_jellyfin
    svc_openclaw -. "0..1" .-> dpt_web_app_jenkins
    svc_openclaw -. "0..1" .-> dpt_web_app_listmonk
    svc_openclaw -. "0..1" .-> dpt_web_app_mattermost

Solid 1:1 edges are fixed relationships; dashed 0..1 edges are conditional (enabled only in matching deployments); red 0..0 edges are turned off in this role. Node markers show the role's deploy modes (💻 host, 🐳 compose, 🐝 swarm); ❌ marks a service that is explicitly turned off, and ⚙️ an Ansible role dependency declared in meta/main.yml.

Features

  • Agent gateway: The container runs the OpenClaw gateway bound to the LAN interface on port 18789, with an HTTP health check against /healthz.
  • Isolating runtime: The compose service is pinned to Kata Containers where /dev/kvm and the Kata shim are available, and to gVisor otherwise.
  • Single sign-on: When Keycloak is deployed, an OAuth2 proxy sits in front of the gateway and admits only members of the administrator group of this application.
  • Gateway token: A generated gateway token guards the Control UI and the API, and the Control UI accepts the canonical domain as its only allowed origin.
  • Model gateway key: With the LiteLLM Gateway deployed, that gateway provisions a per-consumer virtual key aliased to this application.
  • MCP client contract: With Home Assistant deployed, the role declares the MCP client side of the platform contract as an internal streamable-HTTP client with a read-only tool policy. The MCP server list itself lives in openclaw.json, not in the container environment.
  • Persistent state: Memory and workspace live in a volume mounted at /home/node/.openclaw, with the rendered openclaw.json mounted into it, and are included in the container volume backup when that service is deployed.
  • Compose and Swarm: The role deploys in both modes and renders the gateway configuration file for the local host and the swarm peers.

Quick Setup

Development

Clone, set up the workstation, and deploy OpenClaw onto the local stack:

git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=web-app-openclaw full_cycle=false

Production

Run the published image to provision the inventory and deploy OpenClaw to a managed server (the mounted volume persists the inventory):

APP=web-app-openclaw
HOST=<your-server>
DOMAIN=<your-domain>
TLS_MODE=self_signed
SSH_PUBLIC_KEY="<your-ssh-public-key>"

docker run --rm -it \
  -v "$PWD/inventories:/etc/infinito.nexus/inventories" \
  -e APP="$APP" -e HOST="$HOST" -e DOMAIN="$DOMAIN" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \
  ghcr.io/infinito-nexus/core/debian bash -c '
    INVENTORY=/etc/infinito.nexus/inventories/production
    infinito administration inventory provision "$INVENTORY" \
      --inventory-file "$INVENTORY/devices.yml" \
      --host "$HOST" \
      --include "$APP" \
      --vars "{\"TLS_MODE\": \"$TLS_MODE\", \"DOMAIN_PRIMARY\": \"$DOMAIN\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" &&
    infinito administration deploy dedicated "$INVENTORY/devices.yml" \
      --password-file "$INVENTORY/.password" \
      --diff -vv'

MCP Client

OpenClaw consumes MCP. Every deployed shared MCP server role is discovered through MCP_DISCOVERED_SERVERS and written into the agent configuration.

Configuration

Property Value
Direction client
Transport Streamable HTTP
Config file openclaw.json
Auth Authorization header per server, from the discovery data

Servers whose auth scheme cannot be presented as a header are dropped before rendering, so no entry carries a credential the server would reject.

Verification

OpenClaw exposes no administrator-visible list of configured MCP servers, so the configured set is proven by a CLI test instead: files/test/test.sh probes the agent's mcp surface and fails when the exec itself fails or the output carries a failed to start server line. The Playwright spec covers the complementary case, that the gateway holding the MCP credentials refuses an unauthenticated caller.

Default state

Off. mcp.enabled is false unless an MCP server role is part of the deployment.

How to disable

Remove the MCP server roles, or pin mcp.enabled: false for this role. The rendered config then contains no MCP server entry.

Credits

Implemented by Kevin Veen-Birkenbach. Part of the Infinito.Nexus Project and maintained by Kevin Veen-Birkenbach. Licensed under the Infinito.Nexus Community License (Non-Commercial).