diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 732c4705..c4f62491 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -36,6 +36,144 @@ docker run --rm -it \ Replace `vX.Y.Z` with a tag from [GitHub Releases](https://github.com/Hmbown/CodeWhale/releases). +## Default image contract + +`ghcr.io/hmbown/codewhale:latest` and the semver tags are conservative runtime +images: + +- the container runs as the non-root `codewhale` user with UID/GID `1000:1000` +- the image does not grant passwordless `sudo` +- the image is meant to run CodeWhale against mounted workspaces, not to mutate + the base operating system at runtime +- user state belongs in a volume mounted at `/home/codewhale/.deepseek` + +That default is intentional. Keep using it for the smallest trust boundary. If a +project needs `apt-get`, compiler toolchains, Node/Python package managers, +custom CA certificates, or other host-like setup inside Docker, build an +explicit toolbox image instead of changing the default image contract. + +## Opt-in toolbox/custom image + +The repository includes an example +[`docs/examples/Dockerfile.toolbox`](examples/Dockerfile.toolbox) that extends +the official image with passwordless `sudo` and common development packages. +Build it with a pinned CodeWhale tag when you want repeatable project +environments: + +```bash +docker build -f docs/examples/Dockerfile.toolbox \ + --build-arg CODEWHALE_IMAGE=ghcr.io/hmbown/codewhale:vX.Y.Z \ + --build-arg TOOLBOX_PACKAGES="git openssh-client curl build-essential pkg-config python3 python3-pip nodejs npm" \ + -t codewhale-toolbox:my-project . +``` + +Use `latest` only for throwaway testing. For shared projects, keep the +`CODEWHALE_IMAGE` value pinned and review package additions like any other +development-environment change. + +Run the toolbox image with the same workspace and state mounts: + +```bash +docker volume create codewhale-my-project-home + +docker run --rm -it \ + -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ + -v codewhale-my-project-home:/home/codewhale/.deepseek \ + -v "$PWD:/workspace" \ + -w /workspace \ + codewhale-toolbox:my-project +``` + +Inside this opt-in image, CodeWhale can use commands such as +`sudo apt-get update` and `sudo apt-get install -y `. For repeatable +containers, prefer baking those packages into the toolbox Dockerfile instead of +letting a long-lived container drift. + +Do not bake API keys, SSH private keys, or other secrets into custom images. +Pass API keys at runtime and mount any SSH material deliberately, preferably +read-only and only for projects that need it. + +## Multiple independent projects + +Use one named state volume per project so sessions, config, skills, memory, and +the offline queue do not bleed across workspaces: + +```bash +project="$(basename "$PWD")" +image="codewhale-toolbox:${project}" +docker volume create "codewhale-${project}-home" + +docker run --rm -it \ + --name "codewhale-${project}" \ + -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ + -v "codewhale-${project}-home:/home/codewhale/.deepseek" \ + -v "$PWD:/workspace" \ + -w /workspace \ + "$image" +``` + +For projects with different toolchains, build different toolbox tags, for +example `codewhale-toolbox:frontend` and `codewhale-toolbox:backend`. The +separate launcher idea discussed in issue #2217 can build on this contract, but +it is intentionally outside the core Docker image. + +## Project bootstrap scripts + +CodeWhale does not automatically execute `.deepseek/setup.sh` or +`.codewhale/setup.sh`. If you keep one of those files as a local project +recipe, run it explicitly. For shared team setup, prefer a committed project +script or the toolbox Dockerfile so the environment can be reviewed and +rebuilt. + +For example, to run a committed bootstrap script before starting CodeWhale: + +```bash +docker run --rm -it \ + -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ + -v codewhale-my-project-home:/home/codewhale/.deepseek \ + -v "$PWD:/workspace" \ + -w /workspace \ + --entrypoint bash \ + codewhale-toolbox:my-project \ + -lc './scripts/bootstrap-dev.sh && exec codewhale' +``` + +Use the toolbox image for bootstrap scripts that need `sudo`. The default image +will not elevate privileges. + +## Custom CA certificates and proxies + +For corporate proxies, dev-sidecar, or self-signed internal services, prefer +baking trusted CA certificates into a custom toolbox image: + +```dockerfile +USER root +COPY docker/certs/*.crt /usr/local/share/ca-certificates/ +RUN update-ca-certificates +USER codewhale +``` + +All files copied into `/usr/local/share/ca-certificates/` must use the `.crt` +extension. Keep private CA material out of public images. + +For a local-only run, mount certificates read-only and update the trust store at +container start: + +```bash +docker run --rm -it \ + -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ + -v codewhale-my-project-home:/home/codewhale/.deepseek \ + -v "$PWD:/workspace" \ + -v "$PWD/docker/certs:/usr/local/share/ca-certificates/local:ro" \ + -w /workspace \ + --entrypoint bash \ + codewhale-toolbox:my-project \ + -lc 'sudo update-ca-certificates && exec codewhale' +``` + +This CA workflow requires the opt-in toolbox image because the default image +does not include passwordless `sudo`. + ## Local build Build the image locally from a checkout: diff --git a/docs/examples/Dockerfile.toolbox b/docs/examples/Dockerfile.toolbox new file mode 100644 index 00000000..fab0b73e --- /dev/null +++ b/docs/examples/Dockerfile.toolbox @@ -0,0 +1,29 @@ +# syntax=docker/dockerfile:1 +# +# Opt-in CodeWhale toolbox image. +# +# The published ghcr.io/hmbown/codewhale:latest image intentionally stays +# minimal, non-root, and without passwordless sudo. Use this Dockerfile only for +# workspaces where you deliberately want package installation, custom CA setup, +# or project-specific build tools inside the container. +# +# Example: +# docker build -f docs/examples/Dockerfile.toolbox \ +# --build-arg CODEWHALE_IMAGE=ghcr.io/hmbown/codewhale:vX.Y.Z \ +# --build-arg TOOLBOX_PACKAGES="git openssh-client curl build-essential pkg-config python3 python3-pip nodejs npm" \ +# -t codewhale-toolbox:my-project . + +ARG CODEWHALE_IMAGE=ghcr.io/hmbown/codewhale:latest +FROM ${CODEWHALE_IMAGE} + +USER root + +ARG TOOLBOX_PACKAGES="git openssh-client curl build-essential pkg-config python3 python3-pip nodejs npm" +RUN apt-get update \ + && apt-get install -y --no-install-recommends sudo ${TOOLBOX_PACKAGES} \ + && rm -rf /var/lib/apt/lists/* \ + && printf '%s\n' 'codewhale ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/codewhale-nopasswd \ + && chmod 0440 /etc/sudoers.d/codewhale-nopasswd + +USER codewhale +WORKDIR /workspace