Remove openclaw. Start supabase work. In flight

This commit is contained in:
2026-08-20 11:25:08 -04:00
parent 5fe7b4edeb
commit 085654d017
17 changed files with 56 additions and 757 deletions

View File

@@ -1,63 +0,0 @@
# CLAUDE.md
## Session Start
Check `docs/summaries/` for a handoff file. If one exists, read it and the files it references — not all summaries. State: what you understand the project state to be, what you plan to do, and open questions.
If no handoff exists, determine session type before proceeding:
- **Quick task**: single-session, self-contained work (adding a playbook, fixing a role, configuring a service) → proceed without setup overhead
- **Sustained work**: multi-session project or significant design work → ask: what is the goal and what is the target deliverable
## Identity
You work with Pat, a Senior Solutions Architect at Red Hat building automation for a HomeLab. Expert-level Ansible knowledge — do not explain Ansible basics.
## Project
**Repo:** Ansible playbooks and roles managing a full HomeLab — Proxmox, OPNsense, OpenShift (SNO), AAP, Satellite, Gitea, and services.
**Inventory:** `/home/ptoal/Dev/inventories/toallab-inventory/static.yml`
**Run locally:** `ansible-navigator run playbooks/<name>.yml --mode stdout`
**Run with extra vars:** `ansible-navigator run playbooks/<name>.yml --mode stdout -e key=value`
**Lint:** `ansible-navigator lint playbooks/ --mode stdout`
**Collections:** `ansible-galaxy collection install -r collections/requirements.yml`
**Production:** playbooks run via AAP — do not refer to AWX
Load `docs/context/project-structure.md` when working on playbooks or roles.
## Rules
1. Do not mix unrelated project contexts in one session.
2. For sustained work: write state to disk after completing meaningful work. Use templates from `templates/claude-templates.md`. Include: decisions with rationale, exact numbers, file paths, open items.
3. For sustained work: before compaction or session end, write to disk — every number, every decision with rationale, every open question, every file path, exact next action.
4. For sustained work: when switching work types (development → documentation → review), write a handoff to `docs/summaries/handoff-[date]-[topic].md` and suggest a new session.
5. Do not silently resolve open questions. Mark them OPEN or ASSUMED.
6. Do not bulk-read documents. Process one at a time: read, summarize to disk, release from context before reading next. For the detailed protocol, read `docs/context/processing-protocol.md`.
7. Sub-agent returns must be structured, not free-form prose. Use output contracts from `templates/claude-templates.md`.
## Where Things Live
- `templates/claude-templates.md` — summary, handoff, decision, analysis, task, output contract templates (read on demand)
- `docs/summaries/` — active session state (latest handoff + decision records + source summaries)
- `docs/context/` — reusable domain knowledge, loaded only when relevant
- `project-structure.md` — playbook inventory, roles, collections, infrastructure map
- `processing-protocol.md` — full document processing steps
- `archive-rules.md` — summary lifecycle and file archival rules
- `subagent-rules.md` — when to use subagents vs. main agent
- `.claude/agents/` — specialized subagents (ansible-idempotency-reviewer — use before adding tasks or before production runs)
- `playbooks/` — main Ansible playbooks
- `roles/` — custom and external Ansible roles
- `collections/``requirements.yml` only; installed collections in `collections/ansible_collections/`
- `docs/archive/` — processed raw files. Do not read unless explicitly told.
- `output/deliverables/` — final outputs
For cross-project user preferences, recurring constraints, or tool preferences: use Claude Code's native memory system, not `docs/summaries/`.
## Error Recovery
If context degrades or auto-compact fires unexpectedly: write current state to `docs/summaries/recovery-[date].md`, tell the user what may have been lost, suggest a fresh session.
## Before Delivering Output
Verify: exact numbers preserved, open questions marked OPEN, output matches what was requested (not assumed), no Ansible idempotency regressions introduced.
All Ansible files (playbooks, task files, templates, vars) must end with a trailing newline.

View File

@@ -1,6 +1,8 @@
[defaults]
# Inventory - override with -i or ANSIBLE_INVENTORY env var
inventory = /home/ptoal/Dev/inventories/toallab-inventory/static.yml
jinja2_native = True
# Role and collection paths
roles_path = roles

View File

