Commit Graph

3 Commits (142b531d45708aa301eb8a1e3f9372f44988ccfe)

Author SHA1 Message Date
boban e3b5aa5902 fix(terminal): correct mobile sizing — fit after web-font load + open PTY at the visible grid
On a phone the terminal clipped on the right: FitAddon measured the column count before
JetBrains Mono finished loading (narrower fallback metrics → too many columns → the wider real
font then overflowed and the right edge was cut). And the PTY opened at a fixed 80x24, so the
remote shell's first screenful (MOTD/prompt) was generated at 80 columns and wrapped awkwardly
on a ~43-column viewport.

- refit immediately, on the next animation frame, and again on document.fonts.ready (font race)
- refit on the 'ready' frame before sending the size
- pass the browser's cols/rows on the WS URL; the sidecar opens the host PTY / SSH shell at that
  size (clamped 1..1000), so output is born at the visible width

Verified in-browser at 390px: PTY reports 43x23 (matches the box), no horizontal overflow, clean
wrapping, zero console errors; desktop server+host terminals and cross-origin rejection still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:08:12 +02:00
boban 05042c3582 fix(terminal): clean Clusev-host shell prompt — node@clusev:~ not node@<hash>:/workspace
The host terminal is a shell in the sidecar container; it opened in the read-only
repo mount (/workspace) with the prompt showing the random container id, so it read
like a stray container rather than "the Clusev host".

- give the sidecar a stable `hostname: clusev` (dev + prod) → prompt host is `clusev`
- land the host PTY in /home/node (a real, writable home) and set HOME to it, so the
  shell opens at ~ like a normal terminal instead of /workspace

Server (SSH) terminals are unchanged. Verified in-browser: prompt reads node@clusev,
pwd is /home/node, HOME=/home/node, no container hash / no /workspace; server terminal
still connects and runs; zero console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 06:31:49 +02:00
boban 07fe404ce9 feat(terminal): web terminal — per-server SSH + Clusev host PTY (xterm.js + node sidecar)
A dedicated /terminal page with a target rail: one terminal per fleet server (SSH)
plus one for the Clusev host itself. php-fpm cannot hold an interactive PTY, so a
small Node sidecar (ws + ssh2 + node-pty) bridges xterm.js to the PTY.

Flow: the Livewire page mints a single-use, 60s TerminalSession token and dispatches
it to an Alpine xterm island; the browser opens a same-origin WS (/terminal/ws,
proxied by nginx in dev / Caddy in prod to terminal:3000); the sidecar burns the
token at an internal resolve endpoint (shared-secret header, private network only)
for a connection spec, then opens an SSH PTY (decrypted credential, never sent to the
browser) or a local host PTY. Real shell = native Tab/Shift-Tab completion.

Security:
- token burned ATOMICALLY (single conditional UPDATE) — no double-open race
- resolve endpoint guarded by hash_equals shared secret, exempt from CSRF + PanelScheme
  host enforcement (private net only), returns decrypted creds over the internal net
- sidecar enforces same-origin on the WS upgrade (hostname match, port-agnostic)
- single-use 60s tokens, swept daily so the table can't grow unbounded

Verified in-browser (R12): host + server terminals connect, accept keyboard input,
run commands, Tab-complete; cross-origin WS rejected; zero console errors. 9 feature
tests cover minting, the resolve spec, single-use burn, expiry, and locked creds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 03:21:10 +02:00