Skip to content

Commit 56f2457

Browse files
authored
Merge branch 'master' into update/scalatest-flatspec-3.2.20
2 parents f3ef09f + dfa3e0a commit 56f2457

98 files changed

Lines changed: 854 additions & 382 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/Dockerfile.app

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,61 @@
11
FROM mcr.microsoft.com/devcontainers/base:debian
22

3-
# gosu is used by the entrypoint to drop privileges.
43
# ca-certificates, curl, git are already in the devcontainers base image.
4+
# fd-find: fast file finder (aliased to fd below)
5+
# fzf: fuzzy finder for files and command history
6+
# gh: GitHub CLI
7+
# gosu: drops privileges in the entrypoint
8+
# jq: JSON processor
9+
# ripgrep: fast recursive grep (rg)
10+
# tmux: terminal multiplexer
11+
# vim: text editor
512
RUN apt-get update \
6-
&& apt-get install -y --no-install-recommends gh gosu jq vim \
13+
&& apt-get install -y --no-install-recommends fd-find fzf gh gosu jq ripgrep tmux vim \
14+
&& ln -s $(which fdfind) /usr/local/bin/fd \
715
&& rm -rf /var/lib/apt/lists/*
816

917
COPY --chmod=755 sandcat/scripts/app-init.sh /usr/local/bin/app-init.sh
1018
COPY --chmod=755 sandcat/scripts/app-user-init.sh /usr/local/bin/app-user-init.sh
19+
COPY --chown=vscode:vscode sandcat/tmux.conf /home/vscode/.tmux.conf
1120

1221
USER vscode
1322

14-
# Install mise (SDK manager), then use it to install Node.js and Claude Code.
23+
# Install Claude Code (native binary — no Node.js required).
24+
RUN curl -fsSL https://claude.ai/install.sh | bash
25+
26+
# Install mise (SDK manager) for language toolchains.
1527
RUN curl https://mise.run | sh
1628
# Make mise available in login shells (su - vscode) and Docker CMD/RUN.
1729
RUN echo 'export PATH="/home/vscode/.local/bin:/home/vscode/.local/share/mise/shims:$PATH"' >> /home/vscode/.profile
1830
ENV PATH="/home/vscode/.local/bin:/home/vscode/.local/share/mise/shims:$PATH"
19-
RUN mise use -g node@lts \
20-
&& npm install -g @anthropic-ai/claude-code
21-
22-
RUN mise use -g java@21
23-
RUN mise use -g sbt@1.12
2431

25-
# If Java was installed above, bake JAVA_HOME and trust-store paths into the
26-
# image. VS Code may probe the environment before the entrypoint finishes
27-
# importing the mitmproxy CA; having these ready avoids a race where Metals
28-
# (or other JVM tooling) starts without JAVA_HOME or JAVA_TOOL_OPTIONS.
29-
# The entrypoint will import the mitmproxy CA into the cacerts copy at runtime.
32+
# Development stacks (managed by sandcat init --stacks):
33+
RUN mise use -g java@lts
34+
RUN mise use -g scala@latest && mise use -g sbt@latest
35+
# END STACKS
36+
37+
# If Java was installed above, bake JAVA_HOME and JAVA_TOOL_OPTIONS into
38+
# .bashrc so VS Code's env probe picks them up before the entrypoint runs.
39+
# Without JAVA_HOME, JVM tooling like Metals fails to find the JDK.
40+
# JAVA_TOOL_OPTIONS points to a trust store copy that the entrypoint will
41+
# populate with the mitmproxy CA at runtime; until then it holds the default
42+
# Java CAs (harmless — equivalent to not setting it at all).
43+
# A version-independent symlink is used so .bashrc doesn't need updating
44+
# when the Java version changes — only the symlink target is updated.
3045
RUN if MISE_JAVA=$(mise where java 2>/dev/null); then \
3146
dir="$HOME/.local/share/sandcat"; mkdir -p "$dir"; \
3247
ln -sfn "$MISE_JAVA" "$dir/java-home"; \
3348
cp "$MISE_JAVA/lib/security/cacerts" "$dir/cacerts" 2>/dev/null || true; \
3449
{ echo ''; \
3550
echo '# sandcat-java-env'; \
36-
echo '_sc_java="$HOME/.local/share/sandcat/java-home"'; \
37-
echo '_sc_cacerts="$HOME/.local/share/sandcat/cacerts"'; \
38-
echo '[ -L "$_sc_java" ] && export JAVA_HOME="$_sc_java"'; \
39-
echo '[ -f "$_sc_cacerts" ] && export JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=$_sc_cacerts -Djavax.net.ssl.trustStorePassword=changeit"'; \
40-
echo 'unset _sc_java _sc_cacerts'; \
51+
echo '[ -L "$HOME/.local/share/sandcat/java-home" ] && export JAVA_HOME="$HOME/.local/share/sandcat/java-home"'; \
52+
echo '[ -f "$HOME/.local/share/sandcat/cacerts" ] && export JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=$HOME/.local/share/sandcat/cacerts -Djavax.net.ssl.trustStorePassword=changeit"'; \
4153
} >> "$HOME/.bashrc"; \
4254
fi
4355

44-
# Pre-create the Claude config directory and seed onboarding flag so Claude
45-
# Code can use an API key from the environment without interactive setup.
46-
RUN mkdir -p /home/vscode/.claude \
47-
&& echo '{"hasCompletedOnboarding":true}' > /home/vscode/.claude.json
56+
# Pre-create ~/.claude so Docker bind-mounts (CLAUDE.md, agents/, commands/)
57+
# don't cause it to be created as root-owned.
58+
RUN mkdir -p /home/vscode/.claude
4859

4960
RUN echo 'alias claude-yolo="claude --dangerously-skip-permissions"' >> /home/vscode/.bashrc
5061

.devcontainer/compose-all.yml

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
name: sttp
2-
1+
name: sttp-sandbox
32
include:
43
- path: sandcat/compose-proxy.yml
5-
64
services:
7-
app:
5+
agent:
86
build:
97
context: .
108
dockerfile: Dockerfile.app
@@ -13,29 +11,39 @@ services:
1311
# so processes inside cannot modify routing, iptables, or the tunnel.
1412
network_mode: "service:wg-client"
1513
volumes:
16-
# Mount the project's code
17-
- ..:/workspaces/sttp:cached
18-
# Overlay .devcontainer as read-only so the agent cannot modify its
19-
# own sandbox (scripts, compose files, Dockerfile, devcontainer.json).
20-
- ../.devcontainer:/workspaces/sttp/.devcontainer:ro
2114
# Named volume for the home directory so Claude Code auth state,
2215
# shell history, and other user-level config persist across rebuilds.
2316
- app-home:/home/vscode
2417
# Shared volume from mitmproxy containing the CA cert and
2518
# sandcat.env (env vars + secret placeholders). Read-only — app
2619
# containers should never write to this.
2720
- mitmproxy-config:/mitmproxy-config:ro
28-
# Bind-mount host Claude Code customizations (read-only) so personal
29-
# settings, agents, and slash commands carry into the container.
30-
# Remove any mount whose source does not exist on your host —
31-
# Docker will otherwise create an empty directory in its place.
32-
- ~/.claude/CLAUDE.md:/home/vscode/.claude/CLAUDE.md:ro
33-
- ~/.claude/agents:/home/vscode/.claude/agents:ro
34-
- ~/.claude/commands:/home/vscode/.claude/commands:ro
21+
# Mount the project's code
22+
- ..:/workspaces/sttp-sandbox
23+
# Read-only devcontainer directory
24+
- ../.devcontainer:/workspaces/sttp-sandbox/.devcontainer:ro
25+
# Read-only settings directory
26+
- ../.sandcat:/workspaces/sttp-sandbox/.sandcat:ro
27+
# Host Claude config (optional)
28+
- ${HOME}/.claude/CLAUDE.md:/home/vscode/.claude/CLAUDE.md:ro
29+
- ${HOME}/.claude/agents:/home/vscode/.claude/agents:ro
30+
- ${HOME}/.claude/commands:/home/vscode/.claude/commands:ro
31+
# Read-only Git directory
32+
# - ../.git:/workspace/.git:ro
33+
# Read-only IntelliJ IDEA project directory
34+
# - ../.idea:/workspace/.idea:ro
3535
command: sleep infinity
36+
environment:
37+
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
3638
depends_on:
3739
wg-client:
3840
condition: service_healthy
39-
41+
working_dir: /workspaces/sttp-sandbox
42+
mitmproxy:
43+
volumes:
44+
- ../.sandcat:/config/project:ro
45+
# Project-level settings (.sandcat/ directory). If the directory does
46+
# not exist on the host, Docker creates an empty one and the addon
47+
# simply finds no files — no error.
4048
volumes:
4149
app-home:

.devcontainer/devcontainer.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
2-
"name": "sttp",
2+
"name": "sttp-sandbox",
33
"dockerComposeFile": "compose-all.yml",
4-
"service": "app",
5-
"workspaceFolder": "/workspaces/sttp",
4+
"service": "agent",
5+
"workspaceFolder": "/workspaces/sttp-sandbox",
66
// Remove credential sockets that VS Code forwards into the container
77
// (SSH agent, git credential helper). Clearing env vars alone only
88
// hides the paths — the socket files in /tmp can still be discovered
99
// by scanning. The post-start script deletes them as best-effort
1010
// hardening.
11-
"postStartCommand": "bash /workspaces/sttp/.devcontainer/sandcat/scripts/app-post-start.sh",
11+
"postStartCommand": "bash /workspaces/sttp-sandbox/.devcontainer/sandcat/scripts/app-post-start.sh",
1212
// VS Code forwards host credential sockets into containers by default.
1313
// Clear them so container code cannot use host SSH keys, GPG signing,
1414
// or VS Code's git credential helpers to authenticate as the host user.
@@ -22,7 +22,8 @@
2222
"extensions": [
2323
"anthropic.claude-code",
2424
"github.vscode-pull-request-github",
25-
"scalameta.metals"
25+
"redhat.java",
26+
"scalameta.metals",
2627
],
2728
"settings": {
2829
// Prevent VS Code from copying host .gitconfig into the

.devcontainer/sandcat/compose-proxy.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,21 @@ services:
2020
interval: 2s
2121
timeout: 2s
2222
retries: 15
23-
2423
mitmproxy:
25-
image: mitmproxy/mitmproxy:latest
24+
image: ghcr.io/virtuslab/sandcat-mitmproxy-op:latest
2625
command: mitmweb --mode wireguard --web-host 0.0.0.0 --set web_password=mitmproxy -s /scripts/mitmproxy_addon.py
2726
ports:
2827
- "8081" # mitmweb UI; host port assigned dynamically to avoid conflicts
2928
volumes:
3029
- mitmproxy-config:/home/mitmproxy/.mitmproxy
3130
- ./scripts/mitmproxy_addon.py:/scripts/mitmproxy_addon.py:ro
3231
- ~/.config/sandcat/settings.json:/config/settings.json:ro
33-
cap_add:
34-
- NET_ADMIN
3532
healthcheck:
3633
test: ["CMD", "test", "-f", "/home/mitmproxy/.mitmproxy/wireguard.conf"]
3734
interval: 2s
3835
timeout: 2s
3936
retries: 15
40-
37+
environment:
38+
- OP_SERVICE_ACCOUNT_TOKEN
4139
volumes:
4240
mitmproxy-config:
Binary file not shown.

.devcontainer/sandcat/scripts/app-init.sh

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
#!/bin/bash
22
#
33
# Entrypoint for containers that share the wg-client's network namespace.
4-
# Installs the mitmproxy CA cert, loads env vars and secret placeholders
5-
# from sandcat.env, then hands off to the container's main command.
4+
# Installs the mitmproxy CA cert, disables commit signing, loads env vars
5+
# and secret placeholders from sandcat.env, runs vscode-user setup (git
6+
# identity, Java trust store, Claude Code update), then drops to vscode
7+
# and exec's the container's main command.
68
#
79
set -e
810

911
CA_CERT="/mitmproxy-config/mitmproxy-ca-cert.pem"
1012

11-
# The CA cert should already exist (wg-client depends_on mitmproxy healthy),
12-
# but wait briefly in case of a slight race on the shared volume.
13-
elapsed=0
14-
while [ ! -f "$CA_CERT" ]; do
15-
if [ "$elapsed" -ge 30 ]; then
16-
echo "Timed out waiting for mitmproxy CA cert" >&2
17-
exit 1
18-
fi
19-
sleep 1
20-
elapsed=$((elapsed + 1))
21-
done
13+
# The CA cert is guaranteed to exist: app depends_on wg-client (healthy),
14+
# which depends_on mitmproxy (healthy), whose healthcheck requires the
15+
# WireGuard config — generated after the CA.
16+
if [ ! -f "$CA_CERT" ]; then
17+
echo "mitmproxy CA cert not found at $CA_CERT" >&2
18+
exit 1
19+
fi
2220

2321
cp "$CA_CERT" /usr/local/share/ca-certificates/mitmproxy.crt
2422
update-ca-certificates
@@ -55,7 +53,7 @@ else
5553
echo "No $SANDCAT_ENV found — env vars and secret substitution disabled"
5654
fi
5755

58-
# Run vscode-user tasks: git identity and Claude Code update.
56+
# Run vscode-user tasks: git identity, Java trust store, Claude Code update.
5957
su - vscode -c /usr/local/bin/app-user-init.sh
6058

6159
# Source all sandcat profile.d scripts from /etc/bash.bashrc so env vars

.devcontainer/sandcat/scripts/app-post-start.sh

100644100755
File mode changed.

.devcontainer/sandcat/scripts/app-user-init.sh

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,18 @@ fi
1919
# repo-level config.
2020
git config --global commit.gpgsign false
2121

22+
# SSH keys are not available in the container (SSH_AUTH_SOCK is cleared
23+
# and credential sockets are removed), so rewrite git SSH URLs to HTTPS.
24+
# Sandcat's secret substitution handles GitHub token authentication over
25+
# HTTPS transparently.
26+
git config --global --replace-all url."https://github.com/".insteadOf "git@github.com:" "git@github.com:"
27+
git config --global --replace-all url."https://github.com/".insteadOf "ssh://git@github.com/" "ssh://git@github.com/"
28+
2229
# If Java is installed (via mise), import the mitmproxy CA into Java's trust
2330
# store. Java uses its own cacerts and ignores the system CA store.
2431
CA_CERT="/mitmproxy-config/mitmproxy-ca-cert.pem"
2532

26-
# Ensure mise is on PATH. `su - vscode` resets the environment and sources
33+
# Ensure mise is on PATH. `su - vscode` resets the environment and sources
2734
# only the first of ~/.bash_profile, ~/.bash_login, ~/.profile. If
2835
# ~/.bash_profile exists (e.g. created by VS Code on a persistent volume),
2936
# ~/.profile — where the Dockerfile adds mise — is never read.
@@ -33,8 +40,8 @@ fi
3340

3441
MISE_JAVA_HOME="$(mise where java 2>/dev/null || true)"
3542
if [ -n "$MISE_JAVA_HOME" ] && [ -f "$CA_CERT" ]; then
36-
# Create a version-independent symlink so JAVA_HOME (exported via
37-
# /etc/profile.d/) doesn't depend on the mise Java version.
43+
# Create a version-independent symlink so JAVA_HOME doesn't depend
44+
# on the mise Java version.
3845
SANDCAT_DIR="$HOME/.local/share/sandcat"
3946
mkdir -p "$SANDCAT_DIR"
4047
ln -sfn "$MISE_JAVA_HOME" "$SANDCAT_DIR/java-home"
@@ -69,25 +76,17 @@ if [ -n "$MISE_JAVA_HOME" ] && [ -f "$CA_CERT" ]; then
6976
}
7077
}
7178
EOFJSON
72-
fi
73-
74-
# Write Java env vars to ~/.bashrc (on the persistent app-home volume)
75-
# so VS Code's userEnvProbe picks them up even after container rebuild.
76-
# The Dockerfile bakes these into the image for first-run, but rebuilds
77-
# with a changed toolchain need the runtime fallback.
78-
BASHRC_JAVA_MARKER="# sandcat-java-env"
79-
if ! grep -q "$BASHRC_JAVA_MARKER" "$HOME/.bashrc" 2>/dev/null; then
80-
cat >> "$HOME/.bashrc" << 'BASHRC_JAVA'
8179

82-
# sandcat-java-env
83-
_sc_java="$HOME/.local/share/sandcat/java-home"
84-
_sc_cacerts="$HOME/.local/share/sandcat/cacerts"
85-
[ -L "$_sc_java" ] && export JAVA_HOME="$_sc_java"
86-
[ -f "$_sc_cacerts" ] && export JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=$_sc_cacerts -Djavax.net.ssl.trustStorePassword=changeit"
87-
unset _sc_java _sc_cacerts
88-
BASHRC_JAVA
8980
fi
9081
fi
9182

92-
# Best-effort: may fail if network isn't routed yet or CLI was just installed.
93-
claude update || true
83+
# Seed the onboarding flag so Claude Code uses the API key without interactive
84+
# setup. Only written when the user configured an ANTHROPIC_API_KEY secret.
85+
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
86+
echo '{"hasCompletedOnboarding":true}' > "$HOME/.claude.json"
87+
fi
88+
89+
# Claude Code is installed at build time (Dockerfile.app).
90+
# Background update so it doesn't block startup.
91+
(claude install >/dev/null 2>&1 &)
92+

0 commit comments

Comments
 (0)