-
Notifications
You must be signed in to change notification settings - Fork 462
Add direct-style AwsSyncServerInterpreter for AWS Lambda #5149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c117ab2
3ebd136
1afc110
3162586
649592c
91362b4
3182a82
a7147c4
4a4d8fd
6a971d0
d174568
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| FROM mcr.microsoft.com/devcontainers/base:debian | ||
|
|
||
| # ca-certificates, curl, git are already in the devcontainers base image. | ||
| # fd-find: fast file finder (aliased to fd below) | ||
| # fzf: fuzzy finder for files and command history | ||
| # gh: GitHub CLI | ||
| # gosu: drops privileges in the entrypoint | ||
| # jq: JSON processor | ||
| # ripgrep: fast recursive grep (rg) | ||
| # tmux: terminal multiplexer | ||
| # vim: text editor | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends fd-find fzf gh gosu jq ripgrep tmux vim \ | ||
| && ln -s $(which fdfind) /usr/local/bin/fd \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| COPY --chmod=755 sandcat/scripts/app-init.sh /usr/local/bin/app-init.sh | ||
| COPY --chmod=755 sandcat/scripts/app-user-init.sh /usr/local/bin/app-user-init.sh | ||
| COPY --chown=vscode:vscode sandcat/tmux.conf /home/vscode/.tmux.conf | ||
|
|
||
| USER vscode | ||
|
|
||
| # Install Claude Code (native binary — no Node.js required). | ||
| RUN curl -fsSL https://claude.ai/install.sh | bash | ||
|
|
||
| # Install mise (SDK manager) for language toolchains. | ||
| RUN curl https://mise.run | sh | ||
| # Make mise available in login shells (su - vscode) and Docker CMD/RUN. | ||
| RUN echo 'export PATH="/home/vscode/.local/bin:/home/vscode/.local/share/mise/shims:$PATH"' >> /home/vscode/.profile | ||
| ENV PATH="/home/vscode/.local/bin:/home/vscode/.local/share/mise/shims:$PATH" | ||
|
|
||
| # Development stacks (managed by sandcat init --stacks): | ||
| RUN mise use -g java@lts | ||
| RUN mise use -g scala@latest && mise use -g sbt@latest | ||
| # END STACKS | ||
|
|
||
| # If Java was installed above, bake JAVA_HOME and JAVA_TOOL_OPTIONS into | ||
| # .bashrc so VS Code's env probe picks them up before the entrypoint runs. | ||
| # Without JAVA_HOME, JVM tooling like Metals fails to find the JDK. | ||
| # JAVA_TOOL_OPTIONS points to a trust store copy that the entrypoint will | ||
| # populate with the mitmproxy CA at runtime; until then it holds the default | ||
| # Java CAs (harmless — equivalent to not setting it at all). | ||
| # A version-independent symlink is used so .bashrc doesn't need updating | ||
| # when the Java version changes — only the symlink target is updated. | ||
| RUN if MISE_JAVA=$(mise where java 2>/dev/null); then \ | ||
| dir="$HOME/.local/share/sandcat"; mkdir -p "$dir"; \ | ||
| ln -sfn "$MISE_JAVA" "$dir/java-home"; \ | ||
| cp "$MISE_JAVA/lib/security/cacerts" "$dir/cacerts" 2>/dev/null || true; \ | ||
| { echo ''; \ | ||
| echo '# sandcat-java-env'; \ | ||
| echo '[ -L "$HOME/.local/share/sandcat/java-home" ] && export JAVA_HOME="$HOME/.local/share/sandcat/java-home"'; \ | ||
| 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"'; \ | ||
| } >> "$HOME/.bashrc"; \ | ||
| fi | ||
|
|
||
| # Pre-create ~/.claude so Docker bind-mounts (CLAUDE.md, agents/, commands/) | ||
| # don't cause it to be created as root-owned. | ||
| RUN mkdir -p /home/vscode/.claude | ||
|
|
||
| RUN echo 'alias claude-yolo="claude --dangerously-skip-permissions"' >> /home/vscode/.bashrc | ||
|
|
||
| USER root | ||
| ENTRYPOINT ["/usr/local/bin/app-init.sh"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| name: tapir-sandbox | ||
| include: | ||
| - path: sandcat/compose-proxy.yml | ||
| services: | ||
| agent: | ||
| build: | ||
| context: . | ||
| dockerfile: Dockerfile.app | ||
| # Share wg-client's network namespace so all traffic goes through its | ||
| # WireGuard tunnel. The app container has no NET_ADMIN capability, | ||
| # so processes inside cannot modify routing, iptables, or the tunnel. | ||
| network_mode: "service:wg-client" | ||
| volumes: | ||
| # Named volume for the home directory so Claude Code auth state, | ||
| # shell history, and other user-level config persist across rebuilds. | ||
| - app-home:/home/vscode | ||
| # Shared volume from mitmproxy containing the CA cert and | ||
| # sandcat.env (env vars + secret placeholders). Read-only — app | ||
| # containers should never write to this. | ||
| - mitmproxy-config:/mitmproxy-config:ro | ||
| # Mount the project's code | ||
| - ..:/workspaces/tapir-sandbox | ||
| # Read-only devcontainer directory | ||
| - ../.devcontainer:/workspaces/tapir-sandbox/.devcontainer:ro | ||
| # Read-only settings directory | ||
| - ../.sandcat:/workspaces/tapir-sandbox/.sandcat:ro | ||
| # Host Claude config (optional) | ||
| - ${HOME}/.claude/CLAUDE.md:/home/vscode/.claude/CLAUDE.md:ro | ||
| - ${HOME}/.claude/agents:/home/vscode/.claude/agents:ro | ||
| - ${HOME}/.claude/commands:/home/vscode/.claude/commands:ro | ||
| # Read-only Git directory | ||
| # - ../.git:/workspace/.git:ro | ||
| # Read-only IntelliJ IDEA project directory | ||
| # - ../.idea:/workspace/.idea:ro | ||
| command: sleep infinity | ||
| environment: | ||
| - CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 | ||
| depends_on: | ||
| wg-client: | ||
| condition: service_healthy | ||
| working_dir: /workspaces/tapir-sandbox | ||
| mitmproxy: | ||
| volumes: | ||
| - ../.sandcat:/config/project:ro | ||
| # Project-level settings (.sandcat/ directory). If the directory does | ||
| # not exist on the host, Docker creates an empty one and the addon | ||
| # simply finds no files — no error. | ||
| volumes: | ||
| app-home: |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,46 +1,53 @@ | ||||||
| { | ||||||
| "name": "tapir", | ||||||
| "build": { | ||||||
| "dockerfile": "Dockerfile" | ||||||
| }, | ||||||
| "features": { | ||||||
| "ghcr.io/devcontainers/features/node:1": { | ||||||
| "version": "24" | ||||||
| }, | ||||||
| "ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}, | ||||||
| "ghcr.io/devcontainers/features/github-cli:1": { | ||||||
| "installDirectlyFromGitHubRelease": true, | ||||||
| "version": "latest" | ||||||
| } | ||||||
| }, | ||||||
| "customizations": { | ||||||
| "vscode": { | ||||||
| "extensions": [ | ||||||
| "anthropic.claude-code", | ||||||
| "scalameta.metals", | ||||||
| "scala-lang.scala", | ||||||
| "github.vscode-pull-request-github" | ||||||
| ], | ||||||
| "settings": { | ||||||
| "terminal.integrated.defaultProfile.linux": "bash", | ||||||
| "claudeCode.allowDangerouslySkipPermissions": true, | ||||||
| "claudeCode.initialPermissionMode": "bypassPermissions", | ||||||
| "claudeCode.selectedModel": "opus" | ||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "postCreateCommand": "bash .devcontainer/post-create.sh", | ||||||
| "postStartCommand": "bash .devcontainer/post-start.sh", | ||||||
| "mounts": [ | ||||||
| "type=volume,source=tapir-vscode-home,target=/home/vscode", | ||||||
| "type=bind,source=${localEnv:HOME}/.claude/CLAUDE.md,target=/home/vscode/.claude/CLAUDE.md,readonly", | ||||||
| "type=bind,source=${localEnv:HOME}/.claude/commands,target=/home/vscode/.claude/commands,readonly", | ||||||
| "type=bind,source=${localEnv:HOME}/.claude/agents,target=/home/vscode/.claude/agents,readonly" | ||||||
| ], | ||||||
| "containerEnv": { | ||||||
| "SSH_AUTH_SOCK": "" | ||||||
| }, | ||||||
| "runArgs": [ | ||||||
| "--env-file=${localWorkspaceFolder}/../dev-container-oss.env" | ||||||
| ] | ||||||
| } | ||||||
| "name": "tapir-sandbox", | ||||||
| "dockerComposeFile": "compose-all.yml", | ||||||
| "service": "agent", | ||||||
| "workspaceFolder": "/workspaces/tapir-sandbox", | ||||||
| // Remove credential sockets that VS Code forwards into the container | ||||||
| // (SSH agent, git credential helper). Clearing env vars alone only | ||||||
| // hides the paths — the socket files in /tmp can still be discovered | ||||||
| // by scanning. The post-start script deletes them as best-effort | ||||||
| // hardening. | ||||||
| "postStartCommand": "bash /workspaces/tapir-sandbox/.devcontainer/sandcat/scripts/app-post-start.sh", | ||||||
| // VS Code forwards host credential sockets into containers by default. | ||||||
| // Clear them so container code cannot use host SSH keys, GPG signing, | ||||||
| // or VS Code's git credential helpers to authenticate as the host user. | ||||||
| "remoteEnv": { | ||||||
| "SSH_AUTH_SOCK": "", | ||||||
| "GPG_AGENT_INFO": "", | ||||||
| "GIT_ASKPASS": "" | ||||||
| }, | ||||||
| "customizations": { | ||||||
| "vscode": { | ||||||
| "extensions": [ | ||||||
| "anthropic.claude-code", | ||||||
| "github.vscode-pull-request-github", | ||||||
| "redhat.java", | ||||||
| "scalameta.metals", | ||||||
|
||||||
| "scalameta.metals", | |
| "scalameta.metals" |
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| FROM debian:trixie-slim | ||
|
|
||
| # Dependencies for the WireGuard tunnel and iptables kill switch: | ||
| # wireguard-tools - `wg` command to configure WireGuard interfaces | ||
| # iproute2 - `ip` command for interface, address, route, and rule management | ||
| # iptables - firewall rules used as a kill switch (blocks traffic if tunnel drops) | ||
| # jq - parse mitmproxy's wireguard.conf JSON to extract key pairs | ||
| # openresolv - `resolvconf` command to configure DNS through the tunnel | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends \ | ||
| wireguard-tools iproute2 iptables jq openresolv \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| COPY scripts/wg-client-init.sh /usr/local/bin/wg-client-init.sh | ||
| RUN chmod +x /usr/local/bin/wg-client-init.sh | ||
|
|
||
| ENTRYPOINT ["/usr/local/bin/wg-client-init.sh"] |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,40 @@ | ||||||||||||
| services: | ||||||||||||
| # Dedicated networking container that manages the WireGuard tunnel to | ||||||||||||
| # mitmproxy. Only this container has NET_ADMIN — no user code runs here. | ||||||||||||
| wg-client: | ||||||||||||
| build: | ||||||||||||
| context: . | ||||||||||||
| dockerfile: Dockerfile.wg-client | ||||||||||||
| volumes: | ||||||||||||
| - mitmproxy-config:/mitmproxy-config:ro | ||||||||||||
| cap_add: | ||||||||||||
| - NET_ADMIN # required for WireGuard interface and iptables setup | ||||||||||||
| sysctls: | ||||||||||||
| - net.ipv4.conf.all.src_valid_mark=1 # required by WireGuard fwmark routing; can't be set inside the container (/proc/sys is read-only) | ||||||||||||
| command: sleep infinity | ||||||||||||
| depends_on: | ||||||||||||
| mitmproxy: | ||||||||||||
| condition: service_healthy | ||||||||||||
| healthcheck: | ||||||||||||
| test: ["CMD", "test", "-f", "/tmp/wg-ready"] | ||||||||||||
| interval: 2s | ||||||||||||
| timeout: 2s | ||||||||||||
| retries: 15 | ||||||||||||
| mitmproxy: | ||||||||||||
| image: ghcr.io/virtuslab/sandcat-mitmproxy-op:latest | ||||||||||||
| command: mitmweb --mode wireguard --web-host 0.0.0.0 --set web_password=mitmproxy -s /scripts/mitmproxy_addon.py | ||||||||||||
| ports: | ||||||||||||
| - "8081" # mitmweb UI; host port assigned dynamically to avoid conflicts | ||||||||||||
|
||||||||||||
| - "8081" # mitmweb UI; host port assigned dynamically to avoid conflicts | |
| - target: 8081 | |
| published: 0 | |
| protocol: tcp | |
| mode: host # mitmweb UI; host port assigned dynamically to avoid conflicts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Entrypoint for containers that share the wg-client's network namespace. | ||
| # Installs the mitmproxy CA cert, disables commit signing, loads env vars | ||
| # and secret placeholders from sandcat.env, runs vscode-user setup (git | ||
| # identity, Java trust store, Claude Code update), then drops to vscode | ||
| # and exec's the container's main command. | ||
| # | ||
| set -e | ||
|
|
||
| CA_CERT="/mitmproxy-config/mitmproxy-ca-cert.pem" | ||
|
|
||
| # The CA cert is guaranteed to exist: app depends_on wg-client (healthy), | ||
| # which depends_on mitmproxy (healthy), whose healthcheck requires the | ||
| # WireGuard config — generated after the CA. | ||
| if [ ! -f "$CA_CERT" ]; then | ||
| echo "mitmproxy CA cert not found at $CA_CERT" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| cp "$CA_CERT" /usr/local/share/ca-certificates/mitmproxy.crt | ||
| update-ca-certificates | ||
|
|
||
| # Node.js ignores the system trust store and bundles its own CA certs. | ||
| # Point it at the mitmproxy CA so TLS verification works for Node-based | ||
| # tools (e.g. Anthropic SDK). | ||
| export NODE_EXTRA_CA_CERTS="$CA_CERT" | ||
| echo "export NODE_EXTRA_CA_CERTS=\"$CA_CERT\"" > /etc/profile.d/sandcat-node-ca.sh | ||
|
|
||
| # GPG keys are not forwarded into the container (credential isolation), | ||
| # so commit signing would always fail. Git env vars have the highest | ||
| # precedence, overriding system/global/local/worktree config files. | ||
| export GIT_CONFIG_COUNT=1 | ||
| export GIT_CONFIG_KEY_0="commit.gpgsign" | ||
| export GIT_CONFIG_VALUE_0="false" | ||
| cat > /etc/profile.d/sandcat-git.sh << 'GITEOF' | ||
| export GIT_CONFIG_COUNT=1 | ||
| export GIT_CONFIG_KEY_0="commit.gpgsign" | ||
| export GIT_CONFIG_VALUE_0="false" | ||
| GITEOF | ||
|
|
||
| # Source env vars and secret placeholders (if available) | ||
| SANDCAT_ENV="/mitmproxy-config/sandcat.env" | ||
| if [ -f "$SANDCAT_ENV" ]; then | ||
| . "$SANDCAT_ENV" | ||
| # Make vars available to new shells (e.g. VS Code terminals in dev | ||
| # containers) that won't inherit the entrypoint's environment. | ||
| cp "$SANDCAT_ENV" /etc/profile.d/sandcat-env.sh | ||
| count=$(grep -c '^export ' "$SANDCAT_ENV" 2>/dev/null || echo 0) | ||
| echo "Loaded $count env var(s) from $SANDCAT_ENV" | ||
| grep '^export ' "$SANDCAT_ENV" | sed 's/=.*//' | sed 's/^export / /' | ||
| else | ||
| echo "No $SANDCAT_ENV found — env vars and secret substitution disabled" | ||
| fi | ||
|
|
||
| # Run vscode-user tasks: git identity, Java trust store, Claude Code update. | ||
| su - vscode -c /usr/local/bin/app-user-init.sh | ||
|
|
||
| # Source all sandcat profile.d scripts from /etc/bash.bashrc so env vars | ||
| # are available in non-login shells (e.g. VS Code integrated terminals). | ||
| # Guard with a marker to avoid duplicating on container restart. | ||
| BASHRC_MARKER="# sandcat-profile-source" | ||
| if ! grep -q "$BASHRC_MARKER" /etc/bash.bashrc 2>/dev/null; then | ||
| cat >> /etc/bash.bashrc << 'BASHRC_EOF' | ||
|
|
||
| # sandcat-profile-source | ||
| for _f in /etc/profile.d/sandcat-*.sh; do | ||
| [ -r "$_f" ] && . "$_f" | ||
| done | ||
| unset _f | ||
| BASHRC_EOF | ||
| fi | ||
|
|
||
| exec gosu vscode "$@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR title/description focus on AWS Lambda interpreters, but this change switches the devcontainer to a new multi-container “sandcat” setup and adds a large number of new scripts/configs under
.devcontainer/. If these dev-environment changes are intentional, they should be called out explicitly in the PR description; otherwise, consider splitting them into a separate PR to keep the Lambda interpreter change reviewable.