first commit

This commit is contained in:
2026-07-08 18:23:00 -07:00
commit 1a69f28eb8
13 changed files with 1116 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
# AGENTS.md
## Purpose
Docker scaffold for running Claude Code in an isolated container (see README.md).
## Structure
- `.env` — gitignored, per-user, written by `claudaris config`. Holds `NAME`
(image/container name — the default `claudaris` is meant to be overridden
per person, e.g. `chris-claude`, so multiple users can each run their own
container off this same repo), `DATA_DIR`, and `WORKSPACE_DIR`. `claudaris`
sources it if present.
- `Dockerfile` — archlinux base image with bash, git, nodejs/npm, tmux, vim,
fastfetch, etc. `files/bash_aliases` is baked in at `/opt/dotfiles/bash_aliases`
and `files/bashrc` becomes the image's default `/root/.bashrc` — both are
rebuild-time defaults, but the host shadows both with its own single-file
bind mounts at runtime (see `claudaris start` below). Claude
Code is installed at build time straight into `/root` (`$HOME`), so a
rebuild always picks up the latest release. Staying logged in across
rebuilds is handled entirely by `claudaris start`, which bind-mounts two
individual host files onto `~/.claude/.credentials.json` (the OAuth token)
and `~/.claude.json` (account/onboarding state) — see `claudaris start`
below for why these are per-file mounts rather than a directory volume.
`git config --system --add safe.directory '*'` is set so git works on
`/projects` repos bind-mounted from the host (which the container, always
running as root, would otherwise treat as untrusted). `files/entrypoint.sh`
starts the tmux server and creates a `main` session, then polls and exits
once the session ends (tmux's defaults apply: `remain-on-exit` off,
`exit-empty` on, so a shell exiting — Ctrl+D, `exit`, crash — tears the
session down) rather than running forever; there's no `--restart` policy,
so a stopped container needs `claudaris connect` or `claudaris start` to
bring it back up (both do this automatically). `/etc/profile.d/nix_path.sh`
points `PATH` at `/root/.local/bin`. `files/tmux` is copied in as a
`/usr/local/bin/tmux` wrapper that shadows the real `tmux` binary so that
plain `docker exec -it "$NAME" tmux` runs `tmux new-session -A -s main`
(attaches to `main` if it exists, creates it otherwise) instead of always
starting a new session.
- `claudaris` — single entry point with subcommands:
- `config` (alias `configure`) — interactive wizard prompting for `NAME`,
`DATA_DIR`, `WORKSPACE_DIR` (showing current/default values, enter to
keep) and writing them to `.env`. Re-run any time to update it.
- `build` — builds the image, tagged `$NAME` (see `.env` above).
- `start` — runs the container as `$NAME` with `--hostname "$NAME"` (so the
shell prompt reads `root@$NAME`, e.g. `root@claudaris`, instead of a
random container ID). `/root/.bashrc`, `/opt/dotfiles/bash_aliases`, and
the two auth files (`~/.claude/.credentials.json`, `~/.claude.json`) are
each bind-mounted individually — seeded once (empty, for the auth files;
from `files/bashrc`/`files/bash_aliases`, for the dotfiles) into
`$DATA_DIR/home/{.bashrc,bash_aliases}` and
`$DATA_DIR/claude/{credentials,claude}.json` on the host, so they can be
edited/persist without a rebuild — including a coworker dropping in their
own aliases. Individual-file mounts matter for the auth files
specifically: Claude Code likely saves them atomically (write a temp
file, then `rename()` over the target), and `rename()` onto a symlink
replaces the symlink instead of writing through it — silently breaking
persistence after the first write. A bind mount doesn't have that
failure mode, which is why these aren't just symlinked from a directory
volume the way an earlier version of this setup did it. `$WORKSPACE_DIR`
is mounted at `/projects`. Also offers an opt-in `/root/.ssh` mount
(read-only) for reaching other nodes: create `$DATA_DIR/ssh` on the host
and populate it before starting the container to enable it. `DATA_DIR`
defaults to `/data/$NAME`; `WORKSPACE_DIR` defaults to
`/home/$USER/projects` (override via `claudaris config`).
- `connect``docker start`s `$NAME` (a no-op if it's already running)
then attaches to its tmux session, so it also works right after the
container has auto-exited (see `Dockerfile`/entrypoint above).
- `remove` (aliases `stop`, `rm`) — stops and removes the container so
a subsequent `start` recreates it fresh.
- `help` — usage.
- `files/` — plain (non-dot) source files `COPY`'d into the image at build
time: `bashrc`/`bash_aliases` (dotfiles — see `Dockerfile` above),
`entrypoint.sh`, and the `tmux` wrapper. Kept dotless so they're easy to
see/edit directly in a normal directory listing; the `Dockerfile` adds the
leading dot back on for `bashrc`'s destination. Both `bashrc` and
`bash_aliases` are also seeded onto the host by `claudaris start` so their
bind-mounted copies can diverge without a rebuild.
- `README.md` — usage instructions.
## Common commands
```bash
# build the image
docker build -t "$NAME" .
# start the container
docker run -d \
--name "$NAME" \
--hostname "$NAME" \
-v "$DATA_DIR/home/.bashrc:/root/.bashrc" \
-v "$DATA_DIR/home/bash_aliases:/opt/dotfiles/bash_aliases" \
-v "$DATA_DIR/claude/credentials.json:/root/.claude/.credentials.json" \
-v "$DATA_DIR/claude/claude.json:/root/.claude.json" \
-v "$WORKSPACE_DIR:/projects" \
"$NAME"
# attach to the running container's tmux session
docker exec -it "$NAME" tmux
```
## Notes
No test suite or CI — this is infra/config, not application code. Verify changes
by rebuilding the image (`./claudaris build`) and exec'ing in
(`./claudaris connect`) to confirm the container behaves as expected.