Skip to content

Commit a7ab950

Browse files
lroolleclaude
andcommitted
feat(trace): codex/grok --trace + container-scoped CA trust, cctrace 0.11
cctrace 0.11 traces Codex and Grok through client profiles; wire deva's --trace to all three agents and adopt #414's container CA model. - codex.sh/grok.sh: --trace wraps AGENT_COMMAND with `cctrace <client> --no-open -- ...`; always mitm - all traced sessions export DEVA_TRACE=1; the entrypoint installs the cctrace MITM CA into /usr/local/share/ca-certificates and runs update-ca-certificates, removing it on the next non-traced start. --print-ca runs as the deva user so CA material lands in the home with sane ownership - CCTRACE_VERSION 0.4.0 -> 0.11.0 in both Dockerfiles Container is the blast radius: the CA key already lives in the container-readable home; trusting the cert adds convenience, not attack surface. Host store untouched. Closes #418 Closes #414 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 1d5cc33 commit a7ab950

8 files changed

Lines changed: 111 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
against the agent config home (`~/.config/deva/<agent>/`) as a named
1313
credentials store; CWD checked first for compat. Provisioning a missing
1414
bare name creates it in the store (#417)
15+
- `--trace` for codex and grok via cctrace client profiles (#418).
16+
`deva codex -- --trace exec "..."` / `deva grok -- --trace -p "..."`
17+
wrap the agent with `cctrace <client> --no-open --`; always MITM capture
18+
- Container-scoped CA trust for traced sessions (#414): the entrypoint
19+
installs the cctrace MITM CA into the container system store when
20+
`DEVA_TRACE=1` and removes it on the next non-traced start. Covers
21+
subprocesses and tools that ignore the CA env vars; never touches the
22+
host trust store
23+
24+
### Changed
25+
- cctrace pin bumped 0.4.0 -> 0.11.0 (client profiles, shape-first
26+
categorization, `view --serve`)
1527

1628
## [0.14.1] - 2026-07-13
1729

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ FROM agent-base AS final
209209

210210
# Declare ARGs immediately before usage to minimize cache invalidation
211211
ARG CLAUDE_CODE_VERSION=2.1.143
212-
ARG CCTRACE_VERSION=0.4.0
212+
ARG CCTRACE_VERSION=0.11.0
213213
ARG CODEX_VERSION=0.131.0
214214
ARG GEMINI_CLI_VERSION=0.42.0
215215
ARG GROK_CLI_VERSION=0.2.93

Dockerfile.rust

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LABEL org.opencontainers.image.title="deva-rust"
99
LABEL org.opencontainers.image.description="Rust development environment with full toolchain"
1010

1111
ARG CLAUDE_CODE_VERSION=2.1.143
12-
ARG CCTRACE_VERSION=0.4.0
12+
ARG CCTRACE_VERSION=0.11.0
1313
ARG CODEX_VERSION=0.131.0
1414
ARG GEMINI_CLI_VERSION=0.42.0
1515
ARG GROK_CLI_VERSION=0.2.93

agents/claude.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ agent_prepare() {
4747
if [ "$use_trace" = true ]; then
4848
# Use cctrace wrapper: captures everything by default,
4949
# claude args go after "--"
50+
# DEVA_TRACE=1 tells the entrypoint to install the cctrace MITM CA
51+
# into the container system trust store (#414)
52+
DOCKER_ARGS+=("-e" "DEVA_TRACE=1")
5053
AGENT_COMMAND=("cctrace" "--no-open" "--")
5154
if [ "$has_dangerously" = false ]; then
5255
AGENT_COMMAND+=("--dangerously-skip-permissions")

agents/codex.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ agent_prepare() {
3333
AUTH_METHOD="$PARSED_AUTH_METHOD"
3434
local -a remaining_args=("${PARSED_REMAINING_ARGS[@]+"${PARSED_REMAINING_ARGS[@]}"}")
3535

36+
# Detect --trace flag (cctrace codex client profile, cctrace >= 0.11)
37+
local use_trace=false
38+
if [ ${#remaining_args[@]} -gt 0 ]; then
39+
local -a filtered_args=()
40+
local arg
41+
for arg in "${remaining_args[@]}"; do
42+
if [ "$arg" = "--trace" ]; then
43+
use_trace=true
44+
else
45+
filtered_args+=("$arg")
46+
fi
47+
done
48+
remaining_args=("${filtered_args[@]+"${filtered_args[@]}"}")
49+
fi
50+
3651
local has_dangerous=false
3752
local has_model=false
3853

@@ -74,6 +89,13 @@ agent_prepare() {
7489

7590
AGENT_COMMAND+=("${remaining_args[@]+"${remaining_args[@]}"}")
7691

92+
if [ "$use_trace" = true ]; then
93+
# cctrace codex profile: codex args go after "--"; always mitm.
94+
# DEVA_TRACE=1 installs the MITM CA into the container store (#414).
95+
DOCKER_ARGS+=("-e" "DEVA_TRACE=1")
96+
AGENT_COMMAND=("cctrace" "codex" "--no-open" "--" "${AGENT_COMMAND[@]:1}")
97+
fi
98+
7799
DOCKER_ARGS+=("-p" "127.0.0.1:1455:1455")
78100

79101
setup_codex_auth "$AUTH_METHOD"

agents/grok.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,32 @@ agent_prepare() {
1818
AUTH_METHOD="$PARSED_AUTH_METHOD"
1919
local -a remaining_args=("${PARSED_REMAINING_ARGS[@]+"${PARSED_REMAINING_ARGS[@]}"}")
2020

21+
# Detect --trace flag (cctrace grok client profile, cctrace >= 0.11)
22+
local use_trace=false
23+
if [ ${#remaining_args[@]} -gt 0 ]; then
24+
local -a filtered_args=()
25+
local arg
26+
for arg in "${remaining_args[@]}"; do
27+
if [ "$arg" = "--trace" ]; then
28+
use_trace=true
29+
else
30+
filtered_args+=("$arg")
31+
fi
32+
done
33+
remaining_args=("${filtered_args[@]+"${filtered_args[@]}"}")
34+
fi
35+
2136
AGENT_COMMAND+=("--always-approve")
2237

2338
AGENT_COMMAND+=("${remaining_args[@]+"${remaining_args[@]}"}")
2439

40+
if [ "$use_trace" = true ]; then
41+
# cctrace grok profile: grok args go after "--"; always mitm.
42+
# DEVA_TRACE=1 installs the MITM CA into the container store (#414).
43+
DOCKER_ARGS+=("-e" "DEVA_TRACE=1")
44+
AGENT_COMMAND=("cctrace" "grok" "--no-open" "--" "${AGENT_COMMAND[@]:1}")
45+
fi
46+
2547
setup_grok_auth "$AUTH_METHOD"
2648
}
2749

docker-entrypoint.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,42 @@ build_gosu_env_cmd() {
332332
exec gosu "$user" env "HOME=$DEVA_HOME" "PATH=$PATH" "$@"
333333
}
334334

335+
# Container-scoped CA trust for cctrace MITM (#414). The container is the
336+
# blast radius: the CA key already lives in the container-readable home, so
337+
# trusting the cert system-wide adds convenience, not attack surface. Runs
338+
# each start: installs when tracing, removes otherwise — trust exists only
339+
# in traced sessions.
340+
setup_trace_ca() {
341+
local crt=/usr/local/share/ca-certificates/cctrace-mitm.crt
342+
343+
if [ "${DEVA_TRACE:-}" != "1" ]; then
344+
if [ -f "$crt" ]; then
345+
rm -f "$crt"
346+
update-ca-certificates >/dev/null 2>&1 || true
347+
fi
348+
return 0
349+
fi
350+
351+
if ! command -v cctrace >/dev/null 2>&1; then
352+
echo "[entrypoint] warning: DEVA_TRACE=1 but cctrace not found; skipping CA trust" >&2
353+
return 0
354+
fi
355+
356+
# --print-ca generates the CA on first use; it must run as the deva user
357+
# so the key lands under $DEVA_HOME/.local/share/cctrace with sane ownership.
358+
local ca_path
359+
ca_path=$(gosu "$DEVA_USER" env "HOME=$DEVA_HOME" "PATH=$PATH" cctrace --print-ca 2>/dev/null | tail -1)
360+
if [ -z "$ca_path" ] || [ ! -f "$ca_path" ]; then
361+
echo "[entrypoint] warning: cctrace --print-ca gave no usable path; skipping CA trust" >&2
362+
return 0
363+
fi
364+
365+
cp "$ca_path" "$crt"
366+
update-ca-certificates >/dev/null 2>&1 || true
367+
[ "$VERBOSE" = "true" ] && echo "[entrypoint] cctrace MITM CA trusted container-wide: $crt"
368+
return 0
369+
}
370+
335371
ensure_agent_binaries() {
336372
case "$DEVA_AGENT" in
337373
claude)
@@ -385,6 +421,7 @@ main() {
385421
setup_claude_chrome_bridge
386422
fix_rust_permissions
387423
fix_docker_socket_permissions
424+
setup_trace_ca
388425
ensure_agent_binaries
389426

390427
if [ $# -eq 0 ]; then

docs/advanced-usage.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,21 @@ The printed `docker run` line is diagnostic output. It masks secrets and may con
165165

166166
```bash
167167
deva.sh claude -- --trace --continue
168+
deva.sh codex -- --trace exec "fix the failing tests"
169+
deva.sh grok -- --trace -p "explain this stack trace"
168170
```
169171

170-
`--trace` wraps Claude with [cctrace](https://github.com/thevibeworks/cctrace),
171-
which records every API call Claude makes — messages, OAuth, usage/credits,
172-
MCP — not just the chat endpoint. Everything else on the line goes to Claude
173-
unchanged.
172+
`--trace` wraps the agent with [cctrace](https://github.com/thevibeworks/cctrace),
173+
which records every API call the agent makes — messages, OAuth, usage/credits,
174+
MCP — not just the chat endpoint. Everything else on the line goes to the
175+
agent unchanged. Codex and Grok use cctrace client profiles (cctrace >= 0.11)
176+
and always run MITM capture.
177+
178+
When tracing is on, the entrypoint installs the cctrace MITM CA into the
179+
container's system trust store (`update-ca-certificates`) so subprocesses and
180+
tools that ignore `NODE_EXTRA_CA_CERTS`/`SSL_CERT_FILE` still verify. The
181+
container is the blast radius: trust never touches the host store and is
182+
removed at the next non-traced start.
174183

175184
Traces land in `.cctrace/` inside your workspace, so they survive the
176185
container. Each run writes a JSONL log plus a self-contained HTML snapshot you

0 commit comments

Comments
 (0)