Ask for the model provider on every start
run.sh shows a window (zenity form, terminal prompts without zenity) for a base URL, token, auth header and optional model list. Left empty, Claude Desktop starts normally with the claude.ai sign-in. Otherwise it runs in its third-party gateway mode: run.sh writes the managed config (inferenceProvider "gateway" and friends) and copies it into the created container as root, because Claude Desktop ignores /etc/claude-desktop/managed-settings.json unless root owns it. The host copy is deleted right away; nothing is kept between starts. Claude Desktop only allows plain http to loopback, so for an http:// gateway launch-apps.sh runs socat on 127.0.0.1:18080 and forwards to it. localhost is rewritten to host.docker.internal for servers on the host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,11 @@ Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with C
|
||||
## Architecture
|
||||
|
||||
- **Claude Desktop and Ghidra share one container** (the root `Dockerfile`, started by `run.sh`), at the user's request. Ghidra is the official GitHub release (version, build date and SHA-256 are global build args, downloaded once in the `ghidra-dist` stage and copied into the final image and into build stages that compile extensions against it), is pinned to **12.1.4** because that's what the GhidraMCP release is built for (Ghidra only loads extensions built for its exact version), runs on Ubuntu's `openjdk-21-jdk`, lives in `/opt/ghidra` and starts together with Claude Desktop. The **Developer** plugin package is enabled in both the project window and the CodeBrowser. A bare `<PACKAGE NAME="Developer" />` only adds the package's RELEASED plugins, so the STABLE ones are listed with `<INCLUDE CLASS=...>`. CodeBrowser: the build inserts `ghidra/codebrowser-developer.xml` into `defaultTools/CodeBrowser.tool` inside `Public_Release.jar`. Project window: it has no default to patch, so `ghidra/FrontEndTool.xml` is copied into the user settings, along with `ghidra/preferences` (`USER_AGREEMENT=ACCEPT`, because a pre-created settings dir otherwise makes Ghidra ask for its user agreement). To check a config really loaded, close Ghidra cleanly and see whether the plugins survive in the file it rewrites; Ghidra drops what it didn't load.
|
||||
- **Model provider window.** `run.sh` asks on every start (zenity form; terminal prompts without zenity) for a base URL, token, auth header and optional model list. Empty URL: normal claude.ai sign-in. Otherwise it writes Claude Desktop's third-party managed config (`inferenceProvider: "gateway"`, `inferenceGatewayBaseUrl`, `inferenceGatewayApiKey`, `inferenceGatewayAuthScheme`, `inferenceModels`, `disableDeploymentModeChooser`; docs: https://claude.com/docs/third-party/claude-desktop) and Chat, Code and Cowork use that Anthropic-Messages-compatible endpoint, with no claude.ai account. Verified against Claude Desktop 4.4212.0:
|
||||
- On Linux the file is `/etc/claude-desktop/managed-settings.json`, and Claude Desktop **ignores it unless root owns it and it isn't group/world-writable**. A bind mount from the host shows the user's UID, so `run.sh` creates the container, copies the file in with `docker cp` from a tar stream (owner 0, mode 0644), then starts it; the host copy (runtime dir, 0700) is deleted right away and on failure. The image provides `/etc/claude-desktop`.
|
||||
- Claude Desktop rejects `http://` base URLs except loopback. For `http://host[:port]` the container gets `CLAUDE_RE_GATEWAY_FORWARD=host:port`, `launch-apps.sh` runs `socat` on `127.0.0.1:18080` to it, and the config points at `http://127.0.0.1:18080` (the token then travels unencrypted, as with any plain-HTTP server). `https://` URLs are used as given.
|
||||
- `localhost`/`127.0.0.1` in the URL is rewritten to `host.docker.internal` (`--add-host host.docker.internal:host-gateway`); a trailing `/` or `/v1` is stripped because Claude Desktop appends `/v1/messages`. An empty token becomes `none` (the key is required in gateway mode).
|
||||
- Check what Claude Desktop made of it in `~/.config/Claude/logs/main.log` (`[managedConfig]`, `[custom-3p]`).
|
||||
- **Shared workspace volume.** Every container mounts the workspace at `/workspace`, so a file path means the same thing to Claude, Ghidra and the RE MCP. `run.sh` asks for the host folder on each start in the desktop's folder picker (`zenity`, or `kdialog` on KDE; terminal prompt if neither exists), opening at the last folder used, else `./workspace`. Skip it with `--workspace DIR` or `WORKSPACE=DIR`. It refuses `/` and `$HOME`. The image symlinks `~/workspace` to `/workspace` for Claude Desktop's file picker.
|
||||
- **GUI forwarding.** Claude Desktop and Ghidra are GUI apps. Default to X11 forwarding (`/tmp/.X11-unix` + `DISPLAY`). Support Wayland through XWayland. Keep a noVNC/VNC fallback in mind for headless hosts. Never solve display access with a blanket `xhost +`. Scope it (`xhost +local:docker` or `+SI:localuser:...`).
|
||||
- **GhidraMCP wiring.** LaurieWired's GhidraMCP (https://github.com/LaurieWired/GhidraMCP), release 2.0, installed from the official release zip (SHA-256 checked). It has two parts:
|
||||
@@ -56,7 +61,7 @@ Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with C
|
||||
- **Pin versions.** Ghidra version, GhidraMCP release, JDK version, Claude Desktop build and RE MCP commit go in `.env` / build args, not `latest`. The GhidraMCP release must match the Ghidra version it was built for.
|
||||
- **Run as non-root** inside containers. Map the host UID/GID (via `.env`) so files in `workspace/` stay owned by the user.
|
||||
- **Persist state in named volumes or `workspace/`**: Ghidra user settings and projects. Rebuilding an image must not lose analysis.
|
||||
- **Claude Desktop sign-in is not persisted.** No keyring is installed and `~/.config/Claude` is not mounted, so the user logs in on every start. Don't add a keyring or config volume back without being asked.
|
||||
- **Claude Desktop sign-in is not persisted.** No keyring is installed and `~/.config/Claude` is not mounted, so the user logs in on every start. Don't add a keyring or config volume back without being asked. The same goes for the model-provider token (below): it's asked for on every start and never stored on the host.
|
||||
- **No secrets in the repo.** Credentials, session data and `workspace/` contents are git-ignored. Ship `.env.example`, never `.env`.
|
||||
- **Untrusted binaries.** Samples under analysis may be malicious. Never execute them on the host or in the Claude Desktop container. Containers that might run samples (dynamic analysis) should have no host mounts beyond `workspace/` and should allow network isolation (`network_mode: none` or an internal-only network).
|
||||
- **Linux only.** Target recent Docker Engine + Compose v2 (`docker compose`, not `docker-compose`). Make the setup work under both Docker and rootless Podman where practical. On SELinux hosts such as Fedora, bind mounts need `:z`/`:Z`.
|
||||
|
||||
Reference in New Issue
Block a user