Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions .devcontainer/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,20 @@ Claude that *uses* the sandbox, not the one editing this repo.)
- **Everything external is PINNED + integrity-gated; bumps are deliberate.** Base
image digest-pinned; yq/lazygit by `*_VER` + SHA-256, AWS CLI by GPG signature,
cargo-binstall by release tag (not `main`), pnpm / npm-globals / uv via Dockerfile
ARGs, zsh plugins by tag + asserted commit SHA; the two third-party apt keys
(GitHub CLI, PGDG) fingerprint-verified. Bump a version *and* its paired checksum
together — a mismatch fails the build by design. Keep the `SHELL [… -o pipefail …]`
line (DL4006 fix) so `curl | sh` layers stay fail-closed. Don't revert any to
floating `latest`/`main`.
ARGs, zsh plugins by tag + asserted commit SHA; Temurin OpenJDK by release tag +
per-arch SHA-256; the two third-party apt keys (GitHub CLI, PGDG)
fingerprint-verified. Bump a version *and* its paired checksum together — a
mismatch fails the build by design. Keep the `SHELL [… -o pipefail …]` line
(DL4006 fix) so `curl | sh` layers stay fail-closed. Don't revert any to floating
`latest`/`main`.
- **JDK is pinned to OpenJDK *11* on purpose — do NOT bump to 17+.** Section 5 bakes
Eclipse Temurin 11 (for the IBKR `clientportal.gw` gateway) from Adoptium's GitHub
releases. The gateway's bundled netty-4.1.15 reflectively accesses
`java.nio.DirectByteBuffer`: a harmless warning on 11, a fatal
`InaccessibleObjectException` on 17+ (Debian bookworm's only apt OpenJDK), which
would force `--add-opens` flags in `run.sh`. `update-alternatives` puts java/javac
in `/usr/bin` (already on PATH); only `JAVA_HOME` gets an `ENV` — leave the
load-bearing final `PATH` untouched.
- **`.dockerignore` is default-deny** (ignore `*`, re-include only Dockerfile COPY
sources) to keep the generated `.env` (Postgres password) out of the build
context. Add a `!`-line for every new COPY source; don't widen to allow-all.
Expand Down
35 changes: 32 additions & 3 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,36 @@ RUN --mount=type=cache,target=/root/.npm \
chmod -R a+rX "$PLAYWRIGHT_BROWSERS_PATH"

# ---------------------------------------------------------------------------
# 5. Rename the base `node` user (uid/gid 1000) -> `claude`; wire sudo + dirs
# 5. Eclipse Temurin (OpenJDK) 11 — required by the IBKR Client Portal Gateway
# (`clientportal.gw`). Pinned to jdk-11.0.31+11 + per-arch SHA-256, pulled from
# Adoptium's GitHub releases. (At RUNTIME the egress firewall blocks the Adoptium
# API/CDN but allowlists GitHub release assets — moot here, the build runs
# pre-firewall, but it's why GitHub is the chosen distribution channel.)
# Do NOT bump to Java 17+: the gateway's bundled netty-4.1.15 reflectively
# accesses java.nio.DirectByteBuffer — a harmless warning on 11, a fatal
# InaccessibleObjectException on 17+ (would force --add-opens flags in run.sh).
# Bump the version + BOTH arch checksums together (mismatch fails the build).
# update-alternatives symlinks java/javac into /usr/bin (already on PATH), so
# only JAVA_HOME needs an ENV — the load-bearing final PATH (below) is untouched.
# ---------------------------------------------------------------------------
RUN set -eux; \
case "$(dpkg --print-architecture)" in \
arm64) JDK_ARCH=aarch64; JDK_SHA=257f4d39e060658fc2eb89a803ca43b3f337e64e253f2d94ebae1d85c9ef5f69 ;; \
amd64) JDK_ARCH=x64; JDK_SHA=1e9de64586b519c0a981319489257cabedd9457599f3823424a87c3158fbe939 ;; \
*) echo "ERROR: unsupported arch for JDK: $(dpkg --print-architecture)" >&2; exit 1 ;; \
esac; \
url="https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.31%2B11/OpenJDK11U-jdk_${JDK_ARCH}_linux_hotspot_11.0.31_11.tar.gz"; \
curl -fL -o /tmp/jdk11.tar.gz "$url"; \
echo "${JDK_SHA} /tmp/jdk11.tar.gz" | sha256sum -c -; \
mkdir -p /usr/lib/jvm; \
tar -xzf /tmp/jdk11.tar.gz -C /usr/lib/jvm; \
rm /tmp/jdk11.tar.gz; \
update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-11.0.31+11/bin/java 1100; \
update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-11.0.31+11/bin/javac 1100
ENV JAVA_HOME=/usr/lib/jvm/jdk-11.0.31+11

# ---------------------------------------------------------------------------
# 6. Rename the base `node` user (uid/gid 1000) -> `claude`; wire sudo + dirs
# ---------------------------------------------------------------------------
ARG USERNAME=claude
RUN set -eux; \
Expand Down Expand Up @@ -211,7 +240,7 @@ RUN set -eux; \
/usr/local/share/npm-global

# ---------------------------------------------------------------------------
# 6. Baked dotfiles (NOT under any volume path -> never shadowed) + seed + scripts
# 7. Baked dotfiles (NOT under any volume path -> never shadowed) + seed + scripts
# ---------------------------------------------------------------------------
COPY --chown=claude:claude home/.zshrc /home/claude/.zshrc
COPY --chown=claude:claude home/.config/ /home/claude/.config/
Expand Down Expand Up @@ -253,7 +282,7 @@ RUN set -eux; \
find /home/claude/.config -type f -exec sed -i 's/\r$//' {} +

# ---------------------------------------------------------------------------
# 7. Per-user runtime: Python (uv), Claude Code (native installer + auto-update)
# 8. Per-user runtime: Python (uv), Claude Code (native installer + auto-update)
# ---------------------------------------------------------------------------
USER claude
ENV HOME=/home/claude \
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ smoke: env allowlist ## Build + boot the image (permissive firewall) and assert
docker exec claude-code bash -lc 'command -v python3 | grep -q "^/home/claude/.local/bin/"'
docker exec claude-code bash -lc 'python3 --version | grep -q "3.14"'
docker exec claude-code bash -lc 'command -v rg fd bat jq yq aws lazygit'
docker exec claude-code bash -lc 'java -version 2>&1 | grep -q "Temurin-11.0.31"'
docker exec claude-code bash -lc '[ "$$JAVA_HOME" = /usr/lib/jvm/jdk-11.0.31+11 ]'
docker exec claude-code test -f /home/claude/.claude/ENVIRONMENT.md
@echo "smoke OK"

Expand Down
Loading