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
3 changes: 2 additions & 1 deletion .devcontainer/claude-sandbox/init-firewall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ for domain in \
"update.code.visualstudio.com" \
"auth.openai.com" \
"api.openai.com" \
"chatgpt.com"; do
"chatgpt.com" \
"mcp.mdn.mozilla.net"; do
echo "Resolving $domain..."
ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
if [ -z "$ips" ]; then
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/build-claude-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,19 @@ jobs:
matrix:
include:
- image-suffix: claude-code
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04
arch: amd64
- image-suffix: claude-code
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04-arm
arch: arm64
- image-suffix: claude-code-sandbox
verify-command: "claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04
arch: amd64
- image-suffix: claude-code-sandbox
verify-command: "claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/build-ralphex-fe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
paths:
- 'ralphex-fe/Dockerfile'
- 'ralphex-fe/*.sh'
- 'ralphex-fe/managed-settings.json'
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -107,5 +108,7 @@ jobs:
test -f /srv/init.sh &&
chromium --no-sandbox --version &&
test -x /usr/bin/chromium &&
test -r /etc/claude-code/managed-settings.json &&
jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ &&
echo 'All checks passed'
"
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ jobs:
add_image "claude-code" \
"claude-code/.devcontainer" \
"claude-code/.devcontainer/Dockerfile" \
"bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"default"
add_image "claude-code-sandbox" \
"claude-code/.devcontainer" \
"claude-code/.devcontainer/Dockerfile" \
"claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"sandbox"
fi

Expand All @@ -144,7 +144,7 @@ jobs:
add_image "ralphex-fe" \
"ralphex-fe" \
"ralphex-fe/Dockerfile" \
"bun --version && hugo version && go version && docker --version && /srv/ralphex --version && rtk --version"
"bun --version && hugo version && go version && docker --version && /srv/ralphex --version && rtk --version && test -r /etc/claude-code/managed-settings.json && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/"
fi

if [ "$INCLUDES" = "[]" ]; then
Expand Down
3 changes: 2 additions & 1 deletion claude-code/.devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ COPY --chmod=0755 patch-playwright-mcp.sh /usr/local/bin/patch-playwright-mcp

# ── Claude Code managed settings ─────────────────────────────────────────────
# Image-policy settings at the Linux managed location (highest precedence, outside
# any volume mount). Wires patch-playwright-mcp as a SessionStart hook. See #98, #101.
# any volume mount). Wires patch-playwright-mcp as a SessionStart hook (#98, #101)
# and provides the MDN MCP server. managedMcpServers needs Claude Code >= 2.1.259.
#
# /etc/claude-code is created explicitly: BuildKit applies COPY --chmod to parent
# dirs it auto-creates, leaving 0644 — not traversable.
Expand Down
9 changes: 9 additions & 0 deletions claude-code/.devcontainer/managed-settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,14 @@
]
}
]
},
"managedMcpServers": {
"mdn": {
"type": "http",
"url": "https://mcp.mdn.mozilla.net/",
"headers": {
"X-Moz-1st-Party-Data-Opt-Out": "1"
}
}
}
}
8 changes: 7 additions & 1 deletion claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ Projects consume these pre-built images and control their own tool versions via

**Sandbox-only:** iptables, ipset, iproute2, dnsutils, aggregate, firewall sudo rule

### MDN MCP server

The image provides Mozilla's [MDN MCP server](https://developer.mozilla.org/en-US/mcp) (web platform docs and browser compatibility data) to every session through `managedMcpServers` in `/etc/claude-code/managed-settings.json` — no per-project setup. It sends `X-Moz-1st-Party-Data-Opt-Out: 1`, Mozilla's documented opt-out from the query logging they do while the server is experimental.

Requires Claude Code >= 2.1.259; earlier clients ignore the key. `claude mcp remove` refuses it, but each developer can turn it off for themselves in `/mcp` under **Managed MCPs**. Sandbox users must allowlist `mcp.mdn.mozilla.net` in their firewall script.

## Multi-platform Support

