skill
Container Conduit
Public UnreviewedWebSocket bridge connecting a main Gallia container to satellite Adom containers — remote command execution, persistent shell sessions, file transfer, system monitoring, and an interactive terminal da
← Commit history
Add adompkg installer files
5 files changed
+192−1
README.md+1−1@@ -162,4 +162,4 @@ Container Conduit registers with the Gallia auto-start system via `service.json` | Permission denied on `/opt/container-conduit` | Script not run with sudo | Use `sudo -E bash` to preserve env vars | | Dashboard shows 0 containers | API URL detection fails | Restart MCP server (it injects `__CONDUIT_API_BASE__`) | | PTY timeout on Connect | Agent running old code | Restart agent with updated `agent.js` |-| Shell tab WebSocket error | Port 8801 not reachable via proxy | Ensure `server.js` binds 8801 to `0.0.0.0` |+| Shell tab WebSocket error | Port 8801 not reachable via proxy | Ensure `server.js` binds 8801 to `0.0.0.0` |
SKILL.mdadded+161@@ -0,0 +1,161 @@+---+name: container-conduit+author: Noah+description: "Use when the user wants to manage remote containers via Container Conduit — connecting satellite containers, running commands remotely, transferring files, opening interactive PTY shells, viewing the Conduit dashboard, or installing the Conduit agent on new containers. Covers the WebSocket bridge between a main Gallia container and satellite Adom containers."+---++# Container Conduit++Container Conduit is a WebSocket bridge that connects a main Gallia container to one or more satellite Adom containers. It enables remote command execution, file transfer, system status queries, and interactive PTY terminal sessions — all from Claude Code or the Gallia Viewer dashboard.++## Architecture++```+Main Container (Gallia) Satellite Container(s)+ server.js (WS port 8800) ←──WS──→ agent.js+ HTTP API (port 8801) ↑+ ↑ install.sh (bootstrap)+ MCP tools (mcp/server.js)+ ↑+ Claude Code / Dashboard+```++- **server.js** — WebSocket server on port 8800 that accepts agent connections, plus an HTTP API on port 8801 for MCP tools and the dashboard+- **agent.js** — Lightweight satellite agent that connects back to the main container and handles exec, file, status, and PTY commands+- **mcp/server.js** — MCP stdio server exposing Container Conduit tools to Claude Code+- **dashboard.html** — Interactive management dashboard displayed in Gallia Viewer with Shell (xterm.js), Terminal, and Prompts tabs++## Key Files++| File | Location | Purpose |+|------|----------|---------|+| `server.js` | `services/container-conduit/server.js` | Main WS+HTTP server |+| `agent.js` | `services/container-conduit/agent/agent.js` | Satellite agent |+| `install.sh` | `services/container-conduit/agent/install.sh` | One-line agent bootstrap |+| `mcp/server.js` | `services/container-conduit/mcp/server.js` | MCP tool definitions |+| `dashboard.html` | `services/container-conduit/dashboard.html` | GV management dashboard |+| `service.json` | `services/container-conduit/service.json` | Auto-start manifest |++## MCP Tools++All tools are prefixed `container_` in the MCP server:++| Tool | Description |+|------|-------------|+| `container_list` | List all connected satellite containers |+| `container_exec` | Execute a shell command on a specific container |+| `container_exec_all` | Execute a command on ALL connected containers |+| `container_file_read` | Read a file from a remote container (base64) |+| `container_file_write` | Write a file to a remote container (base64) |+| `container_file_list` | List directory contents on a remote container |+| `container_status` | Get system status (memory, disk, uptime) of a container |+| `container_kick` | Disconnect a container (it will auto-reconnect) |+| `container_shell_start` | Start a persistent interactive shell session (returns sessionId) |+| `container_shell_exec` | Run a command in a persistent shell (state persists across calls) |+| `container_shell_stop` | Stop a persistent shell session |+| `container_dashboard` | Display the Conduit dashboard in Gallia Viewer |+| `container_create` | Create a new Adom container (repo + curium + container) |+| `container_install_cmd` | Generate the one-liner install command for a satellite |++## Installing the Agent on a Satellite Container++### One-liner install (direct network)++```bash+curl -sL http://<MAIN_IP>:8800/agent/install | CC_NAME=my-service bash+```++### Proxy install (Coder proxy for cross-container communication)++```bash+curl -sL https://coder.<SLUG>.containers.adom.inc/proxy/8800/agent/install | \+ CC_BASE_URL=https://coder.<SLUG>.containers.adom.inc/proxy/8800 \+ CC_NAME=my-service sudo -E bash+```++The install script:+1. Creates `/opt/container-conduit/` owned by `adom`+2. Downloads `agent.js` and `package.json` from the main container+3. Runs `npm install`+4. Writes `config.json` with WebSocket URL and token+5. Starts the agent from `~/project`+6. Adds a cron `@reboot` entry for auto-start++### Environment variables++| Variable | Default | Description |+|----------|---------|-------------|+| `CC_HOST` | (required) | Main container IP |+| `CC_PORT` | `8800` | Main container WS port |+| `CC_TOKEN` | `adom-container-token-2025` | Authentication token |+| `CC_NAME` | `$(hostname)` | Container name shown in dashboard |+| `CC_BASE_URL` | `http://$CC_HOST:$CC_PORT` | Override download URL (for proxy setups) |+| `CC_WS_URL` | auto-detected | Override WebSocket URL (e.g. `wss://...`) |++## Dashboard (Gallia Viewer)++Open the dashboard via MCP tool:++```+container_dashboard()+```++The dashboard has three tabs:++1. **Shell** — Interactive xterm.js terminal connected to a remote container via PTY WebSocket relay. Supports full TTY (nano, vim, htop, etc.)+2. **Terminal** — Simple command execution with output display (non-interactive)+3. **Claude Prompts** — Pre-built prompts for common container management tasks++The left panel shows all connected containers with name, hostname, IP, capabilities, and connection age.++## Persistent Shell Sessions (MCP)++The `container_shell_*` tools give Claude Code a persistent interactive shell on any satellite container. Unlike `container_exec` (which spawns a new process per call), shell sessions preserve state:++```+container_shell_start("service-test1") → sessionId+container_shell_exec(sessionId, "cd /tmp")+container_shell_exec(sessionId, "pwd") → /tmp (cwd persists!)+container_shell_exec(sessionId, "export FOO=bar")+container_shell_exec(sessionId, "echo $FOO") → bar (env vars persist!)+container_shell_stop(sessionId)+```++**How it works:** The server spawns a PTY on the agent, then wraps each command with SOH-byte sentinel markers to detect completion and extract output. Sessions auto-cleanup after 30 minutes of inactivity.++## PTY Terminal (Dashboard)++The Shell tab provides a full interactive terminal via:++```+Dashboard (xterm.js) → WS port 8801 /pty → server.js relay → agent.js → script -qfc 'bash -i' /dev/null+```++The agent uses `script` to allocate a real PTY without requiring `node-pty`. This supports curses-based applications (nano, vim, htop, etc.).++## Auto-Start++The `service.json` manifest registers Container Conduit with the gallia auto-start system:++```json+{+ "name": "container-conduit",+ "port": 8800,+ "health": "tcp",+ "start": "node server.js",+ "cwd": "."+}+```++Both `gallia-start.sh` and `gallia-watchdog.sh` auto-discover this manifest. The server starts on boot and restarts automatically if it crashes.++## Troubleshooting++| Symptom | Cause | Fix |+|---------|-------|-----|+| Agent can't connect | Internal hostname not routable between containers | Use `CC_BASE_URL` with the Coder proxy URL |+| `\r': command not found` during install | CRLF line endings in agent files | Run `sed -i 's/\r$//' /opt/container-conduit/agent.js` |+| Permission denied on `/opt/container-conduit` | Script not run with sudo | Use `sudo -E bash` to preserve env vars |+| Dashboard shows 0 containers | API URL detection fails in srcdoc iframe | MCP server injects `__CONDUIT_API_BASE__` — restart MCP if stale |+| PTY timeout on Connect | Agent running old code without PTY support | Restart agent with updated `agent.js` |+| Shell tab WebSocket error | Port 8801 not reachable via proxy | Ensure `server.js` binds 8801 to `0.0.0.0` |
install.shadded+15@@ -0,0 +1,15 @@+#!/bin/bash+set -e+PKG_DIR="$(cd "$(dirname "$0")" && pwd)"+SKILLS_DIR="$HOME/.claude/skills"+mkdir -p "$SKILLS_DIR/container-conduit"+if [ -f "$PKG_DIR/SKILL.md" ]; then+ cp "$PKG_DIR/SKILL.md" "$SKILLS_DIR/container-conduit/SKILL.md"+fi+for skill_dir in "$PKG_DIR/skills"/*/; do+ [ -d "$skill_dir" ] || continue+ skill_name=$(basename "$skill_dir")+ mkdir -p "$SKILLS_DIR/$skill_name"+ cp -r "$skill_dir"* "$SKILLS_DIR/$skill_name/"+done+echo "Installed container-conduit"
package.jsonadded+11@@ -0,0 +1,11 @@+{+ "slug": "container-conduit",+ "version": "1.0.0",+ "type": "skill",+ "description": "WebSocket bridge connecting a main Gallia container to satellite Adom containers — remote command execution, persistent shell sessions, file transfer, system monitoring, and an interactive terminal ",+ "dependencies": {},+ "scripts": {+ "install": "./install.sh",+ "uninstall": "./uninstall.sh"+ }+}
uninstall.shadded+4@@ -0,0 +1,4 @@+#!/bin/bash+set -e+rm -rf "$HOME/.claude/skills/container-conduit"+echo "Uninstalled container-conduit"