A browser reaching this panel is often a long way away, so the session is now built to survive the link rather than be torn down by it. Liveness first, because the old numbers were wrong: pings every 20s with a 60s deadline. The previous setting was a single 15-minute timer, and since the browser answers pings automatically it never fired for an idle user — the one thing it did do was take fifteen minutes to notice a peer that had vanished. - the shell outlives the socket. When the client goes away the PTY keeps running for 120s with its output landing in a 256 KiB ring (with a notice if anything is dropped). A client that comes back with its token resumes the same shell, so a tunnel, a cell handover or a sleeping laptop no longer costs a running build. The token travels in the first frame rather than the URL, is bound to the user, and lives in sessionStorage — reloading resumes, closing the window lets go. - the page has its own heartbeat: a browser cannot see protocol-level pings, so the server also sends a small JSON one and the page reconnects with exponential backoff and jitter after 45s of silence, up to eight attempts. - a handshake that neither completes nor fails would otherwise stall the retry loop for good, so an attempt that has not opened in 15s is abandoned. - at the per-user cap the client is offered its slots back instead of being refused, which is the only way to reclaim a window the user can no longer see. Where there is room, the count is just a notice. Three bugs the browser testing turned up, all of them real: - Pty writes had no deadline, so a wedged Incus connection blocked the handler forever — resume hung after any pause long enough to break that socket. - a chunk whose write failed was neither delivered nor buffered. - a session stayed resumable between Close and the end of its drain. An operator can now turn the whole thing off with `vps config set panel.web_ssh false`: the button is not rendered and the page, the script and the connection all refuse. It defaults on, including for a config written before the setting existed.
2.8 KiB
Web SSH
A terminal in the browser: a shell inside the user's own container, opened from the Machine card (next to Start / Stop / Restart) in the user panel.
It is not an SSH client. The container runs on the panel host, so the panel talks to it through the Incus exec API over the daemon's unix socket, with a PTY. Nothing listens on the container side, there are no keys to hand out, and there is no extra network path to arrange.
Shape of it
- Its own window. The button opens a separate browser window rather than a dialog, so it can be dragged to any size, moved to another screen or maximised. It remembers the size it was last dragged to and reopens that way.
- Root shell. The shell runs as root inside the account's own container, which is the same access the user already has over SSH. The container is always taken from the session: a client cannot name a container it does not own.
- One account, two shells. A third connection is offered the option to close the others instead of being refused, which is also how a slot held by a closed window is reclaimed.
Surviving a bad connection
The panel is often reached from far away, so the browser side is built to survive the link rather than to be torn down by it.
- Liveness. The server pings every 20 seconds and treats 60 seconds of silence as gone. The browser answers pings from its network stack, so this works in a backgrounded tab too, and a connection that disappears without saying so is noticed in about a minute.
- Reconnect. The page hears a small heartbeat the server sends alongside the protocol pings; hearing nothing for 45 seconds means the socket is dead even though it has not said so. It then reconnects with exponential backoff and jitter, up to eight attempts, after which a manual Reconnect is offered.
- Resume. The shell outlives the socket: when the client goes away the PTY keeps running for 120 seconds and its output goes into a bounded buffer (256 KiB, with a notice if anything is dropped). A client that comes back with its session token resumes the same shell — a running build, an open editor and the shell's variables are all still there.
- Redraw. A resumed terminal starts blank and is rebuilt from the buffered
output, so a full-screen program is asked to repaint itself; if one does not,
Ctrl+Lforces it. - Token. The resume token lives in
sessionStorage, so reloading the page comes back to the shell and closing the window lets it go.
Turning it off
vps config set panel.web_ssh false
The panel restarts and the feature is off: the button is not rendered, and the
terminal page, its script and the terminal connection all refuse. true (the
default) turns it back on. A configuration written before this feature existed
comes out enabled.