Both variants are built for:
Expand Down Expand Up @@ -125,7 +131,7 @@ To remove a plugin in your project, delete its entry from the local `init-plugin

Default-deny iptables firewall. The image provides the packages and sudo rule; the project provides this script via bind mount. Customize the domain allowlist for your project.

See the [repo's own sandbox firewall script](../.devcontainer/claude-sandbox/init-firewall.sh) for a complete example. The script should: preserve Docker internal DNS rules, allow DNS/SSH/localhost, fetch GitHub IP ranges via `curl -s https://api.github.com/meta`, resolve additional allowed domains (npm, Anthropic API, VS Code marketplace, etc.) via `dig`, set default DROP policies, allow established connections and the ipset allowlist, then verify by confirming `example.com` is blocked and `api.github.com` is reachable.
See the [repo's own sandbox firewall script](../.devcontainer/claude-sandbox/init-firewall.sh) for a complete example. The script should: preserve Docker internal DNS rules, allow DNS/SSH/localhost, fetch GitHub IP ranges via `curl -s https://api.github.com/meta`, resolve additional allowed domains (npm, Anthropic API, VS Code marketplace, `mcp.mdn.mozilla.net` for the MDN MCP server, etc.) via `dig`, set default DROP policies, allow established connections and the ipset allowlist, then verify by confirming `example.com` is blocked and `api.github.com` is reachable.

Mark as executable and ensure git tracks the executable bit:

Expand Down
6 changes: 6 additions & 0 deletions ralphex-fe/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ ARG CLAUDE_CODE_VERSION=2.1.280
RUN --mount=type=cache,target=/root/.npm \
npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}

# ── Claude Code managed settings ─────────────────────────────────────────────
# Provides the MDN MCP server to every session, including `claude -p` runs.
# managedMcpServers needs Claude Code >= 2.1.259; earlier clients ignore it.
RUN mkdir -p /etc/claude-code
COPY --chown=root:root --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json

# ── Bun ──────────────────────────────────────────────────────────────────────
ENV BUN_INSTALL=/usr/local/bun
ENV PATH="$BUN_INSTALL/bin:$PATH"
Expand Down
20 changes: 13 additions & 7 deletions ralphex-fe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,26 @@ This is a standalone image, not a devcontainer.
| Tool | Version |
|------|---------|
| Node.js | 24 (from base image) |
| Bun | 1.3.9 |
| Hugo Extended | 0.156.0 |
| Bun | 1.4.2 |
| Hugo Extended | 0.166.0 |
| Go | for Hugo Modules |
| Python 3 | system |
| Playwright + Chromium | native Debian |
| Claude Code CLI | 2.1.216 (pinned) |
| RTK | 0.43.0 (pinned) |
| Ralphex | 1.6.0 (pinned) |
| Git, ripgrep, jq, curl, wget | system |
| Claude Code CLI | 2.1.280 (pinned) |
| RTK | 0.49.0 (pinned) |
| Ralphex | 1.7.0 (pinned) |
| Git, ripgrep, jq, curl | system |

All pinned versions live as `ARG`s in the Dockerfile and are kept current by Renovate — see
[Automatic Rebuilds](#automatic-rebuilds). This image has no `mise`, so its Bun and Hugo are
image-level versions rather than per-project ones; Renovate tracks them like everything else.

### MDN MCP server

The image provides Mozilla's [MDN MCP server](https://developer.mozilla.org/en-US/mcp) (web platform docs and browser compatibility data) through `managedMcpServers` in `/etc/claude-code/managed-settings.json`. Ralphex drives Claude non-interactively; a local managed settings file is read at session start, so `claude -p` runs get it too — the unattended runs where a hallucinated DOM or CSS API would otherwise land in a commit unreviewed.

It sends `X-Moz-1st-Party-Data-Opt-Out: 1`, Mozilla's documented opt-out from the query logging they do while the server is experimental. Requires Claude Code >= 2.1.259; earlier clients ignore the key.

## Usage

### Via ralphex docker-wrapper
Expand Down Expand Up @@ -69,7 +75,7 @@ This image is built for multiple architectures:
## Image Tags

- `latest` — most recent build
- `bun{VERSION}-hugo{VERSION}` — version-specific tag (e.g., `bun1.3.9-hugo0.156.0`)
- `bun{VERSION}-hugo{VERSION}` — version-specific tag (e.g., `bun1.4.2-hugo0.166.0`)

Note: this image deviates from the standalone convention of a single primary version tag because it bundles multiple independently-versioned tools.

Expand Down
11 changes: 11 additions & 0 deletions ralphex-fe/managed-settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"managedMcpServers": {
"mdn": {
"type": "http",
"url": "https://mcp.mdn.mozilla.net/",
"headers": {
"X-Moz-1st-Party-Data-Opt-Out": "1"
}
}
}
}
Loading