Skip to content

Commit 5d38e65

Browse files
committed
feat: add tmux bridge for container-to-host tmux control
- Build tmux 3.6a from source with SHA256 verification - Add deva-bridge-tmux-host (host TCP listener) and deva-bridge-tmux (container Unix socket proxy) scripts for macOS<->Linux socket bridging - Fix docker-entrypoint.sh usermod error handling for mounted volumes - Document bridge architecture and security model in AGENTS.md - Fix version-upgrade build resilience (gh api vs curl for rate limits) - Add LSP support planning devlog
1 parent 5807889 commit 5d38e65

10 files changed

Lines changed: 1209 additions & 9 deletions

AGENTS.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,37 @@ Mitigations:
148148
- Set `DEVA_NO_DOCKER=1` environment variable
149149
- Only mount when Docker-in-Docker workflows are required
150150

151+
## Bridges (privileged)
152+
153+
Deva's container IS the sandbox. Bridges punch controlled holes back to the host for specific integrations. Each bridge has TWO components: host-side and container-side.
154+
155+
| Bridge | Host Command | Container Command | Risk |
156+
|--------|--------------|-------------------|------|
157+
| Docker | (auto-mount `/var/run/docker.sock`) | `docker ...` | Root-equivalent on host |
158+
| tmux | `deva-bridge-tmux-host` | `deva-bridge-tmux` | Host command execution |
159+
160+
**tmux bridge**: Connect container tmux client to host tmux server.
161+
- Problem: Unix socket mount fails across macOS<->Linux kernel boundary
162+
- Solution: socat TCP proxy (host) + socat Unix socket (container)
163+
- Security: Container gains full tmux control (send-keys, run-shell, scrollback)
164+
165+
Usage:
166+
```bash
167+
# Host (macOS)
168+
./scripts/deva-bridge-tmux-host
169+
170+
# Container
171+
deva-bridge-tmux
172+
tmux -S /tmp/host-tmux.sock list-sessions
173+
```
174+
175+
Environment variables:
176+
- `DEVA_BRIDGE_BIND`: host bind address (default: 127.0.0.1)
177+
- `DEVA_BRIDGE_PORT`: TCP port (default: 41555)
178+
- `DEVA_BRIDGE_HOST`: container's host address (default: host.docker.internal)
179+
- `DEVA_BRIDGE_SOCKET`: host tmux socket path (default: auto-detected)
180+
- `DEVA_BRIDGE_SOCK`: container local socket (default: /tmp/host-tmux.sock)
181+
151182
## Docker Architecture Details
152183

153184
### Volume Mounting Strategy

DEV-LOGS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@
1414
- Reference issue numbers in the format `#<issue-number>` for easy linking.
1515

1616

17+
# [2026-01-07] Dev Log: Fix version-upgrade build resilience
18+
- Why: `make versions-up` exited 56 during GitHub API changelog fetch - GitHub API 403 rate limit (60/hour) from unauthenticated curl
19+
- What:
20+
- Changed `fetch_github_releases()` and `fetch_recent_github_releases()` in `scripts/release-utils.sh` from `curl` to `gh api` for authenticated requests
21+
- All changelog fetch functions now fail gracefully with `{ echo "(fetch failed)"; return 0; }` instead of `|| return` (was causing `set -e` script abort)
22+
- Added fallback in `load_versions()` - network fetch failure uses current image version instead of empty string
23+
- Added pre-build version check in `scripts/version-upgrade.sh` - warns about missing versions but proceeds with build
24+
- Result: Build script resilient to transient network failures and GitHub rate limits. Changelog display is best-effort, won't block builds.
25+
26+
**Files changed**:
27+
- `scripts/release-utils.sh` (lines 175, 221, 452, 480)
28+
- `scripts/version-upgrade.sh` (lines 82-95)
29+
1730
# [2025-11-27] Dev Log: Docker-in-Docker auto-mount support
1831
- Why: Common dev workflow need - testing containers, building images, CI/CD simulation inside deva environments
1932
- What: Auto-mount Docker socket (`/var/run/docker.sock`) by default with graceful detection, opt-out via `--no-docker` flag or `DEVA_NO_DOCKER=1`, quick permission fix (chmod 666) for deva user access

Dockerfile

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
3030
openssh-client rsync \
3131
shellcheck bat fd-find silversearcher-ag \
3232
vim \
33-
procps psmisc zsh
33+
procps psmisc zsh socat \
34+
libevent-dev libncurses-dev bison
3435

3536
RUN git lfs install --system
3637

@@ -123,6 +124,24 @@ RUN --mount=type=cache,target=/tmp/delta-cache,sharing=locked \
123124
mv delta-0.18.2-${DELTA_ARCH}-unknown-linux-gnu/delta /usr/local/bin/ && \
124125
rm -rf delta-0.18.2-${DELTA_ARCH}-unknown-linux-gnu*
125126

127+
# Install tmux from source (protocol version must match host for socket bridge)
128+
# Same major.minor usually works; exact match is pragmatic, not required.
129+
ARG TMUX_VERSION=3.6a
130+
ARG TMUX_SHA256=b6d8d9c76585db8ef5fa00d4931902fa4b8cbe8166f528f44fc403961a3f3759
131+
RUN --mount=type=cache,target=/tmp/tmux-cache,sharing=locked \
132+
set -eu && \
133+
cd /tmp/tmux-cache && \
134+
TARBALL="tmux-${TMUX_VERSION}.tar.gz" && \
135+
wget -q "https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/${TARBALL}" && \
136+
echo "${TMUX_SHA256} ${TARBALL}" | sha256sum -c - && \
137+
tar -xzf "${TARBALL}" && \
138+
cd "tmux-${TMUX_VERSION}" && \
139+
./configure --prefix=/usr/local && \
140+
make -j"$(nproc)" && \
141+
make install && \
142+
rm -rf /tmp/tmux-cache/tmux-* && \
143+
hash -r && \
144+
tmux -V
126145

127146
ENV NPM_CONFIG_FETCH_RETRIES=5 \
128147
NPM_CONFIG_FETCH_RETRY_FACTOR=2 \
@@ -207,9 +226,11 @@ RUN curl -fsSL "https://raw.githubusercontent.com/lroolle/atlas-cli/${ATLAS_CLI_
207226
USER root
208227

209228
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
229+
COPY scripts/deva-bridge-tmux /usr/local/bin/deva-bridge-tmux
210230

211-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \
212-
chmod -R +x /usr/local/bin/scripts || true
231+
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh && \
232+
chmod 755 /usr/local/bin/deva-bridge-tmux && \
233+
chmod -R 755 /usr/local/bin/scripts || true
213234

214235
WORKDIR /root
215236

docker-entrypoint.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,22 @@ setup_nonroot_user() {
202202

203203
if [ "$DEVA_UID" != "$current_uid" ]; then
204204
[ "$VERBOSE" = "true" ] && echo "[entrypoint] updating $DEVA_USER UID: $current_uid -> $DEVA_UID"
205-
usermod -u "$DEVA_UID" -g "$DEVA_GID" "$DEVA_USER"
205+
# usermod may fail with rc=12 when it can't chown home directory (mounted volumes)
206+
# The UID change itself usually succeeds even when chown fails
207+
if ! usermod -u "$DEVA_UID" -g "$DEVA_GID" "$DEVA_USER" 2>/dev/null; then
208+
# Verify what UID we actually got
209+
local actual_uid
210+
actual_uid=$(id -u "$DEVA_USER" 2>/dev/null)
211+
if [ -z "$actual_uid" ]; then
212+
echo "[entrypoint] ERROR: cannot determine UID for $DEVA_USER" >&2
213+
exit 1
214+
fi
215+
if [ "$actual_uid" != "$DEVA_UID" ]; then
216+
echo "[entrypoint] WARNING: UID change failed ($DEVA_USER is UID $actual_uid, wanted $DEVA_UID)" >&2
217+
# Adapt to reality so subsequent operations use correct UID
218+
DEVA_UID="$actual_uid"
219+
fi
220+
fi
206221
# Only chown files owned by container, skip mounted volumes
207222
find "$DEVA_HOME" -maxdepth 1 ! -type l -user root -exec chown "$DEVA_UID:$DEVA_GID" {} \; 2>/dev/null || true
208223
fi

0 commit comments

Comments
 (0)