{"id":50842818,"url":"https://github.com/kpeacocke/piclaw","last_synced_at":"2026-06-14T07:34:54.679Z","repository":{"id":349259351,"uuid":"1201659741","full_name":"kpeacocke/piclaw","owner":"kpeacocke","description":"Ansible runbook for Raspberry Pi 5 + AI HAT+ 2 (Hailo-10H) — enforces a known-good state with Hailo runtime, local LLM via Hailo Ollama, and OpenClaw on every run.","archived":false,"fork":false,"pushed_at":"2026-06-02T00:21:08.000Z","size":352,"stargazers_count":0,"open_issues_count":4,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T07:34:50.627Z","etag":null,"topics":["ai-hat","ansible","hailo","hailo10h","homelab","local-llm","ollama","openclaw","raspberry-pi","raspberry-pi-5","raspberry-pi-5-ai"],"latest_commit_sha":null,"homepage":"https://github.com/kpeacocke/piclaw","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kpeacocke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":".github/SECURITY.md","support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-05T01:21:48.000Z","updated_at":"2026-06-02T00:21:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kpeacocke/piclaw","commit_stats":null,"previous_names":["kpeacocke/piclaw"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kpeacocke/piclaw","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpeacocke%2Fpiclaw","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpeacocke%2Fpiclaw/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpeacocke%2Fpiclaw/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpeacocke%2Fpiclaw/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kpeacocke","download_url":"https://codeload.github.com/kpeacocke/piclaw/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpeacocke%2Fpiclaw/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34313515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-hat","ansible","hailo","hailo10h","homelab","local-llm","ollama","openclaw","raspberry-pi","raspberry-pi-5","raspberry-pi-5-ai"],"created_at":"2026-06-14T07:34:49.112Z","updated_at":"2026-06-14T07:34:54.672Z","avatar_url":"https://github.com/kpeacocke.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pi 5 + AI HAT+ 2 OpenClaw Stack\n\n[![CI](https://github.com/kpeacocke/piclaw/actions/workflows/ansible-ci.yml/badge.svg)](https://github.com/kpeacocke/piclaw/actions/workflows/ansible-ci.yml)\n[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n[![Python 3.14+](https://img.shields.io/badge/python-3.14%2B-blue.svg)](https://www.python.org/)\n[![Ansible 2.20+](https://img.shields.io/badge/ansible-2.20%2B-blue.svg)](https://ansible.com/)\n\nDeploy a **privacy-preserving AI inference gateway** on Raspberry Pi 5 + AI HAT+ 2 using Ansible. Run large language models locally on edge hardware with hardware acceleration (Hailo NPU) and expose them via an **OpenAI-compatible API**.\n\n## What You Get\n\n- **OpenClaw Gateway** — OpenAI-compatible REST API for local LLM inference\n  - Web UI (Canvas) for interactive chat\n  - Streaming completions support\n  - No cloud dependency, no API keys sent externally\n- **Hailo NPU Acceleration** — Hardware-accelerated inference on AI HAT+ 2 (Hailo-10H)\n- **Ollama Integration** — Local model management (pull, load, cache)\n- **Flexible Profiles**\n  - `local-safe`: Fully local inference (privacy-first)\n  - `external-power`: Fallback to cloud providers (OpenAI, etc.)\n- **Idempotent Ansible Playbooks** — Repeatable, validated deployments\n- **CI/CD Ready** — Pre-commit hooks, linting, molecule testing, GitHub Actions\n\n## Quick Start\n\n### Prerequisites\n\n- **Hardware**: Raspberry Pi 5 + AI HAT+ 2 (Hailo-10H)\n- **OS**: Raspberry Pi OS Trixie 64-bit (fresh install recommended)\n- **Control Host**: Ansible 2.20+ with Python 3.14+\n\n### 1. Configure Target Host \u0026 Tailscale Auth Key\n\n**Edit `inventories/prod/hosts.yml`** to point to your Pi:\n\n```yaml\npi5-node:\n  ansible_host: 192.168.1.50  # ← Update to your Pi's IP (or Tailscale IP after first deploy)\n  ansible_user: pi\n```\n\n**Obtain a Tailscale auth key** (required for bootstrap):\n1. Go to https://login.tailscale.com/admin/settings/keys\n2. Click **Generate auth key**\n3. ✅ Check \"Reusable\" (allows multiple devices)\n4. Copy the key: `tskey-auth-\u003cbase64\u003e`\n5. Store it in AWX as a secret credential or encrypted inventory variable\n\n### 2. Run Playbooks (in order)\n\n```bash\n# 1. Bootstrap: Tailscale, OS packages, firmware, Hailo runtime\nansible-playbook playbooks/bootstrap.yml -l pi5\n\n# 2. Deploy: Ollama + OpenClaw gateway\nansible-playbook playbooks/openclaw.yml -l pi5\n\n# 3. Verify: Functional tests (chat roundtrip, health checks)\nansible-playbook playbooks/verify.yml -l pi5\n```\n\n### 3. Use the Gateway\n\nOpenClaw listens on `127.0.0.1:18789` and is accessible via **Tailscale mesh VPN** for secure, encrypted device-to-device access.\n\n#### Access via Tailscale (Recommended)\n\n1. **Join the Tailscale network**:\n   - Install [Tailscale](https://tailscale.com/download) on your control machine\n   - Sign in with your Tailscale account\n   - Your Pi will auto-join during bootstrap with the auth key you provided\n\n2. **Find the Pi's Tailscale IP**:\n   ```bash\n   tailscale list  # Shows all devices; look for your pi5 hostname\n   # Example: pi5 (100.100.100.50) to authenticate; created Apr 20, 2026\n   ```\n\n3. **Access OpenClaw via Tailscale**:\n   ```bash\n   # Web UI (Canvas)\n   open http://100.100.100.50:18789/__openclaw__/canvas/\n\n   # CLI\n   ssh pi@100.100.100.50 openclaw chat\n\n   # OpenAI-compatible API\n   curl http://100.100.100.50:18789/v1/chat/completions \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n       \"model\": \"ollama/llama3.2:3b\",\n       \"messages\": [{\"role\": \"user\", \"content\": \"Hello from Tailscale!\"}]\n     }'\n   ```\n\n#### Legacy SSH Port-Forward (if Tailscale unavailable)\n\n```bash\nssh -L 18789:127.0.0.1:18789 pi@\u003cpi5-ip\u003e\n\n# Then access at http://localhost:18789/...\n```\n\n## Deployment Modes\n\n### Standalone (CLI)\n\n```bash\n# Copy repo to your control machine\ngit clone https://github.com/kpeacocke/piclaw.git\ncd piclaw\n\n# Set up local dev environment\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements-dev.txt\nansible-galaxy collection install -r collections/requirements.yml\n\n# Run playbooks\nansible-playbook playbooks/bootstrap.yml -l pi5\nansible-playbook playbooks/openclaw.yml -l pi5\nansible-playbook playbooks/verify.yml -l pi5\n```\n\n### AWX (Orchestration)\n\nImport job templates from `awx/job_templates.yml`. AWX manages:\n- Inventory + host vars\n- Survey-driven variable overrides\n- Secrets injection\n- Job history and audit logs\n- Execution on execution nodes\n\nSee [AWX Setup](#awx-setup) for details.\n\n## Configuration\n\n### Core Variables\n\nRepository defaults live in each role's `defaults/main.yml`. Override them in one of:\n\n1. `group_vars/pi5.yml` — For all Pi 5s in the inventory\n2. AWX inventory/group/host vars — Per-host overrides\n3. Playbook `extra_vars` — Runtime via CLI or survey\n\n### Key Options\n\n**OpenClaw Profile** (`openclaw_profile`)\n- `local-safe` (default) — All inference runs on Hailo NPU via Ollama\n- `external-power` — Fallback to cloud API (e.g., OpenAI)\n\n**Model Selection** (`hailo_model`)\n- Default: `llama3.2:3b` (3B parameter, strongest installed Hailo-backed local model)\n- Options: Any Ollama model compatible with Hailo's quantization constraints\n\n**Sanitizer Proxy** (`use_sanitizer_proxy`)\n- Enabled by default — Adds safety layer to Ollama responses\n- Set to `false` to skip\n\n**External Provider** (for `external-power`)\n```yaml\nopenclaw_external_provider_name: custom\nopenclaw_external_provider_base_url: https://api.openai.com/v1\nopenclaw_external_provider_api: openai-completions\nopenclaw_external_provider_model: gpt-4-mini\nopenclaw_external_provider_api_key_env: OPENAI_API_KEY\n```\n\n**Tailscale Mesh VPN** (`tailscale_auth_key`)\n- **Required** — Obtain a reusable auth key from https://login.tailscale.com/admin/settings/keys\n  1. Go to **Settings \u003e Keys**\n  2. Click **Generate auth key**\n  3. ✅ Check \"Reusable\" (allows multiple devices)\n  4. Copy the key: `tskey-auth-\u003cbase64\u003e`\n- Provide it via an AWX custom credential, an encrypted AWX inventory/group variable, or an Ansible vault file\n- Enables secure, encrypted mesh VPN access to all services\n- Replaces LAN allowlists with Tailscale network boundary\n\n**Additional Tailscale Options**\n```yaml\ntailscale_device_name: \"pi5\"  # Custom hostname on Tailscale network\ntailscale_accept_dns: true    # Let Tailscale manage system DNS\ntailscale_advertise_routes: false  # Advertise routes to other devices\ntailscale_advertise_exit_node: false  # Don't route all traffic through Pi\n```\n\n## What Gets Installed \u0026 Validated\n\nEach playbook enforces a known-good state:\n\n### `bootstrap.yml`\n- **Tailscale** — Mesh VPN agent (joins your Tailscale network via auth key)\n- OS packages and security updates\n- Firmware updates + reboot handling\n- PCIe Gen 3.0 optimization\n- Hailo runtime and driver installation\n- Hardware probe (PCIe enumeration check)\n\n### `openclaw.yml`\n- Hailo Ollama integration (model pull and cache)\n- OpenClaw installer and systemd service\n- Provider configuration (local or external)\n- Optional sanitizer proxy\n- Port 18789 is available and responding\n\n### `verify.yml`\n- Smoke test: Chat roundtrip (request → response)\n- Health checks: Gateway and Ollama health endpoints\n- Doctor verification: System readiness report\n\n## Repository Structure\n\n```\n.\n├── playbooks/\n│   ├── bootstrap.yml      # OS setup, firmware, Hailo runtime\n│   ├── openclaw.yml       # Ollama, gateway, provider config\n│   └── verify.yml         # Smoke tests and health checks\n├── roles/\n│   ├── base/              # OS packages, EEPROM, PCIe config\n│   ├── hailo/             # Hailo runtime and driver\n│   ├── hailo_ollama/      # Ollama + model integration\n│   ├── openclaw/          # OpenClaw gateway + systemd\n│   └── validate/          # Verification and smoke tests\n├── inventories/\n│   └── prod/hosts.yml     # Target Pi host and group vars\n├── group_vars/\n│   ├── pi5.yml            # Local overrides\n│   └── pi5.vault.yml.example\n├── awx/                   # Job template and survey specs\n├── collections/\n│   └── requirements.yml    # Ansible collection dependencies\n├── molecule/\n│   └── default/           # Role testing (syntax/lint focused)\n├── tests/\n│   └── test_molecule_default.py\n└── .github/\n    ├── workflows/         # CI/CD automation (lint, test, release)\n    └── ISSUE_TEMPLATE/    # Bug and feature templates\n```\n\n## Development\n\n### Local Setup\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npython -m pip install --upgrade pip\npip install -r requirements-dev.txt\nansible-galaxy collection install -r collections/requirements.yml\npre-commit install\n```\n\n### Quality Checks\n\n```bash\n# All checks in one go\npre-commit run --all-files\n\n# Individual checks\nansible-lint                                         # Playbook lint\nyamllint .                                          # YAML syntax\nansible-playbook --syntax-check playbooks/*.yml    # Ansible syntax\nmolecule test                                       # Role testing\npytest                                              # Python unit tests\n```\n\nAll checks pass by default before commit (pre-commit hooks).\n\n### Running on Hardware\n\nTest code changes on a real Pi before submitting a PR:\n\n```bash\n# Get Pi's Tailscale IP: tailscale list | grep pi5\n# Assuming Pi's Tailscale IP is 100.100.100.50\n\n# SSH to the Pi via Tailscale and check status\nssh pi@100.100.100.50 openclaw status\n\n# Re-run a single playbook from your control machine\nansible-playbook playbooks/openclaw.yml -l pi5 -v\n\n# Check logs on the Pi\nssh pi@100.100.100.50 journalctl -u openclaw -n 50 -f\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for branching, PRs, and release workflow.\n\n## AWX Setup\n\n### Quick Import\n\nJob templates, surveys, and credential examples are defined in `awx/job_templates.yml`, `awx/surveys.yml`, and `awx/credentials.yml`. Create them in AWX via:\n\n1. **API**: `awx-cli` or `curl` with the YAML spec\n2. **UI**: Create manually, use the specs as reference\n3. **Automation**: Third-party AWX provisioning tools\n\n### Key Configuration\n\n**Tailscale Secret Storage**\n\nDo not keep the Tailscale auth key in a survey. Store it once in AWX and let the bootstrap job consume it automatically.\n\nRecommended options:\n\n1. **Custom AWX credential**\n  - Create a credential type with one secret field: `tailscale_auth_key`\n  - Paste the field schema into `Input configuration`\n  - Paste the variable injection into `Injector configuration` or `Output configuration`, depending on your AWX version\n  - Attach that credential to `piclaw-bootstrap`\n  - Use `awx/credentials.yml` as the source-controlled reference\n\n2. **Encrypted inventory/group variable**\n  - Put `tailscale_auth_key` in the AWX inventory or group vars UI\n  - Mark it as a secret if your AWX version supports secret inputs for that path\n\nThe bootstrap template no longer prompts for this value by default.\n\n**Galaxy Credential Requirement**\n\nAttach an Ansible Galaxy or Automation Hub credential to your AWX organization. Without it, collection sync skips and jobs fail with `couldn't resolve module/action 'community.docker...'`.\n\n**Receptor Topology**\n\nEnsure direct connection from AWX Receptor to execution nodes:\n\n```\nawx-task ↔ awx-receptor ↔ execution-node\n```\n\nDo **not** relay through a hop with a different Receptor version.\n\n**Runtime Path**\n\nAWX's `purge_old_stdout_files` task requires `/var/lib/awx/job_status/` in `awx-task`. Add to the entrypoint:\n\n```bash\nmkdir -p /var/lib/awx/job_status\n```\n\n### Execution Order\n\nRun templates in this order:\n\n1. `piclaw-bootstrap` — OS and runtime setup\n2. `piclaw-openclaw` — Ollama + gateway (surveys: profile, model, provider)\n3. `piclaw-verify` — Validation and smoke tests\n\nSee `awx/surveys.yml` for survey question specs and defaults.\n\n## Security \u0026 Secrets\n\n- **Never commit secrets** to git (passwords, API keys, hostnames)\n- **Vault encryption** available: `ansible-vault encrypt group_vars/pi5.vault.yml`\n- **AWX secrets**: Prefer credential plugins or encrypted inventory vars; avoid surveys for persistent secrets\n- **API keys** (for external providers): Injected at runtime via environment variables, not in playbooks\n\nFor sensitive deployments:\n- Manage inventory in a private repo\n- Use AWX's credential store for API keys\n- Rotate secrets regularly\n\n## GitHub Automation\n\n| Workflow | Trigger | Purpose |\n|----------|---------|---------|\n| `ansible-ci` | PR, push | Lint, syntax check, secret scan |\n| `scheduled-verify` | Weekly | Catch regressions early |\n| `pr-labeler` | PR | Auto-label by changed paths |\n| `label-sync` | Push to main | Sync issue labels from `.github/labels.yml` |\n| `release-drafter` | PR merge | Draft semantic release notes |\n| `publish-release` | Manual | Tag release and publish |\n| `scorecard` | Weekly | OpenSSF security posture |\n\n## Contributing\n\n1. **Fork** the repository\n2. **Create a branch** for your feature/fix\n3. **Run quality checks** locally (`pre-commit run --all-files`)\n4. **Test on hardware** if changes affect deployment\n5. **Submit a PR** with a clear, conventional commit title (`feat:`, `fix:`, `chore:`, etc.)\n6. **Request review** from maintainers\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for detailed workflow and PR expectations.\n\n## Support\n\n- **Discussions**: Ask questions in [GitHub Discussions](https://github.com/kpeacocke/piclaw/discussions)\n- **Issues**: Report bugs or request features via [GitHub Issues](https://github.com/kpeacocke/piclaw/issues)\n- **Security**: Report vulnerabilities via [GitHub Security Advisories](https://github.com/kpeacocke/piclaw/security/advisories/new) (private)\n\n## License\n\nLicensed under the **Apache License 2.0**. See [LICENSE](LICENSE) for details.\n\n## Acknowledgments\n\n- Raspberry Pi Foundation (hardware platform)\n- Hailo (NPU hardware and runtime)\n- Ollama (model management)\n- OpenClaw (LLM inference gateway)\n- Ansible (infrastructure automation)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpeacocke%2Fpiclaw","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkpeacocke%2Fpiclaw","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpeacocke%2Fpiclaw/lists"}