From 085654d017480262516daceeb6881aa6c3b626f3 Mon Sep 17 00:00:00 2001 From: Patrick Toal Date: Thu, 20 Aug 2026 11:25:08 -0400 Subject: [PATCH] Remove openclaw. Start supabase work. In flight --- CLAUDE.md | 63 ----- ansible.cfg | 2 + ...handoff-2026-03-29-openclaw-vm-refactor.md | 90 ------- playbooks/deploy_openclaw.yml | 247 ------------------ roles/openclaw/defaults/main.yml | 23 -- roles/openclaw/handlers/main.yml | 10 - roles/openclaw/meta/main.yml | 16 -- roles/openclaw/tasks/install.yml | 122 --------- roles/openclaw/tasks/main.yml | 10 - roles/openclaw/tasks/security.yml | 49 ---- roles/openclaw/tasks/signal.yml | 72 ----- .../templates/openclaw-config.yaml.j2 | 24 -- roles/openclaw/templates/openclaw.service.j2 | 29 -- roles/supabase/defaults/main.yml | 7 + roles/supabase/meta/argument_specs.yml | 13 + roles/supabase/tasks/main.yml | 27 +- roles/supabase/tasks/vault_secrets.yml | 9 + 17 files changed, 56 insertions(+), 757 deletions(-) delete mode 100644 CLAUDE.md delete mode 100644 docs/summaries/handoff-2026-03-29-openclaw-vm-refactor.md delete mode 100644 playbooks/deploy_openclaw.yml delete mode 100644 roles/openclaw/defaults/main.yml delete mode 100644 roles/openclaw/handlers/main.yml delete mode 100644 roles/openclaw/meta/main.yml delete mode 100644 roles/openclaw/tasks/install.yml delete mode 100644 roles/openclaw/tasks/main.yml delete mode 100644 roles/openclaw/tasks/security.yml delete mode 100644 roles/openclaw/tasks/signal.yml delete mode 100644 roles/openclaw/templates/openclaw-config.yaml.j2 delete mode 100644 roles/openclaw/templates/openclaw.service.j2 diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index be438ee..0000000 --- a/CLAUDE.md +++ /dev/null @@ -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/.yml --mode stdout` -**Run with extra vars:** `ansible-navigator run playbooks/.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. diff --git a/ansible.cfg b/ansible.cfg index 212ce77..1e06c07 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -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 diff --git a/docs/summaries/handoff-2026-03-29-openclaw-vm-refactor.md b/docs/summaries/handoff-2026-03-29-openclaw-vm-refactor.md deleted file mode 100644 index 7182c5d..0000000 --- a/docs/summaries/handoff-2026-03-29-openclaw-vm-refactor.md +++ /dev/null @@ -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//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 diff --git a/playbooks/deploy_openclaw.yml b/playbooks/deploy_openclaw.yml deleted file mode 100644 index fd5f4f1..0000000 --- a/playbooks/deploy_openclaw.yml +++ /dev/null @@ -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 diff --git a/roles/openclaw/defaults/main.yml b/roles/openclaw/defaults/main.yml deleted file mode 100644 index f7c60c5..0000000 --- a/roles/openclaw/defaults/main.yml +++ /dev/null @@ -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 diff --git a/roles/openclaw/handlers/main.yml b/roles/openclaw/handlers/main.yml deleted file mode 100644 index ab8ddce..0000000 --- a/roles/openclaw/handlers/main.yml +++ /dev/null @@ -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 diff --git a/roles/openclaw/meta/main.yml b/roles/openclaw/meta/main.yml deleted file mode 100644 index 07445eb..0000000 --- a/roles/openclaw/meta/main.yml +++ /dev/null @@ -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: [] diff --git a/roles/openclaw/tasks/install.yml b/roles/openclaw/tasks/install.yml deleted file mode 100644 index 9dd1a9d..0000000 --- a/roles/openclaw/tasks/install.yml +++ /dev/null @@ -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 diff --git a/roles/openclaw/tasks/main.yml b/roles/openclaw/tasks/main.yml deleted file mode 100644 index 9314033..0000000 --- a/roles/openclaw/tasks/main.yml +++ /dev/null @@ -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 diff --git a/roles/openclaw/tasks/security.yml b/roles/openclaw/tasks/security.yml deleted file mode 100644 index 9dd7de6..0000000 --- a/roles/openclaw/tasks/security.yml +++ /dev/null @@ -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 diff --git a/roles/openclaw/tasks/signal.yml b/roles/openclaw/tasks/signal.yml deleted file mode 100644 index 80b7aab..0000000 --- a/roles/openclaw/tasks/signal.yml +++ /dev/null @@ -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 -# signal-cli -a {{ openclaw_signal_account }} verify -# -# 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 " - - " signal-cli -a {{ openclaw_signal_account }} verify " - - "After registration, approve pairing:" - - " openclaw pairing approve signal" diff --git a/roles/openclaw/templates/openclaw-config.yaml.j2 b/roles/openclaw/templates/openclaw-config.yaml.j2 deleted file mode 100644 index 5632967..0000000 --- a/roles/openclaw/templates/openclaw-config.yaml.j2 +++ /dev/null @@ -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 %} diff --git a/roles/openclaw/templates/openclaw.service.j2 b/roles/openclaw/templates/openclaw.service.j2 deleted file mode 100644 index 2bacdbb..0000000 --- a/roles/openclaw/templates/openclaw.service.j2 +++ /dev/null @@ -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 diff --git a/roles/supabase/defaults/main.yml b/roles/supabase/defaults/main.yml index cd8d3a2..bf2f926 100644 --- a/roles/supabase/defaults/main.yml +++ b/roles/supabase/defaults/main.yml @@ -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. diff --git a/roles/supabase/meta/argument_specs.yml b/roles/supabase/meta/argument_specs.yml index a74c3dc..a3fa1ef 100644 --- a/roles/supabase/meta/argument_specs.yml +++ b/roles/supabase/meta/argument_specs.yml @@ -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 diff --git a/roles/supabase/tasks/main.yml b/roles/supabase/tasks/main.yml index 994b03c..ec682ba 100644 --- a/roles/supabase/tasks/main.yml +++ b/roles/supabase/tasks/main.yml @@ -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: diff --git a/roles/supabase/tasks/vault_secrets.yml b/roles/supabase/tasks/vault_secrets.yml index 72a5877..c17707f 100644 --- a/roles/supabase/tasks/vault_secrets.yml +++ b/roles/supabase/tasks/vault_secrets.yml @@ -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: >-