Skip to content

feat(dsh): ensure the managed web service on every run - #562

Open
lroolle wants to merge 3 commits into
mainfrom
feat/dsh-web-service
Open

feat(dsh): ensure the managed web service on every run#562
lroolle wants to merge 3 commits into
mainfrom
feat/dsh-web-service

Conversation

@lroolle

@lroolle lroolle commented Aug 14, 2026

Copy link
Copy Markdown
Member

Why

Bare dsh refuses to start (--profile <name> is required), so v0.19.0's bare deva.sh dsh was dead on arrival. The official recommendation is running the web service; deva now ensures it on every run.

What

  • Every deva.sh dsh run ensures dsh web in its container: daemonized ($DSH_HOME/web.log), idempotent, survives the launch session. Bare runs follow the service log; args after -- run in the foreground with the service ensured behind them. DEVA_DSH_WEB=0 opts out.
  • dsh binds loopback only and hard-rejects 0.0.0.0 (docker -p can't reach a container-loopback bind): bridge runs get a socat sidecar publishing to the host loopback on the first free port from 3080 (DEVA_DSH_WEB_PORT overrides the probe start). The mapping travels as container env so a reused container announces the port it actually published.
  • Host-net runs skip the sidecar; each container probes a free per-container port, and the ensure check verifies the server is OUR pid namespace's process — another container's server on the same loopback is reported, never adopted.
  • Workspace auto-add: every run (web/tui/headless) seeds the cwd into dsh's workspace registry (storages/workspace.json, domain v2, byte-compatible) so sessions land pre-grouped in the web UI. Idempotent, backs off from state it doesn't own (foreign schema, pending mutation, uninitialized registry with session history). DEVA_DSH_WORKSPACE_AUTO=0 opts out.

Verify

  • bash scripts/test-dsh-auth.sh — 10 new asserts pin publish + sidecar env, host-net no-publish + per-container port, passthrough wiring, web ensured in every mode; all green.
  • Live-smoked in a real container: web UI reachable on the published loopback port, workspace pre-grouped, exec-into-running never double-binds.

🤖 Generated with Claude Code

lroolle and others added 3 commits August 14, 2026 04:36
Bare `dsh` refuses to start (--profile required), so v0.19.0's bare
`deva.sh dsh` was dead on arrival. Every run now ensures `dsh web`
in the container (the official recommendation), daemonized and
idempotent so exec into a running container never double-binds.

dsh binds loopback only and hard-rejects 0.0.0.0, which docker -p
cannot reach: bridge runs get a socat sidecar bridging a publishable
port to the loopback bind, published to the host loopback on the
first free port from 3080 (DEVA_DSH_WEB_PORT overrides). Host-net
runs skip the sidecar but probe a per-container free port so
concurrent containers never adopt each other's server; the ensure
check verifies the server is OUR pid namespace's process, not just
an open port.

Every run also seeds the workspace dir into dsh's registry
(storages/workspace.json, domain v2, byte-compatible) so sessions
from any profile land pre-grouped in the web UI; the seed is
idempotent and backs off from state it does not own. Bare
`deva.sh dsh` follows the service log; args after -- run in the
foreground with the service ensured behind them. DEVA_DSH_WEB=0
and DEVA_DSH_WORKSPACE_AUTO=0 opt out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Publish + sidecar env on bridge, per-container port under host-net
(no publish), wrapper passthrough of user args, and web ensured in
every mode. Existing asserts updated for the dsh-web wrapper argv0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Document the managed dsh web boot (ports, host-net, shared-storage
caveat, workspace seed) and add a three-layer Testing Auth guide:
hermetic wiring tests, dry-run against real auth, live smoke with
the per-agent first-run table. Default homes list catches up with
pi, dsh, and cursor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 14, 2026 11:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes deva.sh dsh usable out-of-the-box by ensuring dsh web is running in the container on every invocation, publishing it safely to the host loopback (with a socat sidecar when bridged), and auto-seeding the current workspace into dsh’s registry so sessions are grouped in the web UI.

Changes:

  • Add a managed “ensure web + workspace seed + passthrough/log-follow” boot wrapper for dsh runs, including host-net vs bridge behavior and loopback publishing.
  • Expand the dsh auth wiring test script with assertions covering web ensure/publish/sidecar and passthrough behavior.
  • Update user docs to reflect the new default deva.sh dsh behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/test-dsh-auth.sh Adds dry-run assertions for web ensure, publish/sidecar wiring, host-net behavior, and passthrough invocation shape.
README.md Documents that deva.sh dsh boots the dsh web UI by default.
docs/quick-start.md Updates quick start command list to mention dsh web UI + workspace auto-add.
docs/authentication.md Adds detailed documentation of dsh web ensuring, sidecar publish behavior, host-net behavior, and workspace auto-add semantics.
agents/dsh.sh Implements setup_dsh_web boot wrapper (ensure web + optional socat + workspace registry seed) and adjusts command invocation to go through the wrapper.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread agents/dsh.sh
Comment on lines +195 to +202
dsh_web_port="${DEVA_DSH_WEB_PORT_CONTAINER:-3080}"
web_log="${DSH_HOME:-$HOME/.dsh}/web.log"
mkdir -p "$(dirname "$web_log")"
if _dsh_web_ours "$dsh_web_port"; then
: # already serving
elif _dsh_port_open "$dsh_web_port"; then
echo "deva: 127.0.0.1:${dsh_web_port} is bound outside this container (another host-net dsh container?) -- not starting dsh web; recreate this container to allocate a fresh port" >&2
else
Comment thread README.md
deva.sh opencode
deva.sh pi
deva.sh dsh
deva.sh dsh # boots the dsh web UI on http://127.0.0.1:3080
Comment thread docs/quick-start.md
deva.sh opencode
deva.sh pi
deva.sh dsh
deva.sh dsh # web UI on http://127.0.0.1:3080; repo auto-added as workspace
Comment thread agents/dsh.sh
if [ -n "${DEVA_DSH_WEB_URL:-}" ]; then
echo "deva: dsh web UI: ${DEVA_DSH_WEB_URL}" >&2
else
echo "deva: dsh web running on container loopback only (container predates the web publish; recreate it for host access)" >&2

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5a53d7c32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread agents/dsh.sh
fi
fi
if [ -n "${DEVA_DSH_PROXY_PORT:-}" ] && ! _dsh_port_open "$DEVA_DSH_PROXY_PORT"; then
setsid nohup socat "TCP-LISTEN:${DEVA_DSH_PROXY_PORT},fork,reuseaddr" "TCP:127.0.0.1:${dsh_web_port}" </dev/null >>"$web_log" 2>&1 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve isolation when publishing the dsh relay

On the default bridge network, TCP-LISTEN without a bind option listens on every container interface, not only on traffic forwarded by Docker's host-loopback publish. Any peer container that discovers this container's bridge IP can therefore connect directly to port 3081 and send a loopback Host header accepted by dsh's trust fence, exposing the dashboard and the target container's mounted workspace across the repository's container isolation boundary. Add authentication or network filtering that prevents peer containers from reaching this relay.

AGENTS.md reference: AGENTS.md:L3-L5

Useful? React with 👍 / 👎.

Comment thread agents/dsh.sh
# Bare `deva.sh dsh` follows the service log; args after -- run
# that dsh invocation in the foreground with the service ensured
# behind it. DEVA_DSH_WEB=0 skips the service entirely.
setup_dsh_web "${remaining_args[@]+"${remaining_args[@]}"}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward the documented dsh opt-out variables

When a user runs DEVA_DSH_WEB=0 deva.sh dsh or sets DEVA_DSH_WORKSPACE_AUTO=0 in the host environment, neither value is added to DOCKER_ARGS or the persistent container's docker exec environment; only the wrapper inside the container reads them, so both resolve to their default of 1. Consequently the documented opt-outs still launch the service and mutate the workspace registry unless users happen to persist the variables through another configuration mechanism.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants