mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
feat: move the terminal fully into the ssh-terminal plugin (B9)
This commit is contained in:
1 parent
535126fd1b
commit
79d77678c9
309 files changed
+41789
-14958
No files matched your search
@@ -1,70 +0,0 @@
|
||||
# SSH Terminal
|
||||
|
||||
The interactive SSH terminal, packaged as a first-party plugin.
|
||||
|
||||
## Why this plugin runs in-process
|
||||
|
||||
Every other plugin runs in a `worker_threads` worker and reaches the server
|
||||
through a structured-clone `postMessage` boundary. That boundary is what makes
|
||||
the capability gate and the audit trail mean anything: a worker plugin cannot
|
||||
hold a db handle, an ssh2 `Client` or a live socket, so every privileged action
|
||||
has to go through the broker.
|
||||
|
||||
This plugin cannot work that way. It _is_ the SSH transport rather than a
|
||||
consumer of one:
|
||||
|
||||
- it owns a `WebSocketServer` on port 30002,
|
||||
- it holds long-lived `ssh2.Client` objects and PTY streams,
|
||||
- it needs plaintext host credentials to connect at all.
|
||||
|
||||
None of those can cross a structured-clone boundary, and `ctx.hosts` returns a
|
||||
13-field allowlist that deliberately excludes `password`, `key` and
|
||||
`keyPassword`. So `ssh-terminal` runs on the main thread instead, under the
|
||||
`process:transport-owner` tier.
|
||||
|
||||
Membership in that tier is a hardcoded allowlist in
|
||||
`src/backend/plugins/first-party.ts`. A manifest cannot ask for it: declaring
|
||||
`process:transport-owner` without being on the list is a validation error. The
|
||||
tier has no capability gate and no audit interception, which is only defensible
|
||||
because nothing installable can enter it.
|
||||
|
||||
## What lives where
|
||||
|
||||
The terminal's implementation stays in `src/backend/hosts/terminal/`. Only the
|
||||
lifecycle lives here. Two reasons it was not copied in:
|
||||
|
||||
- `session-manager.ts` has six consumers outside the terminal (terminal routes,
|
||||
open-tabs, user-image-storage, collab, and session-sharing twice). It is
|
||||
shared infrastructure, not plugin-private code.
|
||||
- host key verification (`host-key-verifier.ts`), jump host chaining
|
||||
(`jump-host-chain.ts`) and host resolution (`host-resolver.ts`) each have
|
||||
around eight consumers already. They keep working by _not_ moving.
|
||||
|
||||
The SSH connection pool also stays in core. The terminal never used it: it
|
||||
calls `new Client()` directly. The pool's consumers are metrics, tmux, tunnel,
|
||||
fleet routes, AI tools, automations and `ctx.ssh` itself, so moving it here
|
||||
would have made all of them depend on the terminal being enabled.
|
||||
|
||||
Only `SSHAuthManager` is terminal-exclusive.
|
||||
|
||||
## WebSocket: still port 30002, not /plugin-api/
|
||||
|
||||
The terminal WS was deliberately left on its own port and path.
|
||||
|
||||
`/plugin-api` has no auth middleware, so routing terminal auth through it would
|
||||
be a regression. Beyond that, `/ssh/websocket/` is mapped in both nginx configs,
|
||||
three frontend call sites build that URL (`Terminal.tsx`, `SharedSessionView`,
|
||||
`CollabRoomTab`), Electron's `buildOriginWsUrl` pins `localPort: 30002`, and the
|
||||
8-attempt reconnect plus detach/reattach buffering all depend on the current
|
||||
path.
|
||||
|
||||
The plugin owns the port's lifecycle instead: enabling it calls
|
||||
`startTerminalServer()`, disabling it calls `stopTerminalServer()`, which closes
|
||||
live sessions and frees the port.
|
||||
|
||||
## Disabling
|
||||
|
||||
Disabling behaves like any other plugin. No confirmation dialog, no special
|
||||
casing. The terminal tab stops being offered from the host list, command
|
||||
palette and quick connect, the port stops listening, and every other feature
|
||||
(metrics, docker, tunnels, file manager) keeps working off its own connections.
|
||||
Reference in new issue
Block a user