From 1777b4b25fcffe47178409495d79fd63ebe9f57d Mon Sep 17 00:00:00 2001 From: mgabor3141 <9047995+mgabor3141@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:10:54 +0200 Subject: [PATCH] feat: unix socket IPC, auth-only TCP, devcontainer UX polish Update the gmux devcontainer feature for the new architecture: - Replace config file generation with GMUXD_LISTEN=0.0.0.0 env var - Health check via Unix socket instead of localhost TCP - Run gmuxd as the remote user (vscode), not root - Port 8790 (single authenticated TCP listener, was 8791) - Add postAttachCommand with OSC 8 clickable auth link - Add bold cyan 'Open gmux' link with version and changelog Depends on gmuxapp/gmux#40 (publish after that release lands). --- src/gmux/README.md | 10 ++--- src/gmux/devcontainer-feature.json | 7 ++-- src/gmux/install.sh | 64 ++++++++++++++++++++---------- test/gmux/test.sh | 15 +++++-- 4 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/gmux/README.md b/src/gmux/README.md index 2a1e699..1b26b4a 100644 --- a/src/gmux/README.md +++ b/src/gmux/README.md @@ -1,6 +1,6 @@ # gmux (ghcr.io/gmuxapp/features/gmux) -Installs [gmux](https://gmux.app) and gmuxd into a dev container. gmuxd starts automatically with a [network listener](https://gmux.app/develop/network-listener) so it's accessible from the host via port forwarding. +Installs [gmux](https://gmux.app) and gmuxd into a dev container. gmuxd starts automatically with `GMUXD_LISTEN=0.0.0.0` so it's accessible from the host via port forwarding. ## Usage @@ -13,15 +13,15 @@ Installs [gmux](https://gmux.app) and gmuxd into a dev container. gmuxd starts a } ``` -Port 8791 is automatically forwarded to the host. Open the forwarded URL and authenticate with the bearer token. +Port 8790 is automatically forwarded to the host. After the container starts, a clickable auth link appears in the terminal. ### Finding the auth token ```bash -docker exec gmuxd auth-link +gmuxd auth ``` -Prints a ready-to-use URL with the token. Open it in a browser to authenticate. +Prints the listen address and a ready-to-use URL with the token. Open it in a browser to authenticate. ## Options @@ -31,7 +31,7 @@ Prints a ready-to-use URL with the token. Open it in a browser to authenticate. ## Security -The network listener uses bearer token authentication. The token is auto-generated on first start and stored inside the container at `~/.local/state/gmux/auth-token`. +All TCP connections use bearer token authentication. The token is auto-generated on first start and stored inside the container at `~/.local/state/gmux/auth-token`. Devcontainer-aware tooling (VS Code, Codespaces) forwards the port to `localhost` on the host, so only local processes can reach it. The bearer token provides a second layer of protection on the Docker bridge network. diff --git a/src/gmux/devcontainer-feature.json b/src/gmux/devcontainer-feature.json index b5f3248..2a4a3a5 100644 --- a/src/gmux/devcontainer-feature.json +++ b/src/gmux/devcontainer-feature.json @@ -11,12 +11,13 @@ "description": "gmux version to install (e.g. '0.8.0' or 'latest')" } }, - "forwardPorts": [8791], + "forwardPorts": [8790], "portsAttributes": { - "8791": { + "8790": { "label": "gmux", "onAutoForward": "silent" } }, - "entrypoint": "/usr/local/bin/gmuxd-start.sh" + "entrypoint": "/usr/local/bin/gmuxd-start.sh", + "postAttachCommand": "/usr/local/bin/gmuxd-auth-notice.sh" } diff --git a/src/gmux/install.sh b/src/gmux/install.sh index bd90756..310e0f3 100755 --- a/src/gmux/install.sh +++ b/src/gmux/install.sh @@ -36,31 +36,55 @@ fi echo "gmux ${VERSION} installed successfully" -# Generate config for the network listener. -# Tailscale is intentionally disabled; container gmuxd is accessed -# via port forwarding (browser) or Docker network (peer discovery). -mkdir -p /usr/local/share/gmux -cat > /usr/local/share/gmux/config.toml << EOF -[network] -listen = "0.0.0.0:8791" -EOF +# Entrypoint: starts gmuxd with GMUXD_LISTEN=0.0.0.0 so the container +# is reachable via port forwarding. No config file needed. +# _REMOTE_USER and _REMOTE_USER_HOME are available at build time (install.sh) +# but NOT at container start. Bake them into the entrypoint script now. +REMOTE_USER="${_REMOTE_USER:-root}" +REMOTE_HOME="${_REMOTE_USER_HOME:-/root}" -# Entrypoint: copies config if user hasn't provided their own, starts gmuxd. -cat > /usr/local/bin/gmuxd-start.sh << 'SCRIPT' +cat > /usr/local/bin/gmuxd-start.sh << SCRIPT #!/bin/bash -GMUX_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/gmux" -if [ ! -f "$GMUX_CONFIG_DIR/config.toml" ]; then - mkdir -p "$GMUX_CONFIG_DIR" - cp /usr/local/share/gmux/config.toml "$GMUX_CONFIG_DIR/config.toml" -fi +TARGET_USER="${REMOTE_USER}" +TARGET_HOME="${REMOTE_HOME}" +export GMUXD_LISTEN="0.0.0.0" + +# Start gmuxd as the remote user so state (auth token, db) lives in their home. +start_gmuxd() { + if [ "\$(id -u)" = "0" ] && [ "\$TARGET_USER" != "root" ] && id "\$TARGET_USER" &>/dev/null; then + su - "\$TARGET_USER" -c "GMUXD_LISTEN=0.0.0.0 gmuxd start" >/tmp/gmuxd.log 2>&1 & + else + gmuxd start >/tmp/gmuxd.log 2>&1 & + fi +} -if ! curl -fsS http://localhost:8790/ >/dev/null 2>&1; then - gmuxd start >/tmp/gmuxd.log 2>&1 & - for i in $(seq 1 30); do - curl -fsS http://localhost:8790/ >/dev/null 2>&1 && break +SOCK="\${TARGET_HOME}/.local/state/gmux/gmuxd.sock" +if ! curl -fsS --unix-socket "\$SOCK" http://localhost/v1/health >/dev/null 2>&1; then + start_gmuxd + for i in \$(seq 1 30); do + curl -fsS --unix-socket "\$SOCK" http://localhost/v1/health >/dev/null 2>&1 && break sleep 0.5 done fi -exec "$@" +exec "\$@" SCRIPT chmod +x /usr/local/bin/gmuxd-start.sh + +# Post-attach notice: prints a localhost auth URL that's clickable in the +# VS Code terminal. Runs as the remote user after VS Code attaches. +cat > /usr/local/bin/gmuxd-auth-notice.sh << 'NOTICE' +#!/bin/bash +TOKEN_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/gmux/auth-token" +if [ -f "$TOKEN_FILE" ]; then + TOKEN=$(cat "$TOKEN_FILE") + URL="http://localhost:8790/auth/login?token=${TOKEN}" + VERSION=$(gmuxd version 2>/dev/null || echo "unknown") + CHANGELOG="https://gmux.app/changelog" + echo "" + printf ' \e[1;36m\e]8;;%s\007Open gmux\e]8;;\007\e[0m\n' "$URL" + echo "" + printf ' Version %s \xc2\xb7 \e]8;;%s\007Changelog\e]8;;\007\n' "$VERSION" "$CHANGELOG" + echo "" +fi +NOTICE +chmod +x /usr/local/bin/gmuxd-auth-notice.sh diff --git a/test/gmux/test.sh b/test/gmux/test.sh index 3972524..4000cfa 100755 --- a/test/gmux/test.sh +++ b/test/gmux/test.sh @@ -10,11 +10,20 @@ command -v gmuxd || { echo "FAIL: gmuxd not found"; exit 1; } # Entrypoint script exists test -x /usr/local/bin/gmuxd-start.sh || { echo "FAIL: gmuxd-start.sh not found"; exit 1; } -# Default config was generated with network listener -grep -q 'listen = "0.0.0.0:8791"' /usr/local/share/gmux/config.toml \ - || { echo "FAIL: default config missing or wrong"; exit 1; } +# Entrypoint sets GMUXD_LISTEN for container use +grep -q 'GMUXD_LISTEN' /usr/local/bin/gmuxd-start.sh \ + || { echo "FAIL: entrypoint missing GMUXD_LISTEN"; exit 1; } # gmuxd can report its version gmuxd version || { echo "FAIL: gmuxd version failed"; exit 1; } +# Entrypoint bakes in the remote user, not root (unless root IS the remote user) +if grep -q 'TARGET_USER="root"' /usr/local/bin/gmuxd-start.sh; then + # Only acceptable if _REMOTE_USER was actually root + echo "WARN: TARGET_USER is root; verify this is intentional" +else + grep -q 'TARGET_USER=' /usr/local/bin/gmuxd-start.sh \ + || { echo "FAIL: entrypoint missing TARGET_USER"; exit 1; } +fi + echo "All tests passed!"