TermAl

Feature Reference: Project-Scoped Remotes

This document describes the project-scoped remote architecture that is currently implemented in TermAl.

Status

Implemented for SSH-backed remote execution through the local control plane.

The browser always talks to the local TermAl server. The local server owns the browser-facing API, persisted preferences, project list, workspace layouts, and event stream. Projects may be bound to the built-in local remote or to an SSH remote. Sessions, orchestrator instances, terminal commands, file operations, git operations, review documents, and instruction search route from that project ownership.

Core model

Ownership

Remote config

The shipped config shape is intentionally small:

type RemoteConfig = {
  id: string;
  name: string;
  transport: "local" | "ssh";
  enabled: boolean;
  host?: string | null;
  port?: number | null;
  user?: string | null;
};

No private keys or secret material are stored. TermAl relies on the user’s system ssh and ssh-agent setup.

SSH connection model

For SSH remotes, the local server creates a forwarded local port from the 47000-56999 range:

Browser -> local TermAl :8787 -> ssh -L local_port:127.0.0.1:8787 -> remote TermAl

Startup attempts:

  1. Managed server: run ssh ... remote termal server, then probe the forwarded /api/health. This intentionally stays a direct remote command so Windows SSH hosts do not need a POSIX shell just to start TermAl.
  2. Tunnel only fallback: run ssh -N ... and expect a TermAl server to already be running on the remote host.

Both modes use batch SSH, ExitOnForwardFailure, and keepalive options. Managed mode starts a remote server only for the lifetime of the SSH process. Saving a remote definition does not start, stop, install, or upgrade anything.

Remote registration and upgrades

SSH remotes expose one-shot lifecycle actions from the Remotes preferences panel:

Registration is intentionally based on an existing remote enlistment/checkout. TermAl does not clone the repository, install Rust, configure SSH keys, or manage a system service. The local server invokes both actions as one-shot SSH commands, using sh -lc for POSIX checkout paths and encoded PowerShell for Windows checkout paths, then captures stdout/stderr and returns the sanitized output to the browser.

Build / upgrade can run for several minutes on a slow remote checkout. The preferences panel keeps the action pending during that request, but build output is returned only after the remote command finishes; progress streaming is not part of the current lifecycle action API.

Managed startup still runs termal server from the remote command environment. If you want the managed server path to pick up the binary installed by Build / upgrade, put ~/.termal/bin on the remote user’s PATH.

Routing rules

Project-scoped routes resolve the project first:

Session-scoped routes resolve the session’s remote mapping:

The UI passes canonical local project/session ids. Remote-native ids stay inside the backend mapping layer.

State and events

The local server subscribes to each active remote’s /api/events stream and merges remote snapshots/deltas into local browser-facing state.

Important invariants:

Project deletion

DELETE /api/projects/{id} removes the local project reference only.

For local and remote-backed projects:

This is deliberate. Project deletion in TermAl is a local organization action, not a remote filesystem or remote database deletion.

Terminal behavior

Terminal commands use the same project/session routing rules as file and git operations.

Current limitations

Non-goals