@@ -1,90 +0,0 @@
# Session Handoff: OpenClaw Deployment + VM Role Refactor
**Date:** 2026-03-29
**Session Focus:** Extract SNO VM creation into its own role; build new OpenClaw playbook with Signal channel and security stack
**Context Usage at Handoff:** ~60%
## What Was Accomplished
1. **Refactored SNO VM deployment into `proxmox_vm` role**`roles/proxmox_vm/`
2. **Removed `create_vm.yml` from `sno_deploy` role**`roles/sno_deploy/tasks/create_vm.yml` deleted
3. **Updated `deploy_openshift.yml` Play 1** to use `role: proxmox_vm` directly
4. **Created `roles/openclaw/`** — full role for OpenClaw installation and Signal channel
5. **Created `playbooks/deploy_openclaw.yml`** — 3-play pipeline: VM creation → SSH wait → install
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `roles/proxmox_vm/tasks/main.yml` | Created | VM creation tasks moved from sno_deploy/tasks/create_vm.yml |
| `roles/proxmox_vm/defaults/main.yml` | Created | Proxmox connection + VM spec defaults |
| `roles/proxmox_vm/meta/main.yml` | Created | Role metadata |
| `roles/sno_deploy/tasks/create_vm.yml` | Deleted | Moved to proxmox_vm role |
| `roles/sno_deploy/defaults/main.yml` | Modified | Removed `sno_pvc_disk_gb` (VM-only, now in proxmox_vm) |
| `roles/sno_deploy/meta/argument_specs.yml` | Modified | Removed VM-creation-only entries |
| `playbooks/deploy_openshift.yml` | Modified | Play 1 now uses `role: proxmox_vm` |
| `roles/openclaw/defaults/main.yml` | Created | Role-scoped defaults only (no proxmox vars) |
| `roles/openclaw/meta/main.yml` | Created | Role metadata |
| `roles/openclaw/handlers/main.yml` | Created | Reload systemd + restart openclaw |
| `roles/openclaw/tasks/main.yml` | Created | Orchestrates security → install → signal |
| `roles/openclaw/tasks/security.yml` | Created | UFW + rootless Podman |
| `roles/openclaw/tasks/install.yml` | Created | User + Node.js + OpenClaw binary + systemd service |
| `roles/openclaw/tasks/signal.yml` | Created | signal-cli install + registration reminder |
| `roles/openclaw/templates/openclaw-config.yaml.j2` | Created | OpenClaw config (model provider + Signal channel) |
| `roles/openclaw/templates/openclaw.service.j2` | Created | Hardened systemd unit |
| `playbooks/deploy_openclaw.yml` | Created | Full deployment playbook |
## Decisions Made This Session
- **DR-1: `proxmox_vm` role keeps `sno_*` variable names** BECAUSE renaming would break existing host_vars and SNO playbook — STATUS: confirmed
- **DR-2: `proxmox_vm` defaults duplicated in `sno_deploy`** BECAUSE Play 4 (install.yml) runs in a separate play and cannot inherit defaults from Play 1's role — STATUS: confirmed
- **DR-3: No Tailscale** BECAUSE OPNsense firewall provides perimeter security; UFW on VM is defense-in-depth only — STATUS: confirmed
- **DR-4: Rootless Podman instead of Docker CE** for agent sandbox isolation — `podman-docker` shim provides docker CLI compatibility; `DOCKER_HOST` points to user Podman socket — STATUS: confirmed
- **DR-5: `openclaw` user is non-system (`system: false`)** BECAUSE rootless Podman requires `/etc/subuid`+`/etc/subgid` entries, which Ubuntu only creates for non-system users — STATUS: confirmed
- **DR-6: VM spec vars live in playbook Play 1 `vars:` block** (not in `openclaw` role defaults) BECAUSE they're only used in VM creation, not in the role itself — STATUS: confirmed
## Key Numbers
- OpenClaw gateway port: **18789**
- signal-cli version: **0.13.15** (pinned in `openclaw_signal_cli_version` default — verify this is current)
- Node.js version: **24** (`openclaw_node_version`)
- OpenClaw VM defaults: **2 vCPU, 4096 MB RAM, 40 GB disk**
- UFW: allow **22/tcp** (SSH) + **18789/tcp** (gateway); deny all else inbound
## Conditional Logic Established
- IF `openclaw_signal_enabled: true` THEN signal.yml runs AND Signal block appears in config template
- IF `openclaw_vm_ip == 'dhcp'` THEN DHCP cloud-init task runs, ELSE static IP task runs (requires `openclaw_vm_gateway` and `openclaw_vm_nameserver`)
- IF disk already imported (scsi0 present in VM config) THEN `qm importdisk` and disk attach tasks are skipped (idempotency guard)
## Exact State of Work in Progress
- `openclaw` role: complete and syntax-checked (no errors)
- `deploy_openclaw.yml`: syntax-checked — passes with expected warnings (inventory host not yet defined)
- Signal registration: **cannot be automated** — requires interactive QR scan or SMS captcha. Tasks print instructions; user must run manually post-deploy.
## Open Questions Requiring User Input
- [ ] What inventory hostname/group for the OpenClaw VM? Currently hardcoded to `openclaw.toal.ca` in playbook `hosts:` — confirm or change
- [ ] What `openclaw_vm_vnet` should be used? Defaulted to `lan` — confirm VNet name in Proxmox
- [ ] Static IP or DHCP for the OpenClaw VM? (`openclaw_vm_ip` default is `dhcp`)
- [ ] Which phone number to use for Signal? Dedicated bot number recommended (registration de-authenticates the main Signal app on that number)
- [ ] Confirm `signal-cli` version **0.13.15** is the desired version — check https://github.com/AsamK/signal-cli/releases
## Assumptions That Need Validation
- ASSUMED: OpenClaw config file format is YAML at `$OPENCLAW_STATE_DIR/config.yaml` — validate against actual OpenClaw docs/source; the config template (`openclaw-config.yaml.j2`) may need field name corrections
- ASSUMED: `DOCKER_HOST=unix:/run/user/<uid>/podman/podman.sock` is sufficient for OpenClaw to use Podman for sandboxes — validate that OpenClaw respects `DOCKER_HOST`
- ASSUMED: `openclaw` npm package name is correct — verify at https://www.npmjs.com/package/openclaw
- ASSUMED: Ubuntu 24.04 Noble cloud image at `https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img` — stable URL, but verify
## What NOT to Re-Read
- `roles/sno_deploy/tasks/install.yml` — already reviewed this session; no changes made
- `roles/sno_deploy/tasks/create_vm.yml` — deleted; content now in `roles/proxmox_vm/tasks/main.yml`
## Files to Load Next Session
- `playbooks/deploy_openclaw.yml` — needed to review/run the playbook
- `roles/openclaw/tasks/install.yml` — needed if adjusting OpenClaw install steps
- `roles/openclaw/templates/openclaw-config.yaml.j2` — needed if config format needs correction
- `roles/openclaw/tasks/signal.yml` — needed if adjusting Signal setup

