Skip to content

bug(ssh): window-change requests not applied to remote PTY — terminal resize broken #543

@chenstanilovsky

Description

@chenstanilovsky

Description

When connecting to a sandbox, the terminal is allocated with the correct initial dimensions but does not resize when the terminal window changes size. SIGWINCH never reaches the process inside the sandbox.

This directly impacts Claude Code — its TUI renders at the initial terminal size and never redraws when the terminal is resized, leaving the interface clipped or misaligned.

Reproduction steps

# 1. Create a sandbox
openshell sandbox create --name test

# 2. Connect to it
openshell sandbox connect test

# 3. Inside the sandbox, check initial size (correct)
stty size

# 4. Resize your terminal window

# 5. Check size again (unchanged — should have updated)
stty size

The initial stty size matches the terminal. After resizing the terminal window, stty size still reports the original dimensions.

This is also reproducible with Claude Code directly:

# 1. Connect and launch Claude Code
openshell sandbox connect test
claude

# 2. Resize the terminal window — Claude Code's TUI does not redraw to the new size

Expected behavior

After resizing the terminal, stty size should report the new dimensions and processes should receive SIGWINCH. Claude Code should redraw its TUI to fit the new terminal size.

Actual behavior

The remote PTY stays at its initial size. No SIGWINCH is delivered. This can be confirmed by trapping the signal:

trap "echo WINCH" WINCH
# Resize terminal window — "WINCH" is never printed

The SSH client does send the window-change request (confirmed via ssh -vvv):

debug2: client_check_window_change: changed
debug2: channel 0: request window-change confirm 0

But it's not applied to the remote PTY.

Agent Investigation

Explored the OpenShell codebase to understand the SSH proxy architecture:

  • The SSH server is implemented in the openshell-sandbox crate at /crates/openshell-sandbox/src/ssh.rs using the russh crate (v0.57)
  • PTY allocation is fully implemented: pty_request() stores the PTY config, start_shell() dispatches to spawn_pty_shell() which calls nix::pty::openpty() with the requested Winsize
  • The window_change_request() handler exists and calls unsafe_pty::set_winsize() which uses TIOCSWINSZ ioctl on the PTY master fd
  • The SshHandler stores the PTY master fd in self.pty_master: Option<std::fs::File>
  • The gateway tunnels SSH traffic bidirectionally via tokio::io::copy_bidirectional() in /crates/openshell-server/src/ssh_tunnel.rs

The infrastructure for handling window-change appears complete. The client sends the request, the server has the handler. Something in the path between receiving the window-change request and applying TIOCSWINSZ to the PTY master fd may be failing silently.

Environment

  • openshell 0.0.13
  • Linux aarch64
  • OpenSSH client 9.2p1
  • russh 0.57.1 (server banner: SSH-2.0-russh_0.57.1)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions