From ac0a99a0412b5a15a537f4f5d5cd8eba74c431a6 Mon Sep 17 00:00:00 2001 From: casjay Date: Fri, 14 Aug 2026 15:23:39 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=96=20Standardize=20AI.md;=20move=20pr?= =?UTF-8?q?oject=20spec=20to=20IDEA.md=20=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AI.md is now the standardized application-image specification from claudemgr/docker/CASJAYSDEVDOCKER.md (PARTs 0–8: critical rules, repo model, template system and OCI label canon, tooling, .env.scripts, runtime system, README layout, CI/CD, verification gates). The former AI.md — the full ClawLama project specification (OpenClaw + Ollama single-container design) — moved wholesale to IDEA.md, which did not previously exist, so nothing was lost. - AI.md: replaced with the CASJAYSDEVDOCKER.md master template - IDEA.md: new — carries the ClawLama project specification moved out of AI.md --- AI.md | 1025 +++++++++++++++++++++++++++++++++---------------------- IDEA.md | 476 ++++++++++++++++++++++++++ 2 files changed, 1099 insertions(+), 402 deletions(-) create mode 100644 IDEA.md diff --git a/AI.md b/AI.md index 8727e37..e972c0a 100644 --- a/AI.md +++ b/AI.md @@ -1,476 +1,697 @@ -## Project Overview +# CasjaysDev Docker Application Image Specification (casjaysdevdocker) -**ClawLama** is a CPU-optimized, multi-arch (amd64/arm64), single-container AI assistant that bundles [OpenClaw](https://github.com/openclaw/openclaw) and [Ollama](https://ollama.com) into one self-contained Docker image. Zero-cost, fully local, privacy-first — GPU accelerated when available, fully functional without it. +**Name**: {name} -**Image:** `docker.io/casjaysdevdocker/clawlama:latest` -**Base:** `debian:bookworm-slim` -**Platforms:** `linux/amd64`, `linux/arm64` -**Single container:** Both Ollama and OpenClaw run inside one image, no compose required. +**About this file:** This is the complete, authoritative specification for a CasjaysDev +Docker **application image** repository (`casjaysdevdocker/{name}`). It is a master +template — copied into an app image repo as that repo's `AI.md`. It is **permanent** — +never delete it from a repo that carries it. -**Source Reference:** Based on [iam-veeramalla's OpenClaw + Ollama guide](https://gist.github.com/iam-veeramalla/9d10f968038ee76d5bc374b44f0cf8bb). +**Note:** `{name}` in this file is a reference token, not setup-time replacement text. Its +value is always the repo directory basename (`basename "$PWD"`). + +**Maintenance procedure:** The bootstrap/update runbook (regenerating files after upstream +template changes, creating new repos) is NOT in this file — it lives in the +`dockersrc-bootstrap` agent (it handles both repo families via `REPO_TYPE` detection). +This file defines the standards that procedure enforces. --- -## Problem Statement +# PART INDEX -Running OpenClaw with a local Ollama model requires manual multi-step setup: installing OpenClaw, installing Ollama, pulling a model, writing a JSON config, and wiring everything together. ClawLama eliminates this friction by packaging everything into a single container — just `docker run`. +| PART | Title | +|------|-------| +| 0 | Critical rules | +| 1 | Repository model & structure | +| 2 | Template system reference | +| 3 | Tooling — gen-dockerfile & gen-script | +| 4 | `.env.scripts` reference | +| 5 | Runtime system — setup scripts, entrypoint, init.d | +| 6 | README.md standard layout | +| 7 | CI/CD workflows | +| 8 | Verification & commit | --- -## Architecture +# PART 0: CRITICAL RULES + +## Org mapping + +| System | Org | Example | +|--------|-----|---------| +| GitHub (source) | `casjaysdevdocker` | `https://github.com/casjaysdevdocker/{name}` | +| Docker Hub (push) | `casjaysdevdocker` | `casjaysdevdocker/{name}` | + +`casjaysdevdocker` repos are **applications** (gitea, opengist, super-productivity, +ampache, aria2, …). They always build FROM the pre-built, multi-arch `casjaysdev/*` base +images — never directly from upstream distro images. The bases themselves live in the +separate `dockersrc` org (GitHub `dockersrc/{base}` → Docker Hub `casjaysdev/{base}`) — +see the base specification (`DOCKERSRC.md`). + +## Non-negotiable rules + +1. **`AI.md` is permanent** — never delete it from the repo. +2. **Generated files are owned by the template system** — never hand-tune content that + `gen-dockerfile` regenerates (see PART 1 ownership table); fix the upstream + `gen-dockerfile` template instead, then regenerate. +3. **Hand-crafted files are owned by the repo** — `gen-dockerfile` must never overwrite + app-specific init.d scripts, custom bin scripts, a `05-custom.sh` with real content, or + a hand-crafted README (PART 6). +4. **Removed OCI labels stay removed** (PART 2) — never re-add `base.name`, + `schema-version`, or duplicate `authors`/`source` entries. +5. **`image.url` is a browsable page** — `https://hub.docker.com/r/casjaysdevdocker/{name}`. + `docker.io` is only a registry pull host; it is never a label URL. +6. **`image.source` and `image.documentation` are the GitHub repo** — + `https://github.com/casjaysdevdocker/{name}`. +7. **One Dockerfile, one file set** — app repos build one image (`latest` + date tag); + version variants (`Dockerfile.{ver}`) belong to base repos only. +8. **Always `FROM casjaysdev/`** — never pull an upstream distro image directly; + the base repos exist so every app shares one patched, multi-arch foundation. +9. **Only `root/`, `tmp/`, and `usr/` may exist at `rootfs/` top level** (PART 1). +10. **Maintenance runs through the `dockersrc-bootstrap` agent** — do not improvise the + update procedure from memory. + +--- + +# PART 1: REPOSITORY MODEL & STRUCTURE + +## What an app image repo is + +A `casjaysdevdocker/{name}` repo containerizes one application on top of a +`casjaysdev/*` base. It publishes a single image (`casjaysdevdocker/{name}:latest` plus a +date tag) — no per-version Dockerfile variants. The application itself is installed in +`05-custom.sh` and started by one or more init.d service scripts. + +## Standard tree ``` -┌───────────────────────────────────────────────────────────┐ -│ docker.io/casjaysdevdocker/clawlama:latest │ -│ debian:bookworm-slim | linux/amd64, linux/arm64 │ -│ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ entrypoint.sh │ │ -│ │ • Detect arch (amd64/arm64) + GPU (nvidia-smi) │ │ -│ │ • Generate openclaw.json from env vars │ │ -│ │ • Pull model if not cached │ │ -│ │ • Start Ollama (background) │ │ -│ │ • Wait for Ollama health │ │ -│ │ • Start OpenClaw gateway (foreground) │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────┐ ┌───────────────────┐ │ -│ │ OpenClaw │───▶│ Ollama (CPU/GPU) │ │ -│ │ Gateway + │ │ localhost:11434 │ │ -│ │ Agent │ │ Auto-detects GPU │ │ -│ │ :18789 │ │ at runtime │ │ -│ └──────────────┘ └───────────────────┘ │ -│ │ │ │ -│ ▼ ▼ │ -│ ┌─────────────┐ ┌────────────────┐ │ -│ │ /data/ │ │ /data/ │ │ -│ │ workspace/ │ │ ollama/ │ │ -│ │ (volume) │ │ (volume) │ │ -│ └─────────────┘ └────────────────┘ │ -└───────────────────────────────────────────────────────────┘ +{name}/ +├── AI.md # This specification (permanent) +├── Dockerfile # [generated] single build file +├── .dockerignore # [generated] +├── .env.scripts # [generated] build config +├── .gitattributes # [generated] +├── .gitea/workflows/ +│ └── build.yml # [generated] gen-dockerfile actions +├── .gitignore # [generated] +├── LICENSE.md # License (WTFPL / app's own license) +├── README.md # [generated*] standard layout (PART 6) +└── rootfs/ # Container filesystem overlay + ├── root/docker/setup/ # [generated*] build-time setup scripts 00–07 + ├── tmp/ # staged files installed at build time (optional) + └── usr/local/ + ├── bin/ # [generated*] entrypoint.sh, pkmgr, symlink, copy, + │ # healthcheck + [hand-crafted] app-specific scripts + └── etc/docker/ + ├── env/ # [hand-crafted] build/runtime env fragments (optional) + ├── functions/ + │ └── entrypoint.sh # [generated] entrypoint function library + └── init.d/ # [hand-crafted] runtime init scripts (one per service) ``` -### Single-Image Architecture +`[generated]` — safe to regenerate; local edits will be lost. +`[generated*]` — regenerated from the template, EXCEPT files carrying repo-specific +content (`05-custom.sh` with a real body, extra bin scripts, a hand-crafted README) — +those follow the hand-crafted rules in PARTs 5 and 6. +`[hand-crafted]` — never overwritten by the template system. -Both Ollama and OpenClaw run inside one container. The entrypoint manages process lifecycle: +App repos may additionally carry project files (`IDEA.md`, `CLAUDE.md`, `TODO.AI.md`) +per the global project conventions — they are repo-owned and never touched by tooling. -1. **Ollama** starts as a background process, binding to `localhost:11434`. -2. **OpenClaw** starts as the foreground process after Ollama is healthy, connecting to `http://localhost:11434/v1`. -3. If Ollama crashes, the entrypoint detects it and exits (container restarts via Docker's restart policy). -4. Signals (SIGTERM/SIGINT) are forwarded to both processes for clean shutdown. +## rootfs top-level policy -### GPU Runtime Detection +The only valid directories at the `rootfs/` root are `root/`, `tmp/`, and `usr/`. +Anything else is a leftover from old patterns. Migration map: -The image ships **no GPU libraries**. GPU acceleration is achieved through NVIDIA Container Toolkit runtime passthrough: +| Old rootfs path | Correct rootfs path | +|-----------------|---------------------| +| `rootfs/etc/{path}` | `rootfs/tmp/etc/{path}` | +| `rootfs/config/{path}` | `rootfs/tmp/etc/{path}` | +| `rootfs/data/{path}` | `rootfs/tmp/var/{path}` | +| `rootfs/var/{path}` | `rootfs/tmp/var/{path}` | +| `rootfs/opt/{path}` | `rootfs/tmp/opt/{path}` | +| `rootfs/share/{path}` | `rootfs/usr/local/share/{path}` | -- At startup, entrypoint runs `nvidia-smi` to detect GPU availability. -- **GPU found:** Ollama automatically uses CUDA via the mounted NVIDIA runtime. Logs report GPU model and VRAM. -- **No GPU:** Ollama falls back to CPU inference. No errors, no warnings — this is the expected default path. -- User enables GPU by passing `--gpus all` to `docker run` (requires NVIDIA Container Toolkit on host). +`rootfs/usr/local/share/template-files/` is retired — the `DEFAULT_TEMPLATE_DIR`, +`DEFAULT_FILE_DIR`, `DEFAULT_DATA_DIR`, and `DEFAULT_CONF_DIR` variables were removed +from the template system; the entrypoint installs staged files from `rootfs/tmp/etc/` +at container start instead. -### Ports +## Repo type detection -- `18789` — OpenClaw gateway (exposed) -- `11434` — Ollama API (internal only by default; expose with `-p 11434:11434` for debugging) - -### Installation Method (No curl | sh) - -- **Ollama:** Latest release binary downloaded directly from GitHub at build time: `https://github.com/ollama/ollama/releases/latest/download/ollama-linux-${TARGETARCH}`. No version pinning — always gets the newest stable release. -- **OpenClaw:** Installed via `npm install -g openclaw@latest`. Always gets the newest published version. -- **Node.js:** Current LTS from [NodeSource apt repository](https://deb.nodesource.com) (`node_lts.x`) with GPG key verification. Auto-advances to next LTS major (e.g., 22 → 24) when Node promotes it. - ---- - -## Features - -1. **Single container, single command** — `docker run -d docker.io/casjaysdevdocker/clawlama:latest` launches both Ollama and OpenClaw. No compose file required for basic use. -2. **debian:bookworm-slim base** — Minimal Debian with glibc for full Ollama SIMD compatibility (AVX/AVX2 on amd64, NEON on arm64). -3. **Direct binary installation (no curl | sh):** - - Ollama: latest release binary from GitHub releases (`/releases/latest/download/`), selected per `TARGETARCH`. - - OpenClaw: `npm install -g openclaw@latest`. - - Node.js: current LTS from NodeSource apt repo (`node_lts.x`) with GPG key verification. -4. **Pre-configured OpenClaw ↔ Ollama wiring** — OpenClaw config auto-generated at startup pointing to `http://localhost:11434/v1` with zero-cost pricing. -5. **Persistent volumes** — Two mount points: `/data/ollama` (model store), `/data/workspace` (OpenClaw). Survive restarts. -6. **Default model: `gpt-oss:20b`** — Automatically pulled on first launch before OpenClaw starts. -7. **Health checks** — Container `HEALTHCHECK` verifies both Ollama API and OpenClaw gateway. -8. **Environment variable overrides:** - - `CLAWLAMA_MODEL` — Model to pull and use (default: `gpt-oss:20b`) - - `CLAWLAMA_CONTEXT_WINDOW` — Context window size (default: `131072`) - - `CLAWLAMA_MAX_TOKENS` — Max output tokens (default: `8192`) - - `CLAWLAMA_MAX_CONCURRENT` — Agent concurrency (default: `4`) - - `CLAWLAMA_SUBAGENT_CONCURRENT` — Subagent concurrency (default: `8`) - - `CLAWLAMA_OPENCLAW_PORT` — OpenClaw gateway port (default: `18789`) - - `OLLAMA_NUM_THREADS` — CPU threads for inference (default: auto-detect physical cores) - - `OLLAMA_NUM_PARALLEL` — Max parallel requests (default: `1`) - - `OLLAMA_MAX_LOADED_MODELS` — Models in memory (default: `1`) - - `OLLAMA_HOST` — Ollama bind address (default: `127.0.0.1:11434`) -9. **Multi-arch (amd64 + arm64)** — Single manifest tag built with `docker buildx`. Ollama binary selected by `TARGETARCH`. All scripts POSIX shell. -10. **Runtime GPU detection** — Entrypoint probes `nvidia-smi`. GPU used automatically if available via `--gpus all`. No GPU libs in image; NVIDIA Container Toolkit handles passthrough. CPU is the default and primary path. -11. **Model swap without rebuild** — Changing `CLAWLAMA_MODEL` and restarting pulls the new model and regenerates config. -12. **CPU performance auto-tuning** — Entrypoint auto-detects physical cores, available RAM, and sets `OLLAMA_NUM_THREADS` optimally if unset. Logs detected values. -13. **Telegram integration helper** — Optional `CLAWLAMA_TELEGRAM_BOT_TOKEN` env var auto-configures Telegram channel. -14. **Full tool profile with layered restrictions** — All OpenClaw tools enabled via `profile: "full"`. Git and `/etc` restrictions enforced via TOOLS.md (soft) and container filesystem (hard). -15. **Startup banner** — Print connection info, detected arch, CPU cores, RAM, GPU status, and model to stdout. -16. **Quantized model recommendations** — README documents CPU-friendly models by RAM tier: - - 8 GB RAM: 7B Q4 variants - - 16 GB RAM: `gpt-oss:20b` (default) or 13B Q5 - - 32+ GB RAM: 20B+ full or 34B Q4 -17. **docker-compose.yml included** — Provided for users who prefer compose, with volume mounts and restart policy pre-configured. -18. **Multi-model support** — Comma-separated `CLAWLAMA_MODELS` env var configures multiple models in the OpenClaw provider config. -19. **Backup/restore scripts** — Shell scripts to tar `/data` volumes for migration. -20. **Portainer/Dockge compatible** — Compose file works with popular Docker management UIs. -21. **Architecture detection in logs** — Log detected arch and SIMD instruction sets (AVX, AVX2, AVX-512, NEON) for performance troubleshooting. -22. **GPU VRAM-aware model selection** — When GPU detected, log VRAM and suggest optimal model/quantization for available resources. - ---- - -## File Structure - -``` -clawlama/ -├── AI.md # This spec -├── TODO.AI.md # Task tracking -├── Dockerfile # Multi-stage, multi-arch (amd64 + arm64) -├── docker-compose.yml # Optional compose file for convenience -├── .env.example # Template environment variables -├── rootfs/ -│ ├── usr/local/bin/ -│ │ ├── entrypoint.sh # Main entrypoint: detect GPU, gen config, start services -│ │ └── healthcheck.sh # Health check script for HEALTHCHECK instruction -│ └── etc/clawlama/ -│ ├── openclaw.template.json # OpenClaw config template (envsubst-ready) -│ └── TOOLS.md # Agent tool usage rules (git deny, /etc deny) -├── scripts/ -│ ├── build.sh # Multi-arch buildx build + push -│ ├── backup.sh # Backup /data volumes -│ └── restore.sh # Restore /data volumes -└── README.md # User-facing documentation -``` - ---- - -## Quick Start +A repo is an **app** repo when no `Dockerfile.*` variant files exist: ```bash -# CPU-only (default) -docker run -d \ - --name clawlama \ - -v clawlama-data:/data \ - -p 18789:18789 \ - docker.io/casjaysdevdocker/clawlama:latest - -# With NVIDIA GPU acceleration -docker run -d \ - --name clawlama \ - --gpus all \ - -v clawlama-data:/data \ - -p 18789:18789 \ - docker.io/casjaysdevdocker/clawlama:latest - -# Custom model + expose Ollama API for debugging -docker run -d \ - --name clawlama \ - -v clawlama-data:/data \ - -p 18789:18789 \ - -p 11434:11434 \ - -e CLAWLAMA_MODEL=qwen2:7b \ - -e OLLAMA_HOST=0.0.0.0:11434 \ - docker.io/casjaysdevdocker/clawlama:latest +if find . -maxdepth 1 -name 'Dockerfile.*' -type f | grep -q -- .; then + REPO_TYPE="base" +else + REPO_TYPE="app" +fi ``` --- -## Dockerfile Sketch +# PART 2: TEMPLATE SYSTEM REFERENCE -```dockerfile -# ── Stage 1: Build dependencies ────────────────────────────── -FROM debian:bookworm-slim AS builder +Templates ship with `gen-dockerfile`, installed at +`/usr/local/share/CasjaysDev/scripts/templates/dockerfiles/` +(`$CASJAYSDEVDIR/templates/dockerfiles/` in a dev checkout). To inspect what the current +templates produce, generate a fresh reference tree in a temp dir: -ARG TARGETARCH - -# Install Node.js LTS from NodeSource apt repo (no curl | sh) -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl gnupg gettext-base && \ - mkdir -p /etc/apt/keyrings && \ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ - | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_lts.x nodistro main" \ - > /etc/apt/sources.list.d/nodesource.list && \ - apt-get update && apt-get install -y --no-install-recommends nodejs && \ - rm -rf /var/lib/apt/lists/* - -# Download latest Ollama binary directly (no curl | sh, no version pinning) -RUN curl -fsSL -o /usr/local/bin/ollama \ - "https://github.com/ollama/ollama/releases/latest/download/ollama-linux-${TARGETARCH}" && \ - chmod +x /usr/local/bin/ollama - -# Install latest OpenClaw via npm -RUN npm install -g openclaw@latest - -# ── Stage 2: Runtime ───────────────────────────────────────── -FROM debian:bookworm-slim - -# Install Node.js LTS runtime (same repo method, no dev packages) -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl gnupg tini procps gettext-base && \ - mkdir -p /etc/apt/keyrings && \ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ - | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_lts.x nodistro main" \ - > /etc/apt/sources.list.d/nodesource.list && \ - apt-get update && apt-get install -y --no-install-recommends nodejs && \ - rm -rf /var/lib/apt/lists/* - -# Copy Ollama binary -COPY --from=builder /usr/local/bin/ollama /usr/local/bin/ollama - -# Copy OpenClaw global install -COPY --from=builder /usr/lib/node_modules /usr/lib/node_modules -COPY --from=builder /usr/bin/openclaw /usr/bin/openclaw - -# Copy rootfs overlay -COPY rootfs/ / - -# Create non-root user and data directories -RUN groupadd -r clawlama && useradd -r -g clawlama -m clawlama && \ - mkdir -p /data/ollama /data/workspace && \ - chown -R clawlama:clawlama /data - -# Hard restriction: make /etc read-only for non-root users -RUN chmod -R a-w /etc - -# Environment defaults (CPU-optimized) -ENV CLAWLAMA_MODEL=gpt-oss:20b \ - CLAWLAMA_CONTEXT_WINDOW=131072 \ - CLAWLAMA_MAX_TOKENS=8192 \ - CLAWLAMA_MAX_CONCURRENT=4 \ - CLAWLAMA_SUBAGENT_CONCURRENT=8 \ - CLAWLAMA_OPENCLAW_PORT=18789 \ - OLLAMA_HOST=127.0.0.1:11434 \ - OLLAMA_MODELS=/data/ollama \ - OLLAMA_NUM_PARALLEL=1 \ - OLLAMA_MAX_LOADED_MODELS=1 - -VOLUME ["/data"] -EXPOSE 18789 - -HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \ - CMD /usr/local/bin/healthcheck.sh - -ENTRYPOINT ["tini", "--"] -CMD ["/usr/local/bin/entrypoint.sh"] +```bash +gen-dockerfile /tmp/gen-dockerfile/{org}/{repo} {distro} ``` -**Notes:** -- `tini` is the PID 1 init for proper signal handling — no zombie processes. -- `TARGETARCH` is automatically set by `docker buildx` (`amd64` or `arm64`). -- `curl` used only for apt key download and direct binary fetch — never piped to shell. -- Builder stage is discarded; runtime image contains only what's needed. -- `OLLAMA_MODELS=/data/ollama` ensures models persist in the volume. -- `/etc` made non-writable via `chmod` — hard enforcement of `/etc` write protection. -- Entrypoint copies `TOOLS.md` into `/data/workspace/TOOLS.md` on first run (OpenClaw reads this as agent instructions). +See `gen-dockerfile --help` for supported distros/types. Keep this PART in sync whenever +the templates change. ---- +## Template inventory -## OpenClaw Configuration +The template name selects the base OS family; for an app repo the resulting pull URL is +always the matching `casjaysdev/*` image: -The following JSON config is generated at container startup from environment variables via `envsubst`: +| Template | Final stage | Init / PID 1 | App pulls FROM | +|----------|-------------|--------------|----------------| +| `alpine.template` | `scratch.template` | tini | `casjaysdev/alpine` | +| `debian.template` | `scratch.template` | tini | `casjaysdev/debian` | +| `ubuntu.template` | `scratch.template` | tini | `casjaysdev/ubuntu` | +| `rhel.template` | `scratch.template` | tini | `casjaysdev/almalinux` | +| `archlinux.template` | `scratch.template` | tini | `casjaysdev/archlinux` (multi-arch manifest) | +| `web.template` | `systemd.template` | `/sbin/init` | `casjaysdev/web` | +| `xorg.template` | `systemd.template` | `/sbin/init` | `casjaysdev/xorg` | -```json -{ - "models": { - "providers": { - "ollama": { - "baseUrl": "http://localhost:11434/v1", - "apiKey": "ollama-local", - "api": "openai-completions", - "models": [ - { - "id": "${CLAWLAMA_MODEL}", - "name": "${CLAWLAMA_MODEL}", - "reasoning": false, - "input": ["text"], - "cost": { - "input": 0, - "output": 0, - "cacheRead": 0, - "cacheWrite": 0 - }, - "contextWindow": ${CLAWLAMA_CONTEXT_WINDOW}, - "maxTokens": ${CLAWLAMA_MAX_TOKENS} - } - ] - } - } - }, - "agents": { - "defaults": { - "model": { - "primary": "ollama/${CLAWLAMA_MODEL}" - }, - "workspace": "/data/workspace", - "maxConcurrent": ${CLAWLAMA_MAX_CONCURRENT}, - "subagents": { - "maxConcurrent": ${CLAWLAMA_SUBAGENT_CONCURRENT} - } - } - }, - "tools": { - "profile": "full", - "exec": { - "security": "full", - "ask": "off", - "backgroundMs": 10000, - "timeoutSec": 1800, - "applyPatch": { - "enabled": true, - "workspaceOnly": true - } - }, - "fs": { - "workspaceOnly": false - }, - "elevated": { - "enabled": false - } - } -} +Default template for app repos is `alpine` unless the application needs systemd, a GUI +stack, or a distro-specific package. + +## Final-stage templates + +`scratch.template` — all non-GUI templates. +- `ENTRYPOINT [ "tini", "-p", "SIGTERM","--", "/usr/local/bin/entrypoint.sh" ]` +- `STOPSIGNAL SIGRTMIN+3` + +`systemd.template` — `web` and `xorg` (systemd is PID 1; tini is redundant). +- `ENTRYPOINT [ "/sbin/init" ]` +- `STOPSIGNAL SIGRTMIN+3` +- No `tini_provider` stage, no `COPY --from=tini_provider` line. + +Both are identical apart from `ENTRYPOINT`. OCI labels, `ENV HOSTNAME`, and +`VOLUME`/`EXPOSE`/`HEALTHCHECK` are the same in both. + +## OCI label standard + +Both final-stage templates emit these labels (no others): + +``` +LABEL maintainer="${GEN_DOCKERFILE_MAINTAINER}" +LABEL org.opencontainers.image.vendor="${GEN_DOCKERFILE_VENDOR:-CasjaysDev}" +LABEL org.opencontainers.image.authors="${GEN_DOCKERFILE_AUTHOR:-CasjaysDev}" +LABEL org.opencontainers.image.licenses="${LICENSE}" +LABEL org.opencontainers.image.title="${IMAGE_NAME}" +LABEL org.opencontainers.image.description="Containerized version of ${IMAGE_NAME}" +LABEL org.opencontainers.image.created="${BUILD_DATE}" +LABEL org.opencontainers.image.version="${BUILD_VERSION}" +LABEL org.opencontainers.image.revision="${GIT_COMMIT}" +LABEL org.opencontainers.image.url="${GEN_DOCKERFILE_HUB_REPO}" +LABEL org.opencontainers.image.source="${GEN_DOCKERFILE_GIT_REPO}" +LABEL org.opencontainers.image.documentation="${GEN_DOCKERFILE_GIT_REPO}" +LABEL org.opencontainers.image.vcs-type="Git" +LABEL com.github.containers.toolbox="false" ``` -### Tool Permission Policy +Shell-expanded values (no `\`) are evaluated at template-render time by `gen-dockerfile`. +Dollar-escaped values (`\${...}`) become literal Docker `ARG`/`ENV` references in the +generated `Dockerfile`. -OpenClaw's tool policy operates at the **tool level** (allow/deny entire tools like `exec`, `read`, `write`), not at the command or path level. To enforce the desired restrictions (no `git commit/push/reset --hard`, no writes to `/etc`), ClawLama uses a **layered approach**: +Resolved values for a `casjaysdevdocker` repo pushing to Docker Hub: -| Layer | Mechanism | What It Enforces | -|-------|-----------|------------------| -| **Tool profile** | `tools.profile: "full"` | All tools enabled: `group:fs`, `group:runtime`, `group:ui`, `group:sessions`, `group:memory`, `group:automation`, `web_search`, `web_fetch` | -| **Exec security** | `tools.exec.security: "full"` | Shell commands auto-approved (no prompt). Agent has full exec access. | -| **Workspace TOOLS.md** | Agent instruction file | Soft restrictions: instructs agent to never run `git commit`, `git push`, `git reset --hard` | -| **Container filesystem** | Dockerfile `RUN chmod` / read-only mounts | Hard restriction: `/etc` read-only at container level, preventing writes regardless of agent behavior | -| **Elevated mode** | `tools.elevated.enabled: false` | No host-level exec breakout (good hygiene) | -| **Workspace scope** | `tools.exec.applyPatch.workspaceOnly: true` | `apply_patch` operations restricted to workspace directory | - -### Tool Groups (Reference) - -OpenClaw's built-in tool groups for use in `tools.allow` / `tools.deny`: - -| Group | Tools | +| Label | Value | |-------|-------| -| `group:runtime` | `exec`, `bash`, `process` | -| `group:fs` | `read`, `write`, `edit`, `apply_patch` | -| `group:sessions` | `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `session_status` | -| `group:memory` | `memory_search`, `memory_get` | -| `group:ui` | `browser`, `canvas` | -| `group:automation` | `cron`, `gateway` | -| `group:messaging` | `message` | -| `group:nodes` | `nodes` | +| `url` | `https://hub.docker.com/r/casjaysdevdocker/{name}` — browsable Hub page; `gen-dockerfile` derives it from the registry host (`docker.io` → `hub.docker.com/r/`) | +| `source` | `https://github.com/casjaysdevdocker/{name}` | +| `documentation` | `https://github.com/casjaysdevdocker/{name}` | -### Git Restrictions (via TOOLS.md) +Older app repos may still carry `url="https://docker.io/casjaysdevdocker/{name}"` — that +is the stale form; regeneration corrects it. Removed labels (never re-add): +- `org.opencontainers.image.base.name` — belongs on the base image, not this image +- `org.opencontainers.image.schema-version` — non-spec; redundant with `version` +- Any duplicate `authors` or `source` entries -Since OpenClaw has no command-pattern deny list for `exec`, git restrictions are enforced via the workspace `TOOLS.md` file — an agent instruction document that OpenClaw injects into the system prompt: +## HOSTNAME convention -```markdown - -# Tool Usage Rules +All templates set `ENV HOSTNAME="casjaysdevdocker-${IMAGE_NAME}"` in every stage that +declares it. The prefix is always `casjaysdevdocker-`, never `casjaysdev-`. -## Git Restrictions (MANDATORY) -- NEVER run `git commit` in any form -- NEVER run `git push` in any form -- NEVER run `git reset --hard` in any form -- All other git commands are allowed (status, diff, log, add, branch, checkout, clone, pull, stash, etc.) +## `GEN_DOCKERFILE_APP_DIR` and pull URL logic -## Filesystem Restrictions -- Do NOT write to or delete files in /etc/ -- The /etc directory is read-only at the container level +`GEN_DOCKERFILE_APP_DIR` is auto-detected by `gen-dockerfile` from the parent directory +of `$PWD` (the org the checkout lives in): + +```bash +GEN_DOCKERFILE_APP_DIR="${GEN_DOCKERFILE_APP_DIR:-$(basename -- "$(dirname -- "$PWD")")}" ``` -**Important:** `TOOLS.md` is a **soft restriction** — the LLM is instructed not to run these commands, but it is not technically blocked by OpenClaw's tool policy engine. For hard enforcement, users should enable exec approvals (`tools.exec.ask: "always"`). The container-level `/etc` read-only mount is a **hard restriction** regardless. +It selects the `GEN_DOCKER_SPECIFY_IMAGE_SOURCE_*` defaults: -### `/etc` Protection (via Container) +- `casjaysdevdocker/*` repos → `FROM casjaysdev/:latest` (pre-built, multi-arch) +- `dockersrc/*` and all other orgs → `FROM :latest` (upstream official images) -Since OpenClaw's `tools.fs.workspaceOnly` is an all-or-nothing toggle, and we want reads everywhere but writes denied only to `/etc`, this is enforced at the **Docker layer**: +App repos must resolve to the `casjaysdev/*` branch — a checkout outside +`~/Projects/*/casjaysdevdocker/` needs `GEN_DOCKERFILE_APP_DIR="casjaysdevdocker"` +exported before calling `gen-dockerfile`, or the regenerated Dockerfile silently reverts +to upstream distro pulls (rule 8 violation). + +## Arch Linux apps + +`casjaysdev/archlinux` is a multi-arch manifest (`linux/amd64` + `linux/arm64`), so app +repos use a single `FROM ${PULL_URL}:${DISTRO_VERSION} AS build` — the three-stage +`base-${TARGETARCH}` FROM block belongs to the base repo only. + +## `web.template` / `xorg.template` notes + +`web` apps inherit the systemd + noVNC stack (`SERVICE_PORT="5800"`, +`EXPOSE_PORTS="5800 5900"` defaults); `xorg` apps inherit the systemd + Xorg stack. App +packages go in `ENV_PACKAGES` / `02-packages.sh`, never by editing the template's stack +list. + +## `debian.template` / `ubuntu.template` — RUN continuation + +The first `RUN` block must have `; \` after the `echo` line so +`export DEBIAN_FRONTEND=noninteractive` executes before `apt-get`: ```dockerfile -RUN chmod -R a-w /etc +RUN set -e; \ + echo "Updating the system"; \ + export DEBIAN_FRONTEND=noninteractive; \ + apt-get update && apt-get upgrade -yy && apt-get dist-upgrade -yy ``` -### Override Path +Without the `; \` the export is a no-op and `apt-get` may prompt interactively. + +## Template resolution order + +1. `$GEN_DOCKERFILE_CONFIG_DIR/templates/.template` (user override) +2. `/usr/local/share/CasjaysDev/scripts/templates/dockerfiles/.template` + (installed; `$CASJAYSDEVDIR/templates/dockerfiles/` in a dev checkout) + +`template_options.source` is sourced after `__set_variables`, allowing template-specific +variable overrides. + +--- + +# PART 3: TOOLING — gen-dockerfile & gen-script + +## `gen-dockerfile` + +``` +Usage: gen-dockerfile [options] [dir] [template] [repo-name] [git-repo-url] +``` + +| Flag | Meaning | +|------|---------| +| `--update` | Rewrite `.env.scripts` (add/drop vars against the current template) and update ARG/LABEL lines in the `Dockerfile`. Touches no other file. | +| `--nogit` | Do not init or commit a git repo — required inside an existing repo. | +| `--dir PATH` | Operate on / write output to PATH instead of `$PWD`. | +| `--template NAME` | Template to use (`alpine`, `debian`, `ubuntu`, `rhel`, `archlinux`, `scratch`, `web`, `xorg`). Defaults to `alpine`. | +| `--repo NAME` | Registry repo name (image basename). Defaults to the directory name. | +| `--org NAME` | Registry owner / GitHub org (`--user` is an alias). Prefix `git:` or `reg:` to scope to one system; bare value sets both. For app repos both are `casjaysdevdocker`. | +| `--registry URL` | Registry provider URL (e.g. `https://docker.io`). | +| `--tag VERSION` | Image version tag (default `latest`). | +| `--add-tags TAGS` | Comma-separated additional tags (`USE_DATE` = auto date tag). | +| `--distro-name IMG` | Base image pull URL (overrides `ENV_PULL_URL`). | +| `--distro-version T` | Base image tag (overrides `ENV_DISTRO_TAG`). | +| `--startup FILE` | Generate an init.d service script at `rootfs/usr/local/etc/docker/init.d/FILE` via `gen-script other/start-service`. | +| `--dockerfile` | Regenerate the Dockerfile only. | +| `--force` | Overwrite existing files without prompting. | + +Resolution order when a value is not given by a flag: flags → git remote → project dirs → +defaults. + +Special subcommand — `gen-dockerfile actions` writes `.gitea/workflows/build.yml` from +the existing `Dockerfile` (PART 7). App repos have no versioned `build.{ver}.yml` files. + +## `gen-script` + +``` +Usage: gen-script [options] [template] [filename] +``` + +| Flag / env var | Meaning | +|----------------|---------| +| `--dir PATH` | Write the generated file to `PATH/filename`. | +| `-n` / `--name VALUE` | Service name substituted into the template — fills `REPLACE_SERVICE_NAME` in `other/start-service`, pre-populating `SERVICE_NAME=` without a sed step. | +| `GEN_SCRIPT_OVERWRITE="Y"` | Overwrite the output without prompting (default `"A"` = ask). Required when the target exists, even with `GEN_SCRIPT_EDITFILE="N"`. | +| `GEN_SCRIPT_EDITFILE="N"` | Suppress the interactive editor after generation. `-e`/`--no` sets BOTH this AND `GEN_SCRIPT_OVERWRITE="Y"`; the env var alone does not. | +| `other/start-service` | Template path — positional arg 1, slash-joined words, matching the `@@Template` header. | +| `filename` | Output basename — positional arg 2, combined with `--dir`. | + +Other flags: `-k`/`--keep` (never overwrite), `--replace` (new header replaces old), +`-d`/`--desc` (header description), `-p`/`--prev` (copy header metadata from a file). + +--- + +# PART 4: `.env.scripts` REFERENCE + +Generated at the repo root; sourced by `gen-dockerfile` and by CI at build time. App +repos carry exactly one. It is a pure `KEY="value"` file — no logic. + +## Variables + +| Variable | Purpose | +|----------|---------| +| `ENV_DOCKERFILE` | Dockerfile to build (`Dockerfile`) | +| `ENV_REGISTRY_REPO` | Image name in the registry (`{name}`) | +| `ENV_REGISTRY_ORG` | Registry namespace — `casjaysdevdocker` for app repos | +| `ENV_REGISTRY_URL` | Registry base URL (`https://docker.io`) — pull/push host, never a label URL | +| `ENV_REGISTRY_PUSH` | Full push path `org/repo` (`casjaysdevdocker/{name}`) | +| `ENV_ADD_IMAGE_PUSH` | Extra push destinations | +| `ENV_GIT_REPO_URL` | Full Git repo URL — `https://github.com/casjaysdevdocker/{name}`; feeds the `source`/`documentation` labels, so a wrong value here regresses labels on regeneration | +| `ENV_USE_TEMPLATE` | Template name (`alpine`, `debian`, …) — the authoritative record of which base family the app builds on | +| `ENV_PULL_URL` | Base image to pull FROM (`casjaysdev/`) | +| `ENV_DISTRO_TAG` | Tag for the pull image (`latest`) | +| `ENV_IMAGE_TAG` | Default image tag (`latest`) | +| `ENV_ADD_TAGS` | Additional comma-separated tags; `USE_DATE` auto-generates a date tag | +| `ENV_PACKAGES` | Space-separated package list | +| `ENV_VENDOR` / `ENV_AUTHOR` / `ENV_MAINTAINER` | Label metadata | +| `SERVICE_PORT` | Primary exposed port — apps normally set this | +| `EXPOSE_PORTS` | Additional exposed ports | +| `PHP_VERSION` / `NODE_VERSION` / `NODE_MANAGER` | Runtime versions (`system` default) | +| `WWW_ROOT_DIR` | Web root (`/usr/local/share/httpd/default`) | +| `DOCKER_ENTYPOINT_PORTS_WEB` / `DOCKER_ENTYPOINT_PORTS_SRV` | Ports passed to the entrypoint | +| `DOCKER_ENTYPOINT_HEALTH_APPS` / `DOCKER_ENTYPOINT_HEALTH_ENDPOINTS` | Healthcheck targets | + +## Legacy variable auto-migration + +`gen-dockerfile` calls `__migrate_env_script` on every run, renaming old variables: + +| Old name | Current name | +|----------|-------------| +| `ENV_IMAGE_NAME` | `ENV_REGISTRY_REPO` | +| `ENV_IMAGE_PUSH` | `ENV_REGISTRY_PUSH` | +| `ENV_HUB_BASE` | `ENV_REGISTRY_URL` | +| `ENV_ORG_NAME` | `ENV_REGISTRY_ORG` | + +Never use the old names in new files. Retired variables that must not reappear anywhere: +`DEFAULT_TEMPLATE_DIR`, `DEFAULT_FILE_DIR`, `DEFAULT_DATA_DIR`, `DEFAULT_CONF_DIR`. + +--- + +# PART 5: RUNTIME SYSTEM — SETUP SCRIPTS, ENTRYPOINT, INIT.D + +## Build-time setup scripts (`rootfs/root/docker/setup/`) + +Run in order inside the build stage: + +| Script | Role | +|--------|------| +| `00-init.sh` | Initialize base directory structure and environment | +| `01-system.sh` | Repos, locales, timezone, system settings | +| `02-packages.sh` | App-specific packages, package managers, language runtimes | +| `03-files.sh` | Install staged files (`rootfs/tmp/etc/*` → `/etc/*`), permissions, symlinks | +| `04-users.sh` | Create service users/groups | +| `05-custom.sh` | Application install logic — the heart of an app repo | +| `06-post.sh` | Post-install configuration | +| `07-cleanup.sh` | Remove build deps, caches, temp files | + +**`05-custom.sh` ownership:** the upstream template ships an empty stub. An app repo's +`05-custom.sh` carries the application install (download/build, users, default config) — +that content exists only in the repo's git history, never in the template. On +regeneration, keep the existing body and pull forward only boilerplate (version-stamp +header, `set` line, shellcheck-disable line). The same rule applies to any other `0*.sh` +found to contain real logic beyond the stub. + +## Entrypoint flow + +``` +tini → /usr/local/bin/entrypoint.sh +├─ Load /usr/local/etc/docker/functions/entrypoint.sh +├─ Source env: /root/env.sh, /usr/local/etc/docker/env/*.sh, /config/env/*.sh +├─ Seed /config and /data on first run +├─ __start_init_scripts — source every init.d/*.sh in sort order +├─ Handle `healthcheck` command +└─ Execute main application +``` + +`rootfs/usr/local/bin/` generated set: `entrypoint.sh`, `pkmgr`, `symlink`, `copy`, +`healthcheck`. `pkmgr` wraps the native package manager (`apk`, `apt-get`, `dnf`, +`pacman`) behind `pkmgr update|install|remove|clean`. + +## App-specific bin scripts + +Extra scripts in `rootfs/usr/local/bin/` that `gen-dockerfile` does not generate are +repo-owned. Their `@@Template` header governs maintenance: + +- `@@Template : shell/sh` — boilerplate synced from `$TEMPLATE_DIR/scripts/shell/sh`; + `#!/usr/bin/env sh`, `set -e` only (`pipefail` is a bashism — must NOT appear) +- `@@Template : shell/bash` — synced from `shell/bash`; `set -eo pipefail` required +- No `@@Template` header — hand-written; never modified by tooling + +## init.d scripts — critical rules + +**Each service gets its own numbered init.d script. Never merge or remove services.** +`__start_init_scripts` sources every `*.sh` in sort order — multi-process apps have one +script per daemon (e.g. gitea: `05-dockerd.sh`, `08-gitea.sh`, `zz-act_runner.sh`). + +init.d scripts are **regenerated, never patched in place** — old copies may call functions +removed from the current `functions/entrypoint.sh`. Generate fresh via +`gen-script other/start-service` (or `gen-dockerfile --startup`), then restore the +app-specific values. They are `#!/usr/bin/env bash` with `set -eo pipefail`. + +Required variables in every init.d script: -Users can override tool policy by bind-mounting a custom config: ```bash -docker run -v ./my-openclaw.json:/data/workspace/.openclaw/openclaw.json ... +SERVICE_NAME="myapp" +EXEC_CMD_BIN='myapp' +EXEC_CMD_ARGS='' +EXEC_PRE_SCRIPT='' +SERVICE_USES_PID='' +IS_WEB_SERVER="no" +IS_DATABASE_SERVICE="no" +USES_DATABASE_SERVICE="no" +DATABASE_SERVICE_TYPE="sqlite" +RUNAS_USER="root" ``` ---- +Directory variables: -## Technical Constraints +```bash +DATA_DIR="/data/$SERVICE_NAME" +CONF_DIR="/config/$SERVICE_NAME" +ETC_DIR="/etc/$SERVICE_NAME" +LOG_DIR="/data/logs/$SERVICE_NAME" +TMP_DIR="/tmp/$SERVICE_NAME" +RUN_DIR="/run/$SERVICE_NAME" +ROOT_FILE_PREFIX="/config/secure/auth/root" +USER_FILE_PREFIX="/config/secure/auth/user" +``` -- **CPU-optimized, GPU-optional** — Image ships zero GPU libraries. Ollama runs CPU inference by default. GPU is activated automatically when user passes `--gpus all` and NVIDIA Container Toolkit is installed on host. No image rebuild needed. -- **Direct binary installation only** — No `curl | sh` or `curl | bash` anywhere in the Dockerfile. All software installed via apt packages (Node.js via NodeSource repo with GPG key), direct binary download (Ollama from GitHub releases), and npm package manager (OpenClaw). -- **No version pinning** — Ollama uses `/releases/latest/download/`, OpenClaw uses `@latest`, Node.js uses `node_lts.x`. Each `docker build` picks up the newest stable versions. No version ARGs to maintain. -- **Multi-arch manifest** — Published image contains both `linux/amd64` and `linux/arm64`. Built with `docker buildx`. -- **Platform-specific performance:** - - **amd64:** Ollama leverages AVX/AVX2/AVX-512 SIMD when available (most x86_64 CPUs from 2013+). - - **arm64:** Ollama leverages NEON SIMD (all ARMv8+). Apple Silicon performs well; Raspberry Pi 5 is functional but slower. -- **Node.js LTS** required for OpenClaw (currently ≥ 22). -- **Single-process entrypoint pattern** — Ollama runs as background process, OpenClaw as foreground. Entrypoint handles process supervision, signal forwarding, and crash detection. No external supervisor (s6, supervisord) required. -- **Ollama must be healthy before OpenClaw starts** — Entrypoint polls `localhost:11434` with retry loop before launching OpenClaw. -- **No external API keys required** — entire stack is zero-cost by design. -- **Model storage** can be large (20B model ≈ 12-15 GB); `/data/ollama` volume mount is mandatory, not tmpfs. -- **RAM requirements (CPU inference):** - - 7B Q4 model: ~4-6 GB RAM minimum - - 13B Q4 model: ~8-10 GB RAM minimum - - 20B model: ~16 GB RAM minimum (default) - - Recommend at least 2 GB headroom above model size for OpenClaw + Node.js + OS. -- **RAM requirements (GPU inference):** Model must fit in VRAM. Partial offload (CPU+GPU split) is handled automatically by Ollama. -- **`apiKey: "ollama-local"`** — Ollama doesn't require auth but OpenClaw config requires a non-empty value; this is a dummy placeholder. -- **Soft vs hard restrictions** — OpenClaw's tool policy operates at tool granularity only. Git command restrictions are soft (TOOLS.md). `/etc` write protection is hard (OS-level chmod). For maximum safety, enable `tools.exec.ask: "always"`. -- **Image size target** — Under 500 MB compressed (excluding pulled models). +## Hook functions + +The `start-service` template generates all outer hooks fully implemented — customise via +the matching `*_local()` stub, which each outer hook calls automatically if defined: + +| Outer hook (do not redefine) | Customise via | +|------------------------------|---------------| +| `__run_precopy` | `__run_precopy_local` | +| `__execute_prerun` | `__execute_prerun_local` | +| `__run_pre_execute_checks` | `__run_pre_execute_checks_local` | +| `__update_conf_files` | `__update_conf_files_local` | +| `__pre_execute` | `__pre_execute_local` | +| `__post_execute` | `__post_execute_local` | +| `__pre_message` | `__pre_message_local` | +| `__update_ssl_conf` | `__update_ssl_conf_local` | +| `__create_service_env` | — | +| `__run_start_script` | — | +| `__run_secure_function` | — | + +## PID sentinel guard + +Every init.d script must guard on exactly this sentinel — leading dot, no underscores in +the filename portion; any other form silently skips the guard: + +```bash +if [ ! -f "/run/.start_init_scripts.pid" ]; then + echo "__start_init_scripts function hasn't been Initialized" >&2 + SERVICE_IS_RUNNING="no" + __script_exit 1 +fi +``` + +## Volumes + +- `/config` — persistent configuration +- `/data` — persistent application data --- -## Security Considerations +# PART 6: README.md STANDARD LAYOUT -- OpenClaw gateway should NOT be exposed to the public internet without authentication. -- Ollama API binds to `127.0.0.1` inside the container by default — not accessible from host unless explicitly exposed. -- All data stays local — no telemetry, no cloud calls, no API key leakage. -- Container runs as non-root user (`clawlama`) where possible. Entrypoint drops privileges after setup. -- OpenClaw's prompt injection surface is inherited; users should review OpenClaw's security docs before enabling messaging integrations. -- When GPU passthrough is enabled (`--gpus all`), the container gains access to host GPU devices — standard NVIDIA Container Toolkit security model applies. +App image layout (`casjaysdevdocker/{name}` → `casjaysdevdocker/{name}`). Substitute +`{name}` and `{port}` (the value of `SERVICE_PORT`); omit all `-p`/`ports:` sections only +in the rare case `SERVICE_PORT` is empty. + +**Hand-crafted README exception:** a repo whose README deliberately diverges from this +layout (full env-var tables, app-specific quick-start flags — e.g. gitea) owns its README. +Update its facts (image name, org, ports, URLs), never rewrite its structure back to the +generated layout. + +````markdown +## 👋 Welcome to {name} 🚀 + +{name} README + + +## Install my system scripts + +```shell + sudo bash -c "$(curl -q -LSsf "https://github.com/systemmgr/installer/raw/main/install.sh")" + sudo systemmgr --config && sudo systemmgr install scripts +``` + +## Automatic install/update + +```shell +dockermgr update {name} +``` + +## Install and run container + +```shell +dockerHome="/srv/$USER/docker/casjaysdevdocker/{name}/latest/volumes" +mkdir -p "$dockerHome" +git clone "https://github.com/dockermgr/{name}" "$HOME/.local/share/CasjaysDev/dockermgr/{name}" +cp -Rfva "$HOME/.local/share/CasjaysDev/dockermgr/{name}/volumes/." "$dockerHome/" +docker run -d \ +--restart always \ +--privileged \ +--name casjaysdevdocker-{name}-latest \ +--hostname {name} \ +-e TZ=${TIMEZONE:-America/New_York} \ +-v "$dockerHome/data:/data:z" \ +-v "$dockerHome/config:/config:z" \ +-p {port}:{port} \ +casjaysdevdocker/{name}:latest +``` + +## via docker-compose + +```yaml +services: + ProjectName: + image: casjaysdevdocker/{name} + container_name: casjaysdevdocker-{name} + environment: + - TZ=America/New_York + - HOSTNAME={name} + volumes: + - "/srv/$USER/docker/casjaysdevdocker/{name}/latest/volumes/data:/data:z" + - "/srv/$USER/docker/casjaysdevdocker/{name}/latest/volumes/config:/config:z" + ports: + - {port}:{port} + restart: always +``` + +## Get source files + +```shell +dockermgr download src casjaysdevdocker/{name} +``` + +OR + +```shell +git clone "https://github.com/casjaysdevdocker/{name}" "$HOME/Projects/github/casjaysdevdocker/{name}" +``` + +## Build container + +```shell +cd "$HOME/Projects/github/casjaysdevdocker/{name}" +buildx +``` + +## Authors + +🤖 casjay: [Github](https://github.com/casjay) 🤖 +⛵ casjaysdevdocker: [Github](https://github.com/casjaysdevdocker) [Docker](https://hub.docker.com/u/casjaysdevdocker) ⛵ +```` --- -## Success Criteria +# PART 7: CI/CD WORKFLOWS -1. `docker pull docker.io/casjaysdevdocker/clawlama:latest` succeeds on both amd64 and arm64 hosts. -2. `docker run -d -v clawlama-data:/data -p 18789:18789 docker.io/casjaysdevdocker/clawlama:latest` brings up both services with no manual intervention. -3. Ollama model is pulled automatically on first run using CPU inference. -4. OpenClaw agent responds to queries using the local Ollama model within 120 seconds of container start (CPU inference baseline; faster with GPU). -5. Changing `CLAWLAMA_MODEL` env var and restarting pulls the new model and regenerates config. -6. Container restart preserves all workspace data and downloaded models via `/data` volume. -7. `docker stop && docker start` recovers to working state. -8. Runs successfully on: x86_64 Linux server (cloud VM), Apple Silicon Mac (Docker Desktop), Raspberry Pi 5 (arm64). -9. When launched with `--gpus all` on an NVIDIA host, Ollama detects and uses GPU — verified in logs. -10. When launched without `--gpus` on any host, Ollama runs CPU-only — no GPU-related errors in logs. +## Generated workflow (`gen-dockerfile actions`) + +`gen-dockerfile actions` writes `.gitea/workflows/build.yml` from the current +`Dockerfile`. App repos get the single `build.yml` only — no versioned variants. All +actions are SHA-pinned — never tag-pinned. + +- **Triggers:** `push` to `main`, monthly schedule, `workflow_dispatch` +- **Registry strategy:** always logs in to the Gitea registry via the auto-provided + `GITEA_TOKEN`; conditionally logs in to Docker Hub when `vars.DOCKER_USERNAME` is set + (`vars.DOCKER_USERNAME` + `secrets.DOCKER_PASSWORD`; `vars.DOCKER_REGISTRY` overrides + the registry, `vars.DOCKER_ORG` the namespace) +- **Platforms:** `linux/amd64,linux/arm64` +- **build-args:** only `BUILD_DATE`, `GIT_COMMIT`, `BUILD_VERSION` +- **Tags pushed:** date tag (`yymm`) + `latest` to both registries +- **Annotations:** mirror the OCI label standard (PART 2), with `url`/`source`/ + `documentation` set to the workflow's repository URL + +## Legacy workflow (`docker.yaml`) + +A hand-crafted `.gitea/workflows/docker.yaml` may exist in older repos — reference copy in +the org-level `.github` repo. **Never overwrite it, and never use it as a template for new +work** — it uses tag-pinned actions and retired secret names. All new/updated workflows +come from `gen-dockerfile actions`. --- -## Out of Scope +# PART 8: VERIFICATION & COMMIT -- **Baking GPU libraries into the image** — GPU support is via NVIDIA Container Toolkit runtime passthrough only. -- **AMD ROCm / Intel Arc GPU support** — Only NVIDIA GPUs supported via container toolkit. -- Custom OpenClaw skill development (users add their own post-deploy). -- Building or fine-tuning custom Ollama models. -- Production-grade reverse proxy / TLS termination (user's responsibility). -- OpenClaw's built-in onboarding wizard (`openclaw onboard`) — replaced by container auto-config. -- iMessage / BlueBubbles / platform-specific integrations requiring host OS access. +## Syntax gates ---- +Every touched script must pass before commit: -## References +```bash +for f in rootfs/usr/local/bin/*; do + [ -f "$f" ] || continue + case "$(head -1 "$f")" in + *bash*) bash -n "$f" || exit 1 ;; + *sh*) sh -n "$f" || exit 1 ;; + esac +done -- [OpenClaw GitHub](https://github.com/openclaw/openclaw) -- [Ollama GitHub](https://github.com/ollama/ollama) -- [Source Gist — iam-veeramalla](https://gist.github.com/iam-veeramalla/9d10f968038ee76d5bc374b44f0cf8bb) -- [OpenClaw Docker Docs](https://github.com/openclaw/openclaw#docker) -- [Ollama Docker Image](https://hub.docker.com/r/ollama/ollama) +bash -n rootfs/usr/local/etc/docker/functions/entrypoint.sh + +for f in rootfs/root/docker/setup/0*.sh rootfs/usr/local/etc/docker/init.d/*.sh; do + [ -f "$f" ] || continue + bash -n "$f" || exit 1 +done +``` + +## Dead-reference gates + +After any regeneration: + +1. No script references an env var removed from `.env.scripts` (diff-driven check). +2. No script calls a function absent from both the current + `functions/entrypoint.sh` and the script itself. +3. No `__copy_templates` calls remain (retired with `DEFAULT_TEMPLATE_DIR`). +4. `Dockerfile` still pulls `FROM casjaysdev/*` (rule 8) — an upstream distro pull means + `GEN_DOCKERFILE_APP_DIR` resolved wrong during regeneration. + +## Commit + +```bash +git status --porcelain +git diff --stat +``` + +Write `.git/COMMIT_MESS` from the actual diff — subject ≤64 chars, body as +`- path: change` bullets covering every changed file. Then: + +```bash +gitcommit --dir "$(git rev-parse --show-toplevel)" all +``` + +`git commit` / `git push` directly are forbidden. Never commit with a failing syntax +gate. diff --git a/IDEA.md b/IDEA.md new file mode 100644 index 0000000..8727e37 --- /dev/null +++ b/IDEA.md @@ -0,0 +1,476 @@ +## Project Overview + +**ClawLama** is a CPU-optimized, multi-arch (amd64/arm64), single-container AI assistant that bundles [OpenClaw](https://github.com/openclaw/openclaw) and [Ollama](https://ollama.com) into one self-contained Docker image. Zero-cost, fully local, privacy-first — GPU accelerated when available, fully functional without it. + +**Image:** `docker.io/casjaysdevdocker/clawlama:latest` +**Base:** `debian:bookworm-slim` +**Platforms:** `linux/amd64`, `linux/arm64` +**Single container:** Both Ollama and OpenClaw run inside one image, no compose required. + +**Source Reference:** Based on [iam-veeramalla's OpenClaw + Ollama guide](https://gist.github.com/iam-veeramalla/9d10f968038ee76d5bc374b44f0cf8bb). + +--- + +## Problem Statement + +Running OpenClaw with a local Ollama model requires manual multi-step setup: installing OpenClaw, installing Ollama, pulling a model, writing a JSON config, and wiring everything together. ClawLama eliminates this friction by packaging everything into a single container — just `docker run`. + +--- + +## Architecture + +``` +┌───────────────────────────────────────────────────────────┐ +│ docker.io/casjaysdevdocker/clawlama:latest │ +│ debian:bookworm-slim | linux/amd64, linux/arm64 │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ entrypoint.sh │ │ +│ │ • Detect arch (amd64/arm64) + GPU (nvidia-smi) │ │ +│ │ • Generate openclaw.json from env vars │ │ +│ │ • Pull model if not cached │ │ +│ │ • Start Ollama (background) │ │ +│ │ • Wait for Ollama health │ │ +│ │ • Start OpenClaw gateway (foreground) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────┐ ┌───────────────────┐ │ +│ │ OpenClaw │───▶│ Ollama (CPU/GPU) │ │ +│ │ Gateway + │ │ localhost:11434 │ │ +│ │ Agent │ │ Auto-detects GPU │ │ +│ │ :18789 │ │ at runtime │ │ +│ └──────────────┘ └───────────────────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌────────────────┐ │ +│ │ /data/ │ │ /data/ │ │ +│ │ workspace/ │ │ ollama/ │ │ +│ │ (volume) │ │ (volume) │ │ +│ └─────────────┘ └────────────────┘ │ +└───────────────────────────────────────────────────────────┘ +``` + +### Single-Image Architecture + +Both Ollama and OpenClaw run inside one container. The entrypoint manages process lifecycle: + +1. **Ollama** starts as a background process, binding to `localhost:11434`. +2. **OpenClaw** starts as the foreground process after Ollama is healthy, connecting to `http://localhost:11434/v1`. +3. If Ollama crashes, the entrypoint detects it and exits (container restarts via Docker's restart policy). +4. Signals (SIGTERM/SIGINT) are forwarded to both processes for clean shutdown. + +### GPU Runtime Detection + +The image ships **no GPU libraries**. GPU acceleration is achieved through NVIDIA Container Toolkit runtime passthrough: + +- At startup, entrypoint runs `nvidia-smi` to detect GPU availability. +- **GPU found:** Ollama automatically uses CUDA via the mounted NVIDIA runtime. Logs report GPU model and VRAM. +- **No GPU:** Ollama falls back to CPU inference. No errors, no warnings — this is the expected default path. +- User enables GPU by passing `--gpus all` to `docker run` (requires NVIDIA Container Toolkit on host). + +### Ports + +- `18789` — OpenClaw gateway (exposed) +- `11434` — Ollama API (internal only by default; expose with `-p 11434:11434` for debugging) + +### Installation Method (No curl | sh) + +- **Ollama:** Latest release binary downloaded directly from GitHub at build time: `https://github.com/ollama/ollama/releases/latest/download/ollama-linux-${TARGETARCH}`. No version pinning — always gets the newest stable release. +- **OpenClaw:** Installed via `npm install -g openclaw@latest`. Always gets the newest published version. +- **Node.js:** Current LTS from [NodeSource apt repository](https://deb.nodesource.com) (`node_lts.x`) with GPG key verification. Auto-advances to next LTS major (e.g., 22 → 24) when Node promotes it. + +--- + +## Features + +1. **Single container, single command** — `docker run -d docker.io/casjaysdevdocker/clawlama:latest` launches both Ollama and OpenClaw. No compose file required for basic use. +2. **debian:bookworm-slim base** — Minimal Debian with glibc for full Ollama SIMD compatibility (AVX/AVX2 on amd64, NEON on arm64). +3. **Direct binary installation (no curl | sh):** + - Ollama: latest release binary from GitHub releases (`/releases/latest/download/`), selected per `TARGETARCH`. + - OpenClaw: `npm install -g openclaw@latest`. + - Node.js: current LTS from NodeSource apt repo (`node_lts.x`) with GPG key verification. +4. **Pre-configured OpenClaw ↔ Ollama wiring** — OpenClaw config auto-generated at startup pointing to `http://localhost:11434/v1` with zero-cost pricing. +5. **Persistent volumes** — Two mount points: `/data/ollama` (model store), `/data/workspace` (OpenClaw). Survive restarts. +6. **Default model: `gpt-oss:20b`** — Automatically pulled on first launch before OpenClaw starts. +7. **Health checks** — Container `HEALTHCHECK` verifies both Ollama API and OpenClaw gateway. +8. **Environment variable overrides:** + - `CLAWLAMA_MODEL` — Model to pull and use (default: `gpt-oss:20b`) + - `CLAWLAMA_CONTEXT_WINDOW` — Context window size (default: `131072`) + - `CLAWLAMA_MAX_TOKENS` — Max output tokens (default: `8192`) + - `CLAWLAMA_MAX_CONCURRENT` — Agent concurrency (default: `4`) + - `CLAWLAMA_SUBAGENT_CONCURRENT` — Subagent concurrency (default: `8`) + - `CLAWLAMA_OPENCLAW_PORT` — OpenClaw gateway port (default: `18789`) + - `OLLAMA_NUM_THREADS` — CPU threads for inference (default: auto-detect physical cores) + - `OLLAMA_NUM_PARALLEL` — Max parallel requests (default: `1`) + - `OLLAMA_MAX_LOADED_MODELS` — Models in memory (default: `1`) + - `OLLAMA_HOST` — Ollama bind address (default: `127.0.0.1:11434`) +9. **Multi-arch (amd64 + arm64)** — Single manifest tag built with `docker buildx`. Ollama binary selected by `TARGETARCH`. All scripts POSIX shell. +10. **Runtime GPU detection** — Entrypoint probes `nvidia-smi`. GPU used automatically if available via `--gpus all`. No GPU libs in image; NVIDIA Container Toolkit handles passthrough. CPU is the default and primary path. +11. **Model swap without rebuild** — Changing `CLAWLAMA_MODEL` and restarting pulls the new model and regenerates config. +12. **CPU performance auto-tuning** — Entrypoint auto-detects physical cores, available RAM, and sets `OLLAMA_NUM_THREADS` optimally if unset. Logs detected values. +13. **Telegram integration helper** — Optional `CLAWLAMA_TELEGRAM_BOT_TOKEN` env var auto-configures Telegram channel. +14. **Full tool profile with layered restrictions** — All OpenClaw tools enabled via `profile: "full"`. Git and `/etc` restrictions enforced via TOOLS.md (soft) and container filesystem (hard). +15. **Startup banner** — Print connection info, detected arch, CPU cores, RAM, GPU status, and model to stdout. +16. **Quantized model recommendations** — README documents CPU-friendly models by RAM tier: + - 8 GB RAM: 7B Q4 variants + - 16 GB RAM: `gpt-oss:20b` (default) or 13B Q5 + - 32+ GB RAM: 20B+ full or 34B Q4 +17. **docker-compose.yml included** — Provided for users who prefer compose, with volume mounts and restart policy pre-configured. +18. **Multi-model support** — Comma-separated `CLAWLAMA_MODELS` env var configures multiple models in the OpenClaw provider config. +19. **Backup/restore scripts** — Shell scripts to tar `/data` volumes for migration. +20. **Portainer/Dockge compatible** — Compose file works with popular Docker management UIs. +21. **Architecture detection in logs** — Log detected arch and SIMD instruction sets (AVX, AVX2, AVX-512, NEON) for performance troubleshooting. +22. **GPU VRAM-aware model selection** — When GPU detected, log VRAM and suggest optimal model/quantization for available resources. + +--- + +## File Structure + +``` +clawlama/ +├── AI.md # This spec +├── TODO.AI.md # Task tracking +├── Dockerfile # Multi-stage, multi-arch (amd64 + arm64) +├── docker-compose.yml # Optional compose file for convenience +├── .env.example # Template environment variables +├── rootfs/ +│ ├── usr/local/bin/ +│ │ ├── entrypoint.sh # Main entrypoint: detect GPU, gen config, start services +│ │ └── healthcheck.sh # Health check script for HEALTHCHECK instruction +│ └── etc/clawlama/ +│ ├── openclaw.template.json # OpenClaw config template (envsubst-ready) +│ └── TOOLS.md # Agent tool usage rules (git deny, /etc deny) +├── scripts/ +│ ├── build.sh # Multi-arch buildx build + push +│ ├── backup.sh # Backup /data volumes +│ └── restore.sh # Restore /data volumes +└── README.md # User-facing documentation +``` + +--- + +## Quick Start + +```bash +# CPU-only (default) +docker run -d \ + --name clawlama \ + -v clawlama-data:/data \ + -p 18789:18789 \ + docker.io/casjaysdevdocker/clawlama:latest + +# With NVIDIA GPU acceleration +docker run -d \ + --name clawlama \ + --gpus all \ + -v clawlama-data:/data \ + -p 18789:18789 \ + docker.io/casjaysdevdocker/clawlama:latest + +# Custom model + expose Ollama API for debugging +docker run -d \ + --name clawlama \ + -v clawlama-data:/data \ + -p 18789:18789 \ + -p 11434:11434 \ + -e CLAWLAMA_MODEL=qwen2:7b \ + -e OLLAMA_HOST=0.0.0.0:11434 \ + docker.io/casjaysdevdocker/clawlama:latest +``` + +--- + +## Dockerfile Sketch + +```dockerfile +# ── Stage 1: Build dependencies ────────────────────────────── +FROM debian:bookworm-slim AS builder + +ARG TARGETARCH + +# Install Node.js LTS from NodeSource apt repo (no curl | sh) +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg gettext-base && \ + mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ + | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_lts.x nodistro main" \ + > /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && apt-get install -y --no-install-recommends nodejs && \ + rm -rf /var/lib/apt/lists/* + +# Download latest Ollama binary directly (no curl | sh, no version pinning) +RUN curl -fsSL -o /usr/local/bin/ollama \ + "https://github.com/ollama/ollama/releases/latest/download/ollama-linux-${TARGETARCH}" && \ + chmod +x /usr/local/bin/ollama + +# Install latest OpenClaw via npm +RUN npm install -g openclaw@latest + +# ── Stage 2: Runtime ───────────────────────────────────────── +FROM debian:bookworm-slim + +# Install Node.js LTS runtime (same repo method, no dev packages) +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg tini procps gettext-base && \ + mkdir -p /etc/apt/keyrings && \ + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ + | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_lts.x nodistro main" \ + > /etc/apt/sources.list.d/nodesource.list && \ + apt-get update && apt-get install -y --no-install-recommends nodejs && \ + rm -rf /var/lib/apt/lists/* + +# Copy Ollama binary +COPY --from=builder /usr/local/bin/ollama /usr/local/bin/ollama + +# Copy OpenClaw global install +COPY --from=builder /usr/lib/node_modules /usr/lib/node_modules +COPY --from=builder /usr/bin/openclaw /usr/bin/openclaw + +# Copy rootfs overlay +COPY rootfs/ / + +# Create non-root user and data directories +RUN groupadd -r clawlama && useradd -r -g clawlama -m clawlama && \ + mkdir -p /data/ollama /data/workspace && \ + chown -R clawlama:clawlama /data + +# Hard restriction: make /etc read-only for non-root users +RUN chmod -R a-w /etc + +# Environment defaults (CPU-optimized) +ENV CLAWLAMA_MODEL=gpt-oss:20b \ + CLAWLAMA_CONTEXT_WINDOW=131072 \ + CLAWLAMA_MAX_TOKENS=8192 \ + CLAWLAMA_MAX_CONCURRENT=4 \ + CLAWLAMA_SUBAGENT_CONCURRENT=8 \ + CLAWLAMA_OPENCLAW_PORT=18789 \ + OLLAMA_HOST=127.0.0.1:11434 \ + OLLAMA_MODELS=/data/ollama \ + OLLAMA_NUM_PARALLEL=1 \ + OLLAMA_MAX_LOADED_MODELS=1 + +VOLUME ["/data"] +EXPOSE 18789 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \ + CMD /usr/local/bin/healthcheck.sh + +ENTRYPOINT ["tini", "--"] +CMD ["/usr/local/bin/entrypoint.sh"] +``` + +**Notes:** +- `tini` is the PID 1 init for proper signal handling — no zombie processes. +- `TARGETARCH` is automatically set by `docker buildx` (`amd64` or `arm64`). +- `curl` used only for apt key download and direct binary fetch — never piped to shell. +- Builder stage is discarded; runtime image contains only what's needed. +- `OLLAMA_MODELS=/data/ollama` ensures models persist in the volume. +- `/etc` made non-writable via `chmod` — hard enforcement of `/etc` write protection. +- Entrypoint copies `TOOLS.md` into `/data/workspace/TOOLS.md` on first run (OpenClaw reads this as agent instructions). + +--- + +## OpenClaw Configuration + +The following JSON config is generated at container startup from environment variables via `envsubst`: + +```json +{ + "models": { + "providers": { + "ollama": { + "baseUrl": "http://localhost:11434/v1", + "apiKey": "ollama-local", + "api": "openai-completions", + "models": [ + { + "id": "${CLAWLAMA_MODEL}", + "name": "${CLAWLAMA_MODEL}", + "reasoning": false, + "input": ["text"], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": ${CLAWLAMA_CONTEXT_WINDOW}, + "maxTokens": ${CLAWLAMA_MAX_TOKENS} + } + ] + } + } + }, + "agents": { + "defaults": { + "model": { + "primary": "ollama/${CLAWLAMA_MODEL}" + }, + "workspace": "/data/workspace", + "maxConcurrent": ${CLAWLAMA_MAX_CONCURRENT}, + "subagents": { + "maxConcurrent": ${CLAWLAMA_SUBAGENT_CONCURRENT} + } + } + }, + "tools": { + "profile": "full", + "exec": { + "security": "full", + "ask": "off", + "backgroundMs": 10000, + "timeoutSec": 1800, + "applyPatch": { + "enabled": true, + "workspaceOnly": true + } + }, + "fs": { + "workspaceOnly": false + }, + "elevated": { + "enabled": false + } + } +} +``` + +### Tool Permission Policy + +OpenClaw's tool policy operates at the **tool level** (allow/deny entire tools like `exec`, `read`, `write`), not at the command or path level. To enforce the desired restrictions (no `git commit/push/reset --hard`, no writes to `/etc`), ClawLama uses a **layered approach**: + +| Layer | Mechanism | What It Enforces | +|-------|-----------|------------------| +| **Tool profile** | `tools.profile: "full"` | All tools enabled: `group:fs`, `group:runtime`, `group:ui`, `group:sessions`, `group:memory`, `group:automation`, `web_search`, `web_fetch` | +| **Exec security** | `tools.exec.security: "full"` | Shell commands auto-approved (no prompt). Agent has full exec access. | +| **Workspace TOOLS.md** | Agent instruction file | Soft restrictions: instructs agent to never run `git commit`, `git push`, `git reset --hard` | +| **Container filesystem** | Dockerfile `RUN chmod` / read-only mounts | Hard restriction: `/etc` read-only at container level, preventing writes regardless of agent behavior | +| **Elevated mode** | `tools.elevated.enabled: false` | No host-level exec breakout (good hygiene) | +| **Workspace scope** | `tools.exec.applyPatch.workspaceOnly: true` | `apply_patch` operations restricted to workspace directory | + +### Tool Groups (Reference) + +OpenClaw's built-in tool groups for use in `tools.allow` / `tools.deny`: + +| Group | Tools | +|-------|-------| +| `group:runtime` | `exec`, `bash`, `process` | +| `group:fs` | `read`, `write`, `edit`, `apply_patch` | +| `group:sessions` | `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `session_status` | +| `group:memory` | `memory_search`, `memory_get` | +| `group:ui` | `browser`, `canvas` | +| `group:automation` | `cron`, `gateway` | +| `group:messaging` | `message` | +| `group:nodes` | `nodes` | + +### Git Restrictions (via TOOLS.md) + +Since OpenClaw has no command-pattern deny list for `exec`, git restrictions are enforced via the workspace `TOOLS.md` file — an agent instruction document that OpenClaw injects into the system prompt: + +```markdown + +# Tool Usage Rules + +## Git Restrictions (MANDATORY) +- NEVER run `git commit` in any form +- NEVER run `git push` in any form +- NEVER run `git reset --hard` in any form +- All other git commands are allowed (status, diff, log, add, branch, checkout, clone, pull, stash, etc.) + +## Filesystem Restrictions +- Do NOT write to or delete files in /etc/ +- The /etc directory is read-only at the container level +``` + +**Important:** `TOOLS.md` is a **soft restriction** — the LLM is instructed not to run these commands, but it is not technically blocked by OpenClaw's tool policy engine. For hard enforcement, users should enable exec approvals (`tools.exec.ask: "always"`). The container-level `/etc` read-only mount is a **hard restriction** regardless. + +### `/etc` Protection (via Container) + +Since OpenClaw's `tools.fs.workspaceOnly` is an all-or-nothing toggle, and we want reads everywhere but writes denied only to `/etc`, this is enforced at the **Docker layer**: + +```dockerfile +RUN chmod -R a-w /etc +``` + +### Override Path + +Users can override tool policy by bind-mounting a custom config: +```bash +docker run -v ./my-openclaw.json:/data/workspace/.openclaw/openclaw.json ... +``` + +--- + +## Technical Constraints + +- **CPU-optimized, GPU-optional** — Image ships zero GPU libraries. Ollama runs CPU inference by default. GPU is activated automatically when user passes `--gpus all` and NVIDIA Container Toolkit is installed on host. No image rebuild needed. +- **Direct binary installation only** — No `curl | sh` or `curl | bash` anywhere in the Dockerfile. All software installed via apt packages (Node.js via NodeSource repo with GPG key), direct binary download (Ollama from GitHub releases), and npm package manager (OpenClaw). +- **No version pinning** — Ollama uses `/releases/latest/download/`, OpenClaw uses `@latest`, Node.js uses `node_lts.x`. Each `docker build` picks up the newest stable versions. No version ARGs to maintain. +- **Multi-arch manifest** — Published image contains both `linux/amd64` and `linux/arm64`. Built with `docker buildx`. +- **Platform-specific performance:** + - **amd64:** Ollama leverages AVX/AVX2/AVX-512 SIMD when available (most x86_64 CPUs from 2013+). + - **arm64:** Ollama leverages NEON SIMD (all ARMv8+). Apple Silicon performs well; Raspberry Pi 5 is functional but slower. +- **Node.js LTS** required for OpenClaw (currently ≥ 22). +- **Single-process entrypoint pattern** — Ollama runs as background process, OpenClaw as foreground. Entrypoint handles process supervision, signal forwarding, and crash detection. No external supervisor (s6, supervisord) required. +- **Ollama must be healthy before OpenClaw starts** — Entrypoint polls `localhost:11434` with retry loop before launching OpenClaw. +- **No external API keys required** — entire stack is zero-cost by design. +- **Model storage** can be large (20B model ≈ 12-15 GB); `/data/ollama` volume mount is mandatory, not tmpfs. +- **RAM requirements (CPU inference):** + - 7B Q4 model: ~4-6 GB RAM minimum + - 13B Q4 model: ~8-10 GB RAM minimum + - 20B model: ~16 GB RAM minimum (default) + - Recommend at least 2 GB headroom above model size for OpenClaw + Node.js + OS. +- **RAM requirements (GPU inference):** Model must fit in VRAM. Partial offload (CPU+GPU split) is handled automatically by Ollama. +- **`apiKey: "ollama-local"`** — Ollama doesn't require auth but OpenClaw config requires a non-empty value; this is a dummy placeholder. +- **Soft vs hard restrictions** — OpenClaw's tool policy operates at tool granularity only. Git command restrictions are soft (TOOLS.md). `/etc` write protection is hard (OS-level chmod). For maximum safety, enable `tools.exec.ask: "always"`. +- **Image size target** — Under 500 MB compressed (excluding pulled models). + +--- + +## Security Considerations + +- OpenClaw gateway should NOT be exposed to the public internet without authentication. +- Ollama API binds to `127.0.0.1` inside the container by default — not accessible from host unless explicitly exposed. +- All data stays local — no telemetry, no cloud calls, no API key leakage. +- Container runs as non-root user (`clawlama`) where possible. Entrypoint drops privileges after setup. +- OpenClaw's prompt injection surface is inherited; users should review OpenClaw's security docs before enabling messaging integrations. +- When GPU passthrough is enabled (`--gpus all`), the container gains access to host GPU devices — standard NVIDIA Container Toolkit security model applies. + +--- + +## Success Criteria + +1. `docker pull docker.io/casjaysdevdocker/clawlama:latest` succeeds on both amd64 and arm64 hosts. +2. `docker run -d -v clawlama-data:/data -p 18789:18789 docker.io/casjaysdevdocker/clawlama:latest` brings up both services with no manual intervention. +3. Ollama model is pulled automatically on first run using CPU inference. +4. OpenClaw agent responds to queries using the local Ollama model within 120 seconds of container start (CPU inference baseline; faster with GPU). +5. Changing `CLAWLAMA_MODEL` env var and restarting pulls the new model and regenerates config. +6. Container restart preserves all workspace data and downloaded models via `/data` volume. +7. `docker stop && docker start` recovers to working state. +8. Runs successfully on: x86_64 Linux server (cloud VM), Apple Silicon Mac (Docker Desktop), Raspberry Pi 5 (arm64). +9. When launched with `--gpus all` on an NVIDIA host, Ollama detects and uses GPU — verified in logs. +10. When launched without `--gpus` on any host, Ollama runs CPU-only — no GPU-related errors in logs. + +--- + +## Out of Scope + +- **Baking GPU libraries into the image** — GPU support is via NVIDIA Container Toolkit runtime passthrough only. +- **AMD ROCm / Intel Arc GPU support** — Only NVIDIA GPUs supported via container toolkit. +- Custom OpenClaw skill development (users add their own post-deploy). +- Building or fine-tuning custom Ollama models. +- Production-grade reverse proxy / TLS termination (user's responsibility). +- OpenClaw's built-in onboarding wizard (`openclaw onboard`) — replaced by container auto-config. +- iMessage / BlueBubbles / platform-specific integrations requiring host OS access. + +--- + +## References + +- [OpenClaw GitHub](https://github.com/openclaw/openclaw) +- [Ollama GitHub](https://github.com/ollama/ollama) +- [Source Gist — iam-veeramalla](https://gist.github.com/iam-veeramalla/9d10f968038ee76d5bc374b44f0cf8bb) +- [OpenClaw Docker Docs](https://github.com/openclaw/openclaw#docker) +- [Ollama Docker Image](https://hub.docker.com/r/ollama/ollama)