View File

@@ -1,247 +0,0 @@
---
# Deploy OpenClaw AI Gateway on a Proxmox VM
#
# OpenClaw: https://docs.openclaw.ai
# Ansible install docs: https://docs.openclaw.ai/install/ansible
# Signal channel docs: https://docs.openclaw.ai/channels/signal
#
# Prerequisites:
# Inventory host: openclaw.toal.ca (in group 'openclaw')
# host_vars required:
# openclaw_vm_ssh_public_key — SSH public key injected via cloud-init
# openclaw_vm_ip — static IP or 'dhcp'
# openclaw_vm_gateway — required for static IP
# openclaw_vm_vnet — Proxmox SDN VNet (e.g. lan)
#
# Vault secrets (1Password):
# vault_proxmox_token_secret — Proxmox API token
# vault_openclaw_api_key — Model provider API key (Anthropic, OpenAI, etc.)
# vault_openclaw_signal_phone — Signal account phone number (E.164, if Signal enabled)
#
# Security architecture:
# - OPNsense firewall provides perimeter security
# - UFW on VM: allow SSH (22) + gateway (18789); deny everything else inbound
# - Docker CE for agent sandbox isolation
# - Systemd hardening: NoNewPrivileges, PrivateTmp, ProtectSystem
#
# Signal channel MANUAL STEP required after deploy:
# sudo -i -u openclaw
# signal-cli link -n "OpenClaw" # scan QR with Signal app
# openclaw pairing approve signal
#
# Play order:
# Play 1: openclaw_create_vm — Create Ubuntu VM in Proxmox (cloud-init)
# Play 2: openclaw_wait — Wait for SSH to become available
# Play 3: openclaw_install — Install OpenClaw, security stack, Signal channel
#
# Usage:
# ansible-navigator run playbooks/deploy_openclaw.yml
# ansible-navigator run playbooks/deploy_openclaw.yml --tags openclaw_create_vm
# ansible-navigator run playbooks/deploy_openclaw.yml --tags openclaw_install
# ansible-navigator run playbooks/deploy_openclaw.yml --tags openclaw_install,openclaw_signal
# ---------------------------------------------------------------------------
# Play 1: Create Ubuntu VM in Proxmox using cloud-init
# ---------------------------------------------------------------------------
- name: Create OpenClaw VM in Proxmox
hosts: openclaw.toal.ca
gather_facts: false
connection: local
tags: openclaw_create_vm
vars:
# Proxmox connection — override in host_vars if needed
proxmox_node: pve1
proxmox_api_user: ansible@pam
proxmox_api_token_id: ansible
proxmox_api_token_secret: "{{ vault_proxmox_token_secret }}"
proxmox_validate_certs: false
proxmox_storage: local-lvm
proxmox_iso_dir: /var/lib/vz/template/iso
# VM spec — override in host_vars for the openclaw inventory host
openclaw_vm_name: openclaw
openclaw_vm_id: 0
openclaw_vm_cpu: 2
openclaw_vm_memory_mb: 4096
openclaw_vm_disk_gb: 40
openclaw_vm_vnet: lan
openclaw_vm_user: ubuntu
openclaw_vm_ssh_public_key: "" # required — set in host_vars
openclaw_vm_ip: dhcp # set to x.x.x.x for static
openclaw_vm_prefix: 24
openclaw_vm_gateway: ""
openclaw_vm_nameserver: ""
openclaw_vm_cloud_image_url: "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
openclaw_vm_cloud_image_filename: noble-server-cloudimg-amd64.img
# Computed
__openclaw_proxmox_api_host: "{{ hostvars['proxmox_api']['ansible_host'] }}"
__openclaw_proxmox_api_port: "{{ hostvars['proxmox_api']['ansible_port'] }}"
tasks:
- name: Download Ubuntu 24.04 cloud image to Proxmox host
ansible.builtin.get_url:
url: "{{ openclaw_vm_cloud_image_url }}"
dest: "{{ proxmox_iso_dir }}/{{ openclaw_vm_cloud_image_filename }}"
mode: "0644"
delegate_to: proxmox_host
- name: Create VM definition
community.proxmox.proxmox_kvm:
api_host: "{{ __openclaw_proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_port: "{{ __openclaw_proxmox_api_port }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
validate_certs: "{{ proxmox_validate_certs }}"
node: "{{ proxmox_node }}"
vmid: "{{ openclaw_vm_id | default(omit, true) }}"
name: "{{ openclaw_vm_name }}"
cores: "{{ openclaw_vm_cpu }}"
memory: "{{ openclaw_vm_memory_mb }}"
cpu: host
machine: q35
bios: ovmf
efidisk0:
storage: "{{ proxmox_storage }}"
format: raw
efitype: 4m
pre_enrolled_keys: false
scsihw: virtio-scsi-single
net:
net0: "virtio,bridge={{ openclaw_vm_vnet }}"
boot: "order=scsi0"
onboot: true
state: present
- name: Retrieve VM info
community.proxmox.proxmox_vm_info:
api_host: "{{ __openclaw_proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_port: "{{ __openclaw_proxmox_api_port }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
validate_certs: "{{ proxmox_validate_certs }}"
node: "{{ proxmox_node }}"
name: "{{ openclaw_vm_name }}"
type: qemu
config: current
register: __openclaw_vm_info
retries: 5
- name: Set VM ID fact
ansible.builtin.set_fact:
openclaw_vm_id: "{{ __openclaw_vm_info.proxmox_vms[0].vmid }}"
cacheable: true
- name: Check if disk is already imported (scsi0 present in config)
ansible.builtin.set_fact:
__openclaw_disk_imported: "{{ __openclaw_vm_info.proxmox_vms[0].config.scsi0 is defined }}"
- name: Import cloud image as primary disk
ansible.builtin.command:
cmd: >-
qm importdisk {{ openclaw_vm_id }}
{{ proxmox_iso_dir }}/{{ openclaw_vm_cloud_image_filename }}
{{ proxmox_storage }} --format raw
delegate_to: proxmox_host
changed_when: true
when: not __openclaw_disk_imported | bool
- name: Attach imported disk as scsi0
ansible.builtin.command:
cmd: "qm set {{ openclaw_vm_id }} --scsi0 {{ proxmox_storage }}:vm-{{ openclaw_vm_id }}-disk-0,iothread=1,cache=writeback"
delegate_to: proxmox_host
changed_when: true
when: not __openclaw_disk_imported | bool
- name: Resize disk to configured size
ansible.builtin.command:
cmd: "qm disk resize {{ openclaw_vm_id }} scsi0 {{ openclaw_vm_disk_gb }}G"
delegate_to: proxmox_host
changed_when: true
when: not __openclaw_disk_imported | bool
- name: Add cloud-init drive
ansible.builtin.command:
cmd: "qm set {{ openclaw_vm_id }} --ide2 {{ proxmox_storage }}:cloudinit"
delegate_to: proxmox_host
changed_when: true
when: not __openclaw_disk_imported | bool
- name: Write SSH public key to temp file on Proxmox host
ansible.builtin.copy:
content: "{{ openclaw_vm_ssh_public_key }}"
dest: "/tmp/openclaw-sshkey-{{ openclaw_vm_id }}.pub"
mode: "0600"
delegate_to: proxmox_host
no_log: false
- name: Configure cloud-init user and SSH key
ansible.builtin.command:
cmd: >-
qm set {{ openclaw_vm_id }}
--ciuser {{ openclaw_vm_user }}
--sshkeys /tmp/openclaw-sshkey-{{ openclaw_vm_id }}.pub
delegate_to: proxmox_host
changed_when: true
- name: Configure cloud-init network (static)
ansible.builtin.command:
cmd: >-
qm set {{ openclaw_vm_id }}
--ipconfig0 ip={{ openclaw_vm_ip }}/{{ openclaw_vm_prefix }},gw={{ openclaw_vm_gateway }}
--nameserver {{ openclaw_vm_nameserver }}
delegate_to: proxmox_host
changed_when: true
when: openclaw_vm_ip != 'dhcp'
- name: Configure cloud-init network (DHCP)
ansible.builtin.command:
cmd: "qm set {{ openclaw_vm_id }} --ipconfig0 ip=dhcp"
delegate_to: proxmox_host
changed_when: true
when: openclaw_vm_ip == 'dhcp'
- name: Start VM
community.proxmox.proxmox_kvm:
api_host: "{{ __openclaw_proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_port: "{{ __openclaw_proxmox_api_port }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
validate_certs: "{{ proxmox_validate_certs }}"
node: "{{ proxmox_node }}"
name: "{{ openclaw_vm_name }}"
state: started
- name: Remove temporary SSH key file
ansible.builtin.file:
path: "/tmp/openclaw-sshkey-{{ openclaw_vm_id }}.pub"
state: absent
delegate_to: proxmox_host
# ---------------------------------------------------------------------------
# Play 2: Wait for VM to become reachable
# ---------------------------------------------------------------------------
- name: Wait for OpenClaw VM SSH
hosts: openclaw.toal.ca
gather_facts: false
tags: openclaw_create_vm
tasks:
- name: Wait for SSH port
ansible.builtin.wait_for_connection:
timeout: 300
sleep: 10
# ---------------------------------------------------------------------------
# Play 3: Install OpenClaw, security stack, and Signal channel
# ---------------------------------------------------------------------------
- name: Install and configure OpenClaw
hosts: openclaw.toal.ca
gather_facts: true
become: true
tags: openclaw_install
roles:
- role: openclaw

View File

@@ -1,23 +0,0 @@
---
# OpenClaw service user
openclaw_user: openclaw
openclaw_group: openclaw
openclaw_home: /opt/openclaw
openclaw_state_dir: /opt/openclaw/.openclaw
openclaw_node_version: "24"
# Model provider
openclaw_model_provider: anthropic
openclaw_api_key: "{{ vault_openclaw_api_key }}"
# Signal channel
openclaw_signal_enabled: false
openclaw_signal_account: "{{ vault_openclaw_signal_phone | default('') }}"
openclaw_signal_cli_version: "0.13.15"
openclaw_signal_cli_path: /usr/local/bin/signal-cli
openclaw_signal_dm_policy: pairing
openclaw_signal_allow_from: [] # list of E.164 numbers permitted to DM
# Firewall
openclaw_ssh_port: 22
openclaw_gateway_port: 18789

View File

@@ -1,10 +0,0 @@
---
- name: Reload systemd
ansible.builtin.systemd:
daemon_reload: true
- name: Restart openclaw
ansible.builtin.systemd:
name: openclaw
state: restarted
listen: Restart openclaw

View File

@@ -1,16 +0,0 @@
---
galaxy_info:
author: ptoal
description: Install and configure OpenClaw AI gateway on Ubuntu
license: MIT
min_ansible_version: "2.16"
platforms:
- name: Ubuntu
versions:
- noble
galaxy_tags:
- openclaw
- ai
- signal
dependencies: []

View File

@@ -1,122 +0,0 @@
---
# ---------------------------------------------------------------------------
# System user and directories
# ---------------------------------------------------------------------------
- name: Create openclaw group
ansible.builtin.group:
name: "{{ openclaw_group }}"
system: false
state: present
- name: Create openclaw user
ansible.builtin.user:
name: "{{ openclaw_user }}"
group: "{{ openclaw_group }}"
home: "{{ openclaw_home }}"
shell: /sbin/nologin
system: false # must be non-system: subuid/subgid entries required for rootless Podman
create_home: true
state: present
- name: Get openclaw user UID
ansible.builtin.command:
cmd: "id -u {{ openclaw_user }}"
register: __openclaw_uid_result
changed_when: false
- name: Set openclaw UID fact
ansible.builtin.set_fact:
__openclaw_uid: "{{ __openclaw_uid_result.stdout }}"
- name: Enable lingering for openclaw user
ansible.builtin.command:
cmd: "loginctl enable-linger {{ openclaw_user }}"
register: __openclaw_linger
changed_when: __openclaw_linger.rc == 0
- name: Enable rootless Podman socket for openclaw user
ansible.builtin.systemd:
name: podman.socket
enabled: true
state: started
scope: user
become: true
become_user: "{{ openclaw_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ __openclaw_uid }}"
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ __openclaw_uid }}/bus"
- name: Create OpenClaw state directory
ansible.builtin.file:
path: "{{ openclaw_state_dir }}"
state: directory
owner: "{{ openclaw_user }}"
group: "{{ openclaw_group }}"
mode: "0750"
# ---------------------------------------------------------------------------
# Node.js
# ---------------------------------------------------------------------------
- name: Add NodeSource apt signing key
ansible.builtin.apt_key:
url: "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key"
state: present
- name: Add NodeSource apt repository
ansible.builtin.apt_repository:
repo: "deb https://deb.nodesource.com/node_{{ openclaw_node_version }}.x nodistro main"
state: present
filename: nodesource
- name: Install Node.js
ansible.builtin.apt:
name: nodejs
state: present
update_cache: true
- name: Install pnpm globally
community.general.npm:
name: pnpm
global: true
state: present
# ---------------------------------------------------------------------------
# OpenClaw binary
# ---------------------------------------------------------------------------
- name: Install OpenClaw via npm
community.general.npm:
name: openclaw
global: true
state: "{{ 'latest' if openclaw_version == 'latest' else 'present' }}"
notify: Restart openclaw
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
- name: Template OpenClaw config
ansible.builtin.template:
src: openclaw-config.yaml.j2
dest: "{{ openclaw_state_dir }}/config.yaml"
owner: "{{ openclaw_user }}"
group: "{{ openclaw_group }}"
mode: "0640"
notify: Restart openclaw
# ---------------------------------------------------------------------------
# Systemd service with hardening
# ---------------------------------------------------------------------------
- name: Template openclaw systemd service
ansible.builtin.template:
src: openclaw.service.j2
dest: /etc/systemd/system/openclaw.service
mode: "0644"
notify:
- Reload systemd
- Restart openclaw
- name: Enable and start openclaw service
ansible.builtin.systemd:
name: openclaw
enabled: true
state: started
daemon_reload: true

View File

@@ -1,10 +0,0 @@
---
- name: Configure security (UFW, Tailscale, Docker)
ansible.builtin.include_tasks: security.yml
- name: Install OpenClaw
ansible.builtin.include_tasks: install.yml
- name: Configure Signal channel
ansible.builtin.include_tasks: signal.yml
when: openclaw_signal_enabled | bool

View File

@@ -1,49 +0,0 @@
---
# ---------------------------------------------------------------------------
# UFW firewall — defense-in-depth behind OPNsense perimeter
# Allows SSH and the OpenClaw gateway port; blocks everything else inbound
# ---------------------------------------------------------------------------
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
update_cache: true
- name: Set UFW default policies
community.general.ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: incoming, policy: deny }
- { direction: outgoing, policy: allow }
- { direction: routed, policy: deny }
- name: Allow SSH
community.general.ufw:
rule: allow
port: "{{ openclaw_ssh_port | string }}"
proto: tcp
- name: Allow OpenClaw gateway port
community.general.ufw:
rule: allow
port: "{{ openclaw_gateway_port | string }}"
proto: tcp
- name: Enable UFW
community.general.ufw:
state: enabled
# ---------------------------------------------------------------------------
# Rootless Podman — used exclusively for agent sandbox isolation
# Runs as the openclaw user; no root daemon, no exposed sockets
# podman-docker provides a docker-compatible CLI shim for OpenClaw tooling
# ---------------------------------------------------------------------------
- name: Install Podman and dependencies
ansible.builtin.apt:
name:
- podman
- podman-docker
- uidmap
state: present
update_cache: true

View File

@@ -1,72 +0,0 @@
---
# ---------------------------------------------------------------------------
# signal-cli — Java-based CLI bridge required by OpenClaw's Signal channel.
# Docs: https://docs.openclaw.ai/channels/signal
#
# MANUAL STEP REQUIRED after first deploy:
# Option A (link existing account):
# sudo -i -u openclaw
# signal-cli link -n "OpenClaw" # scan QR code with Signal app
#
# Option B (register dedicated number):
# sudo -i -u openclaw
# signal-cli -a {{ openclaw_signal_account }} register --captcha <token>
# signal-cli -a {{ openclaw_signal_account }} verify <sms-code>
#
# Then approve DM access:
# openclaw pairing approve signal
# ---------------------------------------------------------------------------
- name: Install Java runtime (required by signal-cli)
ansible.builtin.apt:
name: default-jre-headless
state: present
update_cache: true
- name: Create signal-cli install directory
ansible.builtin.file:
path: /opt/signal-cli
state: directory
mode: "0755"
- name: Download signal-cli archive
ansible.builtin.get_url:
url: "https://github.com/AsamK/signal-cli/releases/download/v{{ openclaw_signal_cli_version }}/signal-cli-{{ openclaw_signal_cli_version }}-Linux.tar.gz"
dest: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}.tar.gz"
mode: "0644"
register: __openclaw_signal_cli_download
- name: Extract signal-cli
ansible.builtin.unarchive:
src: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}.tar.gz"
dest: /opt/signal-cli
remote_src: true
creates: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}/bin/signal-cli"
- name: Symlink signal-cli to PATH
ansible.builtin.file:
src: "/opt/signal-cli/signal-cli-{{ openclaw_signal_cli_version }}/bin/signal-cli"
dest: "{{ openclaw_signal_cli_path }}"
state: link
- name: Set ownership of signal-cli data directory
ansible.builtin.file:
path: "{{ openclaw_home }}/.local/share/signal-cli"
state: directory
owner: "{{ openclaw_user }}"
group: "{{ openclaw_group }}"
mode: "0700"
- name: Display Signal registration reminder
ansible.builtin.debug:
msg:
- "*** MANUAL STEP REQUIRED: Signal account not yet registered ***"
- "Switch to the openclaw user and register signal-cli:"
- " sudo -i -u {{ openclaw_user }}"
- " # Option A — link existing account (recommended):"
- " signal-cli link -n 'OpenClaw' # scan QR with Signal app"
- " # Option B — register a dedicated number:"
- " signal-cli -a {{ openclaw_signal_account }} register --captcha <token>"
- " signal-cli -a {{ openclaw_signal_account }} verify <sms-code>"
- "After registration, approve pairing:"
- " openclaw pairing approve signal"

