From 4b13cab081f66633e3dfb14e75eaf4cee2b49b8f Mon Sep 17 00:00:00 2001 From: Venkat Date: Mon, 3 Aug 2026 08:13:28 +0000 Subject: [PATCH 1/4] feat!: require a tunnel endpoint; always bind the bare hostname Drops the legacy central fallback and the "cde" bind branch. dev() now fails loudly if /etc/glueops/tunnel_endpoint is missing on a CDE VM, rather than tunneling somewhere unexpected. BREAKING CHANGE: images from this release only work with a slackbot that writes /etc/glueops/tunnel_endpoint for every CDE VM. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01ErBUiAYTosnpbF9hvUj3Dn --- developer-setup.sh | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/developer-setup.sh b/developer-setup.sh index 1eb603873d..ba1130a2ce 100644 --- a/developer-setup.sh +++ b/developer-setup.sh @@ -358,31 +358,19 @@ dev() { [ -f /etc/glueops/cde_token ] && export CDE_TOKEN=$(cat /etc/glueops/cde_token) - # Regional sish endpoint written by cloud-init on newer VMs; older VMs - # have no file and stay on the legacy central tunnel. - TUNNEL_ENDPOINT="tunnels.glueopshosted.com" + # Regional sish endpoint, written by cloud-init from the region's + # tunnel_endpoint config. No fallback: every region declares one, and a + # VM with no endpoint has nowhere to tunnel, so surface it loudly instead + # of connecting somewhere unexpected. + TUNNEL_ENDPOINT="" if [ -s /etc/glueops/tunnel_endpoint ]; then - _regional_endpoint="$(head -n1 /etc/glueops/tunnel_endpoint | tr -d '[:space:]')" - [ -n "$_regional_endpoint" ] && TUNNEL_ENDPOINT="$_regional_endpoint" + TUNNEL_ENDPOINT="$(head -n1 /etc/glueops/tunnel_endpoint | tr -d '[:space:]')" + fi + if [ -n "$CDE_TOKEN" ] && [ -z "$TUNNEL_ENDPOINT" ]; then + gum style --padding "0 1" --foreground=196 --bold \ + "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." >&2 + return 1 fi - - # Legacy central sish runs --append-user-to-subdomain: bind "cde" + the - # SSH username -> cde-.tunnels.glueopshosted.com. Regional - # instances don't; the VM binds its bare hostname so URLs are just - # ..tunnels.cde.glueopshosted.com. The slackbot derives - # the access URL from the same endpoint-value rule, so keep them in sync. - TUNNEL_BIND="cde" - [ "$TUNNEL_ENDPOINT" != "tunnels.glueopshosted.com" ] && TUNNEL_BIND="$HOSTNAME" - - # Bootstrap the CDE once per container: cde-boot runs CDE_SETUP_SCRIPT (unset -> the - # default `cde-init`: gh auth + repo clone + AutoGlue setup). Non-fatal, and a no-op on - # older container images that predate cde-boot. - # Pass the env at EXEC time (not just the container's frozen create-time --env-file) so a - # later profile/secret edit reaches a forced re-run, and secrets are scoped to this exec. - ENVFILE_ARG="" - [ -f /etc/glueops/codespace.env ] && ENVFILE_ARG="--env-file /etc/glueops/codespace.env" - # shellcheck disable=SC2086 # ENVFILE_ARG must word-split into flag + path (or nothing) - sudo docker exec $ENVFILE_ARG "$CONTAINER_NAME" bash -lc 'command -v cde-boot >/dev/null 2>&1 && cde-boot || true' || true if [ -n "$CDE_TOKEN" ]; then # IdentitiesOnly keeps a forwarded ssh-agent (present when dev is @@ -391,7 +379,7 @@ dev() { # key per username, so an agent key winning the first-ever connection # locks the VM out once that agent is gone. It also avoids blowing # the server's MaxAuthTries budget on agent keys. - AUTOSSH_PIDFILE="$PID_FILE" autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i ~/.ssh/sish_tunnel_key_id_ed25519 -p 2222 -l $HOSTNAME -R "$TUNNEL_BIND":80:localhost:8000 "$TUNNEL_ENDPOINT" + AUTOSSH_PIDFILE="$PID_FILE" autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i ~/.ssh/sish_tunnel_key_id_ed25519 -p 2222 -l $HOSTNAME -R "$HOSTNAME":80:localhost:8000 "$TUNNEL_ENDPOINT" # Disable VS Code Workspace Trust so folders open without the "Do you trust the # authors…" prompt. Normally a no-op (the server is baked + shimmed at image build); # re-shims if a VS Code update pulled a new server. If the prompt comes back, see the From b698c5b387924a9632dcbd148d4f8afe7801f010 Mon Sep 17 00:00:00 2001 From: Venkat Date: Mon, 3 Aug 2026 08:28:34 +0000 Subject: [PATCH 2/4] fix: restore cde-boot bootstrap; reject the retired central endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit's edit removed the adjacent cde-boot docker-exec block (gh auth, repo clone, AutoGlue setup) along with the tunnel code — restored verbatim. Also rebases the change onto the current release so IdentitiesOnly=yes is preserved, and adds an explicit failure when the endpoint is still the retired central host, which would otherwise bind bare while that sish prefixes the username. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01ErBUiAYTosnpbF9hvUj3Dn --- developer-setup.sh | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/developer-setup.sh b/developer-setup.sh index ba1130a2ce..ff896f62a1 100644 --- a/developer-setup.sh +++ b/developer-setup.sh @@ -359,19 +359,37 @@ dev() { [ -f /etc/glueops/cde_token ] && export CDE_TOKEN=$(cat /etc/glueops/cde_token) # Regional sish endpoint, written by cloud-init from the region's - # tunnel_endpoint config. No fallback: every region declares one, and a - # VM with no endpoint has nowhere to tunnel, so surface it loudly instead - # of connecting somewhere unexpected. + # tunnel_endpoint config. No fallback: every region declares one, and the + # VM always binds its bare hostname, so a missing endpoint — or the + # retired central host, which prefixes the SSH username onto binds — can + # only produce an access URL nothing serves. Fail loudly instead. TUNNEL_ENDPOINT="" if [ -s /etc/glueops/tunnel_endpoint ]; then TUNNEL_ENDPOINT="$(head -n1 /etc/glueops/tunnel_endpoint | tr -d '[:space:]')" fi - if [ -n "$CDE_TOKEN" ] && [ -z "$TUNNEL_ENDPOINT" ]; then - gum style --padding "0 1" --foreground=196 --bold \ - "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." >&2 - return 1 + if [ -n "$CDE_TOKEN" ]; then + if [ -z "$TUNNEL_ENDPOINT" ]; then + gum style --padding "0 1" --foreground=196 --bold \ + "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." >&2 + return 1 + fi + if [ "$TUNNEL_ENDPOINT" = "tunnels.glueopshosted.com" ]; then + gum style --padding "0 1" --foreground=196 --bold \ + "❌ ERROR:" "This region still points at the retired central tunnel; it must use a regional endpoint." >&2 + return 1 + fi fi + # Bootstrap the CDE once per container: cde-boot runs CDE_SETUP_SCRIPT (unset -> the + # default `cde-init`: gh auth + repo clone + AutoGlue setup). Non-fatal, and a no-op on + # older container images that predate cde-boot. + # Pass the env at EXEC time (not just the container's frozen create-time --env-file) so a + # later profile/secret edit reaches a forced re-run, and secrets are scoped to this exec. + ENVFILE_ARG="" + [ -f /etc/glueops/codespace.env ] && ENVFILE_ARG="--env-file /etc/glueops/codespace.env" + # shellcheck disable=SC2086 # ENVFILE_ARG must word-split into flag + path (or nothing) + sudo docker exec $ENVFILE_ARG "$CONTAINER_NAME" bash -lc 'command -v cde-boot >/dev/null 2>&1 && cde-boot || true' || true + if [ -n "$CDE_TOKEN" ]; then # IdentitiesOnly keeps a forwarded ssh-agent (present when dev is # re-run from a tmux session after an SSH login) from offering its From 5ba03335034e25bc91040ec3ceb77f1f4cb17df0 Mon Sep 17 00:00:00 2001 From: Venkat Date: Mon, 3 Aug 2026 08:58:02 +0000 Subject: [PATCH 3/4] fix: validate the endpoint after cde-boot, not before it A tunnel misconfiguration should cost only the tunnel: cde-boot (gh auth, repo clone, AutoGlue) is unrelated, the VM stays reachable over the tailnet, and every dev re-run previously hit the same early return so the bootstrap could never run. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01ErBUiAYTosnpbF9hvUj3Dn --- developer-setup.sh | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/developer-setup.sh b/developer-setup.sh index ff896f62a1..a2095dd0e7 100644 --- a/developer-setup.sh +++ b/developer-setup.sh @@ -367,18 +367,6 @@ dev() { if [ -s /etc/glueops/tunnel_endpoint ]; then TUNNEL_ENDPOINT="$(head -n1 /etc/glueops/tunnel_endpoint | tr -d '[:space:]')" fi - if [ -n "$CDE_TOKEN" ]; then - if [ -z "$TUNNEL_ENDPOINT" ]; then - gum style --padding "0 1" --foreground=196 --bold \ - "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." >&2 - return 1 - fi - if [ "$TUNNEL_ENDPOINT" = "tunnels.glueopshosted.com" ]; then - gum style --padding "0 1" --foreground=196 --bold \ - "❌ ERROR:" "This region still points at the retired central tunnel; it must use a regional endpoint." >&2 - return 1 - fi - fi # Bootstrap the CDE once per container: cde-boot runs CDE_SETUP_SCRIPT (unset -> the # default `cde-init`: gh auth + repo clone + AutoGlue setup). Non-fatal, and a no-op on @@ -391,6 +379,20 @@ dev() { sudo docker exec $ENVFILE_ARG "$CONTAINER_NAME" bash -lc 'command -v cde-boot >/dev/null 2>&1 && cde-boot || true' || true if [ -n "$CDE_TOKEN" ]; then + # Validate here, after cde-boot: a bad endpoint costs the tunnel, but + # the container bootstrap (gh auth, repo clone, AutoGlue) is unrelated + # and must still run — the VM stays reachable over the tailnet. + if [ -z "$TUNNEL_ENDPOINT" ]; then + gum style --padding "0 1" --foreground=196 --bold \ + "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." >&2 + return 1 + fi + if [ "$TUNNEL_ENDPOINT" = "tunnels.glueopshosted.com" ]; then + gum style --padding "0 1" --foreground=196 --bold \ + "❌ ERROR:" "This region still points at the retired central tunnel; it must use a regional endpoint." >&2 + return 1 + fi + # IdentitiesOnly keeps a forwarded ssh-agent (present when dev is # re-run from a tmux session after an SSH login) from offering its # keys first: sish's TOFU auth permanently pins the first accepted From 141a194623596019c5114e1de993c7a05dc86bec Mon Sep 17 00:00:00 2001 From: Venkat Date: Mon, 3 Aug 2026 09:41:49 +0000 Subject: [PATCH 4/4] fix: a bad endpoint costs only the tunnel, not the IDE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The earlier guards returned before code serve-web, so a tunnel misconfiguration also withheld the editor — contradicting their own rationale that the VM stays reachable over the tailnet. They now set TUNNEL_OK=0, skip only autossh, and tell the user to reach the VM over Tailscale. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01ErBUiAYTosnpbF9hvUj3Dn --- developer-setup.sh | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/developer-setup.sh b/developer-setup.sh index a2095dd0e7..61af0605ec 100644 --- a/developer-setup.sh +++ b/developer-setup.sh @@ -379,18 +379,21 @@ dev() { sudo docker exec $ENVFILE_ARG "$CONTAINER_NAME" bash -lc 'command -v cde-boot >/dev/null 2>&1 && cde-boot || true' || true if [ -n "$CDE_TOKEN" ]; then - # Validate here, after cde-boot: a bad endpoint costs the tunnel, but - # the container bootstrap (gh auth, repo clone, AutoGlue) is unrelated - # and must still run — the VM stays reachable over the tailnet. + # A bad endpoint costs the tunnel and nothing else: the container + # bootstrap above already ran, and serve-web below still starts, so + # the editor stays usable over the tailnet while the public URL is + # dead. Loud on the console, but never a reason to withhold the IDE. + TUNNEL_OK=1 if [ -z "$TUNNEL_ENDPOINT" ]; then gum style --padding "0 1" --foreground=196 --bold \ - "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." >&2 - return 1 - fi - if [ "$TUNNEL_ENDPOINT" = "tunnels.glueopshosted.com" ]; then + "❌ ERROR:" "/etc/glueops/tunnel_endpoint is missing or empty — this region has no tunnel endpoint configured." \ + "The public CDE URL will not work; reach this VM over Tailscale instead." >&2 + TUNNEL_OK=0 + elif [ "$TUNNEL_ENDPOINT" = "tunnels.glueopshosted.com" ]; then gum style --padding "0 1" --foreground=196 --bold \ - "❌ ERROR:" "This region still points at the retired central tunnel; it must use a regional endpoint." >&2 - return 1 + "❌ ERROR:" "This region still points at the retired central tunnel; it must use a regional endpoint." \ + "The public CDE URL will not work; reach this VM over Tailscale instead." >&2 + TUNNEL_OK=0 fi # IdentitiesOnly keeps a forwarded ssh-agent (present when dev is @@ -399,7 +402,7 @@ dev() { # key per username, so an agent key winning the first-ever connection # locks the VM out once that agent is gone. It also avoids blowing # the server's MaxAuthTries budget on agent keys. - AUTOSSH_PIDFILE="$PID_FILE" autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i ~/.ssh/sish_tunnel_key_id_ed25519 -p 2222 -l $HOSTNAME -R "$HOSTNAME":80:localhost:8000 "$TUNNEL_ENDPOINT" + [ "$TUNNEL_OK" = 1 ] && AUTOSSH_PIDFILE="$PID_FILE" autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i ~/.ssh/sish_tunnel_key_id_ed25519 -p 2222 -l $HOSTNAME -R "$HOSTNAME":80:localhost:8000 "$TUNNEL_ENDPOINT" # Disable VS Code Workspace Trust so folders open without the "Do you trust the # authors…" prompt. Normally a no-op (the server is baked + shimmed at image build); # re-shims if a VS Code update pulled a new server. If the prompt comes back, see the