Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
117 changes: 117 additions & 0 deletions .env.openclaw.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# These keys are read by docker-compose-openclaw.yml. Provider keys are
# duplicated here so the OpenClaw gateway has its own copy independent of
# whatever MC uses; in practice you can put the keys in `.env` once and both
# stacks pick them up.

# -----------------------------------------------------------------------------
# Gateway auth
# -----------------------------------------------------------------------------
# Leave empty on first start: openclaw will auto-generate a token, write it
# to .openclaw-data/openclaw.json (gateway.auth.token), and you can copy it
# back here with: TOKEN=$(make openclaw-token); echo "OPENCLAW_GATEWAY_TOKEN=$TOKEN" >> .env
OPENCLAW_GATEWAY_TOKEN=...

# Bind mode: 'lan' (default — listens on all interfaces, requires token),
# 'loopback' (localhost only — for single-host MC <-> gateway),
# 'tailnet' (tailscale net0).
OPENCLAW_GATEWAY_BIND=lan

# Disable Bonjour/mDNS in containers (auto-detected, but explicit is safer).
OPENCLAW_DISABLE_BONJOUR=1

# Security-hardening projection toggles (applied to gateway + MC CLI shim state).
# These become defaults in both:
# .openclaw-data/openclaw.json
# .mc-openclaw/openclaw.json
# Truthy: 1,true,yes,on | Falsy: 0,false,no,off
OPENCLAW_TOOLS_PROFILE=coding
OPENCLAW_SECURITY_WORKSPACE_ONLY=1
OPENCLAW_SECURITY_DENY_AUTOMATION=1
OPENCLAW_SECURITY_DENY_RUNTIME=1
# IMPORTANT: enabling group:fs deny can break normal agent file workflows.
# Keep disabled unless you explicitly want fully read/write-denied FS tooling.
# *(включи 1, если хочешь максимально жёстко)*
OPENCLAW_SECURITY_DENY_FS=0
OPENCLAW_SECURITY_SANDBOX_ALL=1
# Sandbox requires Docker socket access; the compose stack mounts /var/run/docker.sock read/write when enabled.
# Optional: set DOCKER_SOCKET_GID to the host gid for /var/run/docker.sock if the container's Docker group differs;
# otherwise it auto-detects via stat on the mounted socket.
DOCKER_SOCKET_GID=
# When set, projects the visibleReplies policy into OpenClaw state (automatic|message_tool).
# If left unset and the state contains "message_tool", startup will coerce it
# back to "automatic" to silence doctor-visibleReplies warnings.
OPENCLAW_MESSAGES_GROUPCHAT_VISIBLE_REPLIES=automatic

# --- OpenClaw: внутренние/bridge порты ---
# Container-internal listener ports (keep defaults unless you know why).
OPENCLAW_GATEWAY_INTERNAL_PORT=18789
# Внешний порт bridge на хосте.
OPENCLAW_BRIDGE_PORT=18790
# Внутренний порт bridge внутри контейнера.
OPENCLAW_BRIDGE_INTERNAL_PORT=18790


# --- OpenClaw: внешние порты (host bind) ---
# Внешний порт OpenClaw gateway.
# Host port mappings — must match what MC uses (defaults already do).
OPENCLAW_GATEWAY_PORT=18789
# Внешний порт панели управления OpenClaw (Control UI).
OPENCLAW_CONTROL_UI_PORT=18791



# Makefile health/status probe host for local checks.
OPENCLAW_STATUS_HOST=127.0.0.1

OPENCLAW_SKIP_ONBOARDING=1

# ─── Local Development ────────────────────────────────────────────────────────
# Both MC and gateway run on the same machine. No special config needed.
OPENCLAW_GATEWAY_HOST=127.0.0.1


# -----------------------------------------------------------------------------
# Optional misc
# -----------------------------------------------------------------------------
OPENCLAW_TZ=UTC
OPENCLAW_IMAGE=mc-openclaw:local


# -----------------------------------------------------------------------------
# Provider keys (give the gateway access to the LLM providers it should route)
# -----------------------------------------------------------------------------
OPENAI_API_KEY='sk-...'
# ANTHROPIC_API_KEY=sk-ant-...
# GEMINI_API_KEY=...
# OPENROUTER_API_KEY=sk-or-...


# Telegram bootstrap integration (optional).
# TELEGRAM_DM_POLICY controls incoming DM posture:
# - pairing (secure default; new users require pairing approval)
# - allowlist (only numeric IDs in TELEGRAM_ALLOW_FROM are allowed)
# - open (allow all DMs; least secure)
# TELEGRAM_ALLOW_FROM expects comma-separated numeric ids.
# TELEGRAM_OWNER_ALLOW_FROM expects comma-separated values:
# - telegram:<id>
# - <id> (auto-normalized to telegram:<id>)
# TELEGRAM_NUMERIC_USER_ID remains supported for compatibility and is merged
# into both allowlists.
# When TELEGRAM_BOT_TOKEN is set, b# When TELEGRAM_BOT_TOKEN is set, bootstrap projects:
# channels.telegram.botToken -> env ref TELEGRAM_BOT_TOKEN
# When TELEGRAM_NUMERIC_USER_ID is also set, bootstrap enforces:
# commands.ownerAllowFrom += ["telegram:<id>"]
# channels.telegram.allowFrom += ["<id>"]
# channels.telegram.dmPolicy = "allowlist"
# No wildcard ACLs are added.
# Токен Telegram-бота. Формат: 123456789:AA... (используйте заглушку до реальной настройки).
# t.me/Cooldown_hookah_bot
TELEGRAM_BOT_TOKEN=....:...
# Числовой Telegram user id владельца/оператора.
TELEGRAM_NUMERIC_USER_ID=1234567890
# новые аккаунты проходят через pairing approve flow
TELEGRAM_DM_POLICY=pairing
# только allowlist
# TELEGRAM_DM_POLICY=allowlist
# TELEGRAM_ALLOW_FROM=
# TELEGRAM_OWNER_ALLOW_FROM=
81 changes: 81 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Dockerfile.dev — image for `make dev` (hot-reload, no production build).
#
# Source code is bind-mounted from the host at runtime; this image only
# bakes the OS deps, pnpm, node_modules, and the host-fallback CLIs.
# Rebuild only when the dependency manifest or system tooling changes.
FROM node:22.22.0-slim
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app

# OS deps: better-sqlite3 build tools, agent-runtime probes, /chat PTY,
# docker CLI (talks to host daemon via bind-mounted /var/run/docker.sock —
# required so `openclaw doctor` can verify sandbox readiness without
# emitting the "Docker is not available" warning to the Doctor panel).
RUN apt-get update && apt-get upgrade && apt-get install -y \
curl ca-certificates python3 git make g++ procps tmux jq docker.io \
--no-install-recommends && rm -rf /var/lib/apt/lists/*

# Bake claude / codex CLIs as a fallback. The host's ~/.local/bin still wins
# at runtime via PATH order, so an authenticated host install transparently
# shadows these baked copies.
RUN npm install -g @anthropic-ai/claude-code @openai/codex 2>&1 | tail -5

# OpenClaw CLI is provided as a bind-mounted shim, NOT a baked npm install.
# `docker-compose-dev.yml` mounts the cloned openclaw-src/ at /opt/openclaw-src,
# and this shim runs `node /opt/openclaw-src/dist/index.js "$@"`. To update
# openclaw, run `make openclaw-update` on the host — it git-pulls openclaw-src
# and rebuilds dist via the openclaw-builder container; both this shim AND the
# gateway daemon pick up the new dist on the next call (no docker rebuild).
#
# The shim ALSO transparently rewrites legacy CLI shapes that MC source uses
# but that openclaw 2026.4.x has retired. Specifically MC's wake / agent
# message endpoints call `openclaw gateway sessions_send --session X --message
# Y`, but the daemon now exposes that only as the RPC method `chat.send`
# behind `openclaw gateway call chat.send --params {...}`. Rewriting in the
# shim keeps MC unmodified while staying compatible with new openclaw.
#
# The shim itself is bind-mounted from ./scripts/openclaw-cli-shim.py via
# docker-compose-dev.yml so edits land live without rebuilding this image.
RUN cat > /usr/local/bin/openclaw <<'SHIM' && chmod +x /usr/local/bin/openclaw
#!/bin/sh
# Live-loaded openclaw CLI shim. Source: /opt/openclaw-src (bind-mounted from
# host's openclaw-src/ clone). Updates via `make openclaw-update`.
if [ ! -f /opt/openclaw-src/dist/index.js ]; then
echo "openclaw shim: /opt/openclaw-src/dist/index.js not found." >&2
echo " Did you run 'make openclaw-build' to populate openclaw-src/dist/?" >&2
exit 127
fi
if [ -f /usr/local/lib/openclaw-cli-shim.py ]; then
exec python3 /usr/local/lib/openclaw-cli-shim.py "$@"
fi
exec node /opt/openclaw-src/dist/index.js "$@"
SHIM

# Install deps from manifests only — never from source. The source tree is
# bind-mounted at runtime, so changes to .ts/.tsx/.css don't invalidate this
# layer. Only changes to package.json / pnpm-lock.yaml trigger a rebuild.
COPY package.json pnpm-lock.yaml* ./
RUN if [ -f pnpm-lock.yaml ]; then \
pnpm install --frozen-lockfile; \
else \
echo "WARN: pnpm-lock.yaml not found in build context; running non-frozen install" && \
pnpm install --no-frozen-lockfile; \
fi

# Reuse uid 1000 (the slim image's `node` user) as `nextjs`, matching prod
# image exactly so bind-mounted host files keep their ownership.
RUN if ! id -u nextjs >/dev/null 2>&1; then \
usermod --login nextjs --move-home --home /home/nextjs node && \
groupmod --new-name nodejs node ; \
fi
RUN mkdir -p .data .next && chown -R nextjs:nodejs /app /home/nextjs

USER nextjs
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
ENV NODE_ENV=development
EXPOSE 3000

# Default command — overridden by docker-compose-dev.yml. Provided here so
# the image is also runnable standalone (`docker run mission-control-dev`).
CMD ["pnpm", "exec", "next", "dev", "--hostname", "0.0.0.0", "--port", "3000"]
47 changes: 47 additions & 0 deletions Dockerfile.openclaw.dockercli
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# MC custom gateway image — extends node:24-bookworm with:
# - docker CLI (so the gateway can spawn sandbox containers via the host
# docker socket mounted into this container)
# - Linuxbrew (so skills.install RPC can `brew install <formula>` for
# skills declaring formula deps — gh, ripgrep, starship, etc.)
#
# Why brew here AND in the sandbox image:
# - The skills.install RPC runs INSIDE the gateway, not the sandbox. Without
# brew here the Control UI shows "brew not installed" for any skill with a
# brew install button.
# - Skill execution at runtime happens INSIDE the spawned sandbox container,
# which has its own brew (Dockerfile.openclaw.sandbox). Both layers need
# it for different reasons.

FROM node:24-bookworm

ENV DEBIAN_FRONTEND=noninteractive

# Docker CLI + brew build deps (single apt layer for cache friendliness).
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
docker.io \
build-essential file procps sudo locales \
&& sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
&& locale-gen en_US.UTF-8 \
&& rm -rf /var/lib/apt/lists/*

ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8

# Linuxbrew installer refuses to run as root, so give the existing `node` user
# passwordless sudo and install brew under it. Brew lives at /home/linuxbrew
# regardless of the installer user, so root processes can still use it via PATH.
RUN echo 'node ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/node \
&& chmod 0440 /etc/sudoers.d/node

USER node
RUN NONINTERACTIVE=1 /bin/bash -c \
"curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | bash"

USER root
ENV PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:${PATH}" \
HOMEBREW_NO_AUTO_UPDATE=1 \
HOMEBREW_NO_ANALYTICS=1 \
HOMEBREW_NO_ENV_HINTS=1

# Smoke-test brew is callable from the runtime PATH the gateway will use.
RUN brew --version
65 changes: 65 additions & 0 deletions Dockerfile.openclaw.sandbox
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# syntax=docker/dockerfile:1.7
#
# MC custom sandbox image — extends the upstream openclaw sandbox with
# Linuxbrew so skills that declare a brew-formula install (gh, ripgrep,
# starship, etc.) work without manual host-side install.
#
# Why a separate Dockerfile (not patched into openclaw-src):
# The upstream image is built from openclaw-src/scripts/docker/sandbox/
# and we treat openclaw-src as read-only — only `git pull`, no edits.
# This file lives in MC and overlays brew on top of the upstream image,
# so version bumps of openclaw never lose our brew layer.
#
# Build:
# make openclaw-sandbox-build-brew
# or:
# # 1) ensure upstream sandbox is built (`make openclaw-sandbox-up`
# # bootstraps it via openclaw-src/scripts/sandbox-setup.sh)
# docker build -t mc-openclaw-sandbox:brew \
# -f Dockerfile.openclaw.sandbox .
#
# Use:
# .openclaw-data/openclaw.json:
# agents.defaults.sandbox.docker.image = "mc-openclaw-sandbox:brew"

ARG OPENCLAW_SANDBOX_BASE=openclaw-sandbox:bookworm-slim
FROM ${OPENCLAW_SANDBOX_BASE}

USER root
ENV DEBIAN_FRONTEND=noninteractive

# Brew build deps (debian/bookworm).
RUN --mount=type=cache,id=mc-sandbox-brew-apt-cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=mc-sandbox-brew-apt-lists,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential file procps sudo locales \
&& sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
&& locale-gen en_US.UTF-8

ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8

# Reuse the existing `sandbox` user from the upstream image; give it
# passwordless sudo so the Homebrew installer can chown its cellar dir.
RUN echo 'sandbox ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/sandbox \
&& chmod 0440 /etc/sudoers.d/sandbox

USER sandbox
WORKDIR /home/sandbox

# Install Homebrew (Linuxbrew). NONINTERACTIVE=1 skips the prompts the
# installer normally shows about sudo / paths.
RUN NONINTERACTIVE=1 /bin/bash -c \
"curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | bash"

ENV PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:${PATH}" \
HOMEBREW_NO_AUTO_UPDATE=1 \
HOMEBREW_NO_ANALYTICS=1 \
HOMEBREW_NO_ENV_HINTS=1

# Smoke-test brew + pre-warm the package index. Skip pre-installing
# anything heavy here — `make openclaw-sandbox-prewarm` (or the skill UI
# "Install …" buttons) install per-skill formulae on demand.
RUN brew --version && brew update --quiet || true

CMD ["sleep", "infinity"]
Loading