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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ See [src/gmux/README.md](src/gmux/README.md) for options and configuration.

## Publishing

Features are published automatically to `ghcr.io/gmuxapp/features/` on push to `main` via the [release workflow](.github/workflows/release.yaml).
Features are published automatically to `ghcr.io/gmuxapp/features/` via the [release workflow](.github/workflows/release.yaml). Trigger it manually from the Actions tab after merging changes to `main`.
36 changes: 29 additions & 7 deletions src/gmux/README.md
Original file line number Diff line number Diff line change
@@ -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 and listens on all interfaces so it's accessible via port forwarding.

## Usage

Expand All @@ -13,30 +13,52 @@ 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. Open the forwarded URL and authenticate with the bearer token.

### Finding the auth token

```bash
docker exec <container> gmuxd auth-link
docker exec <container> gmuxd auth
```

Prints a ready-to-use URL with the token. Open it in a browser to authenticate.
Prints the listen address, auth token, and a ready-to-use login URL.

## Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `version` | string | `latest` | gmux version to install (e.g. `0.8.0` or `latest`) |

## Pre-provisioned auth token

If you need a known token (for scripting, health checks, or future host-side peer discovery), set `GMUXD_TOKEN` in your `devcontainer.json`:

```json
{
"image": "mcr.microsoft.com/devcontainers/base:debian",
"features": {
"ghcr.io/gmuxapp/features/gmux:1": {}
},
"containerEnv": {
"GMUXD_TOKEN": "output-of-openssl-rand-hex-32"
}
}
```

The token must be at least 64 hex characters. On first start, gmuxd writes it to disk. On subsequent starts, the file is verified against the env var. See [Environment variables](https://gmux.app/reference/environment/#auth-token) for details.

## 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`.
The network listener requires bearer token authentication on every request. 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.

See [Network Listener](https://gmux.app/develop/network-listener) for the full security model.
See [Security](https://gmux.app/security/) for the full security model.

## How it works

The feature sets `GMUXD_LISTEN=0.0.0.0` via `containerEnv` so gmuxd accepts connections from outside the container (required for port forwarding and Docker bridge access). The entrypoint starts gmuxd in the background before running the container's main process.

## Peer discovery

A host gmuxd can automatically discover gmuxd instances running inside dev containers. No additional configuration is needed; the feature's presence in the container's `devcontainer.metadata` label is the discovery signal. See [Peer Discovery & Aggregation](https://gmux.app/planned/peer-discovery-aggregation) for details.
A host gmuxd will be able to automatically discover gmuxd instances running inside dev containers. The feature's presence in the container's `devcontainer.metadata` label is the discovery signal. See [Peer Discovery & Aggregation](https://gmux.app/planned/peer-discovery-aggregation/) for the planned design.
9 changes: 6 additions & 3 deletions src/gmux/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "gmux",
"version": "1.0.0",
"version": "1.1.0",
"name": "gmux",
"documentationURL": "https://gmux.app/running-in-docker",
"description": "Installs gmux and gmuxd for managing terminal sessions from a browser dashboard.",
Expand All @@ -11,9 +11,12 @@
"description": "gmux version to install (e.g. '0.8.0' or 'latest')"
}
},
"forwardPorts": [8791],
"containerEnv": {
"GMUXD_LISTEN": "0.0.0.0"
},
"forwardPorts": [8790],
"portsAttributes": {
"8791": {
"8790": {
"label": "gmux",
"onAutoForward": "silent"
}
Expand Down
19 changes: 3 additions & 16 deletions src/gmux/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,11 @@ 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: copies config if user hasn't provided their own, starts gmuxd.
# Entrypoint: starts gmuxd in the background if not already running.
# GMUXD_LISTEN is set via containerEnv in devcontainer-feature.json.
# GMUXD_TOKEN can be set via containerEnv in the user's devcontainer.json.
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

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
Expand Down
9 changes: 4 additions & 5 deletions test/gmux/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ echo "Testing gmux installation..."
command -v gmux || { echo "FAIL: gmux not found"; exit 1; }
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; }
# Entrypoint script exists and is executable
test -x /usr/local/bin/gmuxd-start.sh || { echo "FAIL: gmuxd-start.sh not found or not executable"; 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; }
# GMUXD_LISTEN is set via containerEnv
test "$GMUXD_LISTEN" = "0.0.0.0" || { echo "FAIL: GMUXD_LISTEN not set (got: '$GMUXD_LISTEN')"; exit 1; }

# gmuxd can report its version
gmuxd version || { echo "FAIL: gmuxd version failed"; exit 1; }
Expand Down
Loading