Initial commit

This commit is contained in:
2026-10-27 01:02:14 +01:00
commit ea23cf1a50
5 changed files with 203 additions and 0 deletions

26
.claudio Normal file
View File

@@ -0,0 +1,26 @@
# .claudio — project settings for claudio.
# Sourced as bash from the project directory. Everything here is optional;
# the project directory itself is always mounted at its own path.
# Extra mounts, passed to `podman run -v`:
# "/host/path" same path inside the container, read-write
# "/host/path:/container/path" different path
# "/host/path:/host/path:ro" same path, read-only
MOUNTS=(
# "$HOME/.gitconfig:/home/claude/.gitconfig:ro"
# "$HOME/shared-libs:$HOME/shared-libs:ro"
# "$PWD/../sibling-project"
)
# Extra `podman run` options (env vars, ports, resources, ...).
PODMAN_ARGS=(
# -e GITHUB_TOKEN
# -p 3000:3000
# --memory 4g
)
# Default arguments for claude itself.
CLAUDE_ARGS=(
# --dangerously-skip-permissions
# --model opus
)

24
Containerfile Normal file
View File

@@ -0,0 +1,24 @@
# claudio — minimal image for running Claude Code in isolation.
# Rebuild with `claudio update` to get the latest base packages and Claude Code.
FROM docker.io/library/debian:stable-slim
# Base tools Claude Code relies on. Add project-wide tools here (python3, nodejs, ...).
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates curl git ripgrep less procps openssh-client \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -m -u 1000 -s /bin/bash claude
# Native Claude Code binary, installed system-wide (not in $HOME) so that image
# rebuilds update it even though /home/claude is a persistent volume.
RUN curl -fsSL https://claude.ai/install.sh | HOME=/tmp/cc bash \
&& cp -L /tmp/cc/.local/bin/claude /usr/local/bin/claude \
&& rm -rf /tmp/cc
# Updates come from rebuilding the image, not from in-container self-updates.
ENV DISABLE_AUTOUPDATER=1 \
PATH=/home/claude/.local/bin:$PATH
USER claude
WORKDIR /home/claude
ENTRYPOINT ["claude"]

24
LICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>

68
README.md Normal file
View File

@@ -0,0 +1,68 @@
# claudio
Run Claude Code isolated in a podman container, one command per project.
## Files
- `Containerfile`: the image (Debian slim + Claude Code).
- `claudio.sh`: builds, updates and runs the container.
- `.claudio`: template for per-project settings (plain bash).
## Install
```sh
mkdir -p ~/claudio && cp Containerfile claudio.sh ~/claudio/
chmod +x ~/claudio/claudio.sh
echo 'alias claudio=~/claudio/claudio.sh' >> ~/.bashrc
```
Requires `podman` (rootless is fine).
## Usage
```sh
cd ~/projects/myapp
claudio # start Claude Code here (builds the image on first run)
claudio --resume # any extra args are passed to claude
claudio shell # bash inside the container instead of claude
claudio build # build the image
claudio update # rebuild from scratch: latest base image + latest Claude Code
```
The current directory is mounted at the same path inside the container.
Nothing else from the host is visible unless you add it to a `.claudio` file.
## Per-project settings
Copy the template into a project and uncomment what you need:
```sh
cp ~/claudio/.claudio ~/projects/myapp/
```
```bash
MOUNTS=("$HOME/.gitconfig:/home/claude/.gitconfig:ro" "/data/shared")
PODMAN_ARGS=(-e GITHUB_TOKEN -p 3000:3000)
CLAUDE_ARGS=(--model opus)
```
- `MOUNTS`: extra `-v` mounts. A bare path is mounted at the same path. Add `:ro` for read-only.
- `PODMAN_ARGS`: extra `podman run` options.
- `CLAUDE_ARGS`: default arguments for `claude`.
`.claudio` is sourced as bash, so only use ones you trust.
## State
The container's home (`/home/claude`) lives in the podman volume `claudio-home`.
This volume is shared by all projects and holds your login, settings and history.
The container itself is removed after every run.
- Reset everything: `podman volume rm claudio-home`
- Need more tools in the image? Add them to the `apt-get install` line, then run `claudio update`.
## Notes
- Files written in mounts are owned by your host user (`--userns=keep-id`).
- SELinux labeling is disabled for the container, so mounts work without relabeling.
- claudio refuses to run from `$HOME` or `/`.

61
claudio.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# claudio — run Claude Code isolated in a podman container.
#
# Install: keep this file next to the Containerfile, then
# alias claudio=/path/to/claudio.sh (e.g. in ~/.bashrc)
#
# Usage: claudio [claude args...] run Claude Code in the current directory
# claudio shell [bash args] open a bash shell in the container instead
# claudio build build the image
# claudio update rebuild from scratch (latest Debian + Claude Code)
#
# The current directory is mounted at its own path. If it contains a .claudio
# file, that file is sourced (bash) for extra mounts and options.
# The container's home (/home/claude: login, settings, history) lives in the
# global podman volume "claudio-home".
set -euo pipefail
IMAGE=localhost/claudio:latest
VOLUME=claudio-home
DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
build() { podman build -t "$IMAGE" "$@" "$DIR"; }
case "${1:-}" in
build) build; exit ;;
update) build --pull=always --no-cache && podman image prune -f; exit ;;
esac
if [[ "$PWD" == "$HOME" || "$PWD" == / ]]; then
echo "claudio: refusing to mount $PWD — run it from a project directory." >&2
exit 1
fi
podman image exists "$IMAGE" || build
# Project settings (see the .claudio template).
MOUNTS=() PODMAN_ARGS=() CLAUDE_ARGS=()
if [[ -f .claudio ]]; then source ./.claudio; fi
args=(-v "$PWD:$PWD" -w "$PWD" -v "$VOLUME:/home/claude")
for m in "${MOUNTS[@]}"; do
[[ "$m" == *:* ]] || m="$m:$m" # bare path -> same path inside the container
args+=(-v "$m")
done
if [[ "${1:-}" == shell ]]; then
shift
args+=(--entrypoint bash)
CLAUDE_ARGS=()
fi
if [[ -t 0 && -t 1 ]]; then args+=(-it); else args+=(-i); fi
# keep-id maps your host user to the container's uid 1000, so files written
# in mounts stay owned by you. label=disable avoids SELinux relabeling of mounts.
exec podman run --rm "${args[@]}" \
--userns=keep-id:uid=1000,gid=1000 \
--security-opt label=disable \
--hostname claudio \
"${PODMAN_ARGS[@]}" \
"$IMAGE" "${CLAUDE_ARGS[@]}" "$@"