This is where the sharp tools live.
If you keep typing the same mounts and env vars, stop doing that.
Put them in .deva:
VOLUME=$HOME/.ssh:/home/deva/.ssh:ro
VOLUME=$HOME/.config/git:/home/deva/.config/git:ro
ENV=EDITOR=nvim
PROFILE=rust
Local override that should not be committed:
# .deva.local
ENV=GH_TOKEN=${GH_TOKEN}
Load order is:
$XDG_CONFIG_HOME/deva/.deva$HOME/.deva./.deva./.deva.local
See .deva.example.
This is the clean way to keep work and personal auth apart.
Leaf layout:
deva.sh claude -c ~/auth-homes/workDeva-root layout:
~/auth-roots/team-a/
├── claude/
├── codex/
└── gemini/
deva.sh claude -c ~/auth-roots/team-a
deva.sh codex -c ~/auth-roots/team-aIf you pass an explicit config home, deva does not also mount your default ~/.config/deva. That is deliberate isolation.
-Q is the clean-room mode:
- implies
--rm - no
.devaloading - no autolink
- no config-home mounts
Use it when you need a repro that is not contaminated by your local habits.
deva.sh claude -Q
deva.sh claude -Q -v "$PWD:/workspace" -- -p "summarize this repo"-Q and --config-home are mutually exclusive. They solve opposite problems.
If you want the agent to inspect more than it edits, mount most of the world read-only and give it one scratch path.
deva.sh claude \
-v "$PWD:/workspace:ro" \
-v "$HOME/.ssh:/home/deva/.ssh:ro" \
-v /tmp/deva-out:/home/deva/outThat is still not "safe" in some absolute sense. It is just a saner blast radius than handing over your laptop.
Supported profiles:
base->ghcr.io/thevibeworks/deva:latestrust->ghcr.io/thevibeworks/deva:rust
Use them like this:
deva.sh claude -p rust
deva.sh codex -p rustIf the image tag is missing locally, deva pulls it. If that fails and a matching Dockerfile exists, it points you at the build command.
If you want your own image entirely, read Custom Images. That covers local builds, private tags, per-project overrides, and personal-only setups.
One default container shape can serve all supported agents in the same project:
deva.sh claude
deva.sh codex
deva.sh geminiThat keeps package installs, build output, and scratch files hot between agents.
If you change volumes, config-home, or auth mode, deva intentionally uses a different persistent container instead of reusing one with the wrong mounts or env.
Current project:
deva.sh ps
deva.sh status
deva.sh shell
deva.sh stop
deva.sh rm
deva.sh cleanAll projects:
deva.sh ps -g
deva.sh shell -g
deva.sh stop -gThese are the three commands that matter:
deva.sh --show-config
deva.sh claude --debug --dry-run
deva.sh shellUse them in that order:
- inspect config resolution
- inspect Docker shape
- inspect the live container
The printed docker run line is diagnostic output. It masks secrets and may contain unquoted values. Read it. Do not blindly paste it back into a shell and then complain when your shell parses spaces like spaces.
Default behavior auto-mounts /var/run/docker.sock when it exists.
That means the container can control Docker on the host. Translation: host-root in practice.
Disable it:
deva.sh claude --no-dockerOr:
export DEVA_NO_DOCKER=1Use only when you need direct host networking behavior:
deva.sh claude --host-netAgain, this is not a subtle switch. It broadens what the container can see.
If you have a separate JSON credential file, pass the file itself:
deva.sh claude --auth-with ~/work/claude-prod.credentials.json
deva.sh codex --auth-with ~/work/codex-auth.json
deva.sh gemini --auth-with ~/keys/gcp-service-account.jsonDeva mounts the file onto the agent's expected credential path. It does not need to dump a directory full of backup junk into the container to make that work.