View File

@@ -1,24 +0,0 @@
# OpenClaw configuration — managed by Ansible, do not edit manually
# Ref: https://docs.openclaw.ai
gateway:
port: 18789
# Gateway binds localhost only; Tailscale is the remote access path
providers:
- type: {{ openclaw_model_provider }}
apiKey: "{{ openclaw_api_key }}"
{% if openclaw_signal_enabled | bool %}
channels:
signal:
account: "{{ openclaw_signal_account }}"
cliPath: "{{ openclaw_signal_cli_path }}"
dmPolicy: {{ openclaw_signal_dm_policy }}
{% if openclaw_signal_allow_from | length > 0 %}
allowFrom:
{% for number in openclaw_signal_allow_from %}
- "{{ number }}"
{% endfor %}
{% endif %}
{% endif %}

View File

@@ -1,29 +0,0 @@
[Unit]
Description=OpenClaw AI Gateway
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={{ openclaw_user }}
Group={{ openclaw_group }}
WorkingDirectory={{ openclaw_home }}
Environment=OPENCLAW_STATE_DIR={{ openclaw_state_dir }}
Environment=OPENCLAW_CONFIG_PATH={{ openclaw_state_dir }}/config.yaml
Environment=DOCKER_HOST=unix:/run/user/{{ __openclaw_uid }}/podman/podman.sock
Environment=XDG_RUNTIME_DIR=/run/user/{{ __openclaw_uid }}
ExecStart=/usr/bin/openclaw gateway run
Restart=on-failure
RestartSec=5
# Hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths={{ openclaw_state_dir }} {{ openclaw_home }}
ProtectHome=read-only
[Install]
WantedBy=multi-user.target

