get2knowio / get2knowio/remo

web: a second browser tab silently doubles terminals per target, halving the effective cap

Open
#177 1 comment 0 reactions 0 assignees View on GitHub
enhancement question
Dominant language
Python
Stars
2
Forks
0
Avg merge
15h 59m
Merged PRs (30d)
9

Description

## Symptom

Opening the `remo web` console in a second browser tab silently doubles terminal
consumption. Eight panes in two tabs is sixteen live PTYs, sixteen `ssh`
processes, and sixteen cap slots — which is exactly the default
`terminal_cap_per_client`, so the second tab is enough on its own to lock the
operator out with `cap_reached` (see #176 for the diagnostics half of this).

Nothing warns about it, and the duplication is invisible from the UI: both tabs
look correct, because both *are* correct in isolation.

## Why it happens

Three independent behaviours compose into it:

1. **The layout is shared, so the second tab restores the same panes.**
`frontend/src/state/workspace.ts` persists `panes` + `focusedId` to
`localStorage` (FR-034), which is per-origin, not per-tab. A new tab therefore
comes up with the *identical* pane set rather than an empty one.
2. **Each pane mounts its own connection.** Every `TerminalCard` drives a
`TerminalConnection`, whose `attach()` calls `createTerminal()` on mount.
3. **The service never dedups.** `TerminalRegistry.register()`
(`web/terminal_registry.py:108-146`) mints a fresh `terminal_id` per call
with no lookup by `session_target_id`, so two requests for the same target
produce two independent attachments — and two `ssh … remo-host sessions
attach` process groups against one remote Zellij session.

Worth noting the remote side survives this: `remo-host sessions attach` joins the
existing Zellij session, so the two tabs show the same content and neither
corrupts the other. The cost is entirely local — doubled PTYs, doubled ssh, and
a cap that empties twice as fast as the operator expects.

## The actual decision

This is not obviously a bug, which is why it deserves a deliberate answer rather
than a patch. Three defensible designs:

- **A — Share the attachment.** `register()` returns the existing live
attachment when one already exists for that `session_target_id` + `client_id`.
Cheapest on resources and matches the operator's mental model ("one terminal
per target"). But the WS token is single-use and one PTY now has two readers:
output fan-out, resize arbitration (two tabs, two viewport sizes, one
`TIOCSWINSZ` — see #174), and close semantics ("whose DELETE wins?") all have
to be designed. This is a real feature, not a tweak.
- **B — Keep them independent, make the cost visible.** Leave the model alone and
fix the surprise: count duplicates honestly in the `cap_reached` message
(#176), and optionally badge a pane that is already open elsewhere. Smallest
change, keeps per-tab resize correct, and is arguably the honest answer given
the caps exist precisely to bound this.
- **C — Don't restore panes into a second tab.** Scope the *live* pane set per
tab (`sessionStorage`) while keeping the saved layout in `localStorage`, so a
new tab opens empty and the operator chooses what to attach. Removes the
silent doubling at its source, at the cost of changing restore behaviour that
FR-034 deliberately specified.

My weak preference is **B now, C considered separately** — A is the only one that
needs new protocol design, and the multi-reader resize problem is exactly the
class of bug #174 just fixed.

## Acceptance

Whichever direction is chosen:

- The behaviour is documented in `docs/web-session-interface.md` — today the
duplication is undocumented in either direction.
- An operator with two tabs open can tell, from the product, that they have two
attachments per target.
- If A is chosen: resize arbitration, output fan-out, and close semantics are
specified before implementation, and the single-use token model
(`web/tokens.py`) is revisited.

Found while diagnosing a `cap_reached` report; the reporter had two consoles
open and no way to see that it mattered.

Contributor guide

Open the contributing guide

Research direction

Read frontend/src/state/workspace.ts and web/terminal_registry.py:108-146 to trace layout restoration and attachment creation, then review web/tokens.py, docs/web-session-interface.md, and the constraints referenced in #174 and #176. First establish which of A, B, or C is accepted; done means the selected behavior is documented and an operator with two tabs can tell that attachments are duplicated.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, typescript
Domain
backend, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.