View File

@@ -59,6 +59,13 @@ supabase_vector_fullname: supabase-vector
supabase_dockerhub_enabled: false
supabase_dockerhub_username: ""
# --- External DB access ---
# External hostname (or IP) clients use to reach the postgres NodePort.
# Must be set in host_vars; no safe generic default.
supabase_db_external_host: ""
# NodePort assigned for external postgres access (30000-32767).
supabase_db_nodeport: 30432
# --- Wait ---
# helm wait is disabled — image pulls from docker.io can take longer than any
# reasonable helm timeout. Pod readiness is checked separately below.

View File

@@ -114,6 +114,19 @@ argument_specs:
name that receives the privileged SCC.
type: str
default: supabase-vector
supabase_db_external_host:
description: >-
External hostname or IP used in the postgres_url written to Vault and
by clients connecting via the NodePort Service. Must be set in host_vars.
type: str
default: ""
supabase_db_nodeport:
description: >-
NodePort assigned for external PostgreSQL access (30000-32767).
Used both when creating the NodePort Service and when constructing the
postgres_url written to Vault.
type: int
default: 30432
supabase_wait_timeout:
description: Seconds to wait for deployments to become ready.
type: int

View File

@@ -452,7 +452,30 @@
caCertificate: ""
# ------------------------------------------------------------------
# Step 7: Wait for Kong deployment to be healthy
# Step 7: NodePort Service for external PostgreSQL access
# ------------------------------------------------------------------
- name: Create NodePort Service for external PostgreSQL access
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ supabase_db_fullname }}-external"
namespace: "{{ supabase_namespace }}"
spec:
type: NodePort
selector:
app.kubernetes.io/name: "{{ supabase_db_fullname }}"
app.kubernetes.io/instance: "{{ supabase_release_name }}"
ports:
- name: postgres
port: 5432
targetPort: 5432
nodePort: "{{ supabase_db_nodeport | int }}"
# ------------------------------------------------------------------
# Step 9: Wait for Kong deployment to be healthy
# ------------------------------------------------------------------
- name: Wait for Kong deployment to be ready
kubernetes.core.k8s_info:
@@ -468,7 +491,7 @@
delay: 10
# ------------------------------------------------------------------
# Step 8: Summary
# Step 10: Summary
# ------------------------------------------------------------------
- name: Display Supabase deployment summary
ansible.builtin.debug:

View File

@@ -144,6 +144,15 @@
openai_api_key: "{{ __supabase_openai_api_key }}"
no_log: "{{ supabase_no_log }}"
- name: Write postgres_url to oys/dev/supabase in Vault
community.hashi_vault.vault_kv2_write:
path: oys/dev/supabase
engine_mount_point: "{{ supabase_vault_mount }}"
data:
postgres_url: >-
postgresql://postgres:{{ __supabase_db_password }}@{{ supabase_db_external_host }}:{{ supabase_db_nodeport }}/{{ supabase_db_name }}
no_log: "{{ supabase_no_log }}"
- name: Report vault secret status
ansible.builtin.debug:
msg: >-