Repository navigation
SSH Computers: start the App Server on a Linux computer over SSH - #592
Merged
Merged
Conversation
Add ADR-0069: an App Shell installs, starts, and keeps the App Server on a Linux computer over SSH, holds its token, and forwards its loopback endpoint through the SSH connection. Covers version cases with confirmed updates, a keeper client for lifetime, system ssh on Desktop and an embedded client on Android. Add the SSH Computer term to CONTEXT.md.
Add client/replacementImpact (protocol 2.2). It lists the other attached clients with their kind, name, product version, protocol version, and whether they are reconnecting, plus active turns and pending Task requests, without starting a replacement. A client that installed another App Server version shows it before the user confirms the update (ADR-0069). The App Server now keeps each client's declared protocol version.
Add `openaide-app-server keep start|stop`, which an App Shell's SSH launcher runs on an SSH Computer (ADR-0069). `keep start` attaches to or starts the App Server for the default state root, detaches a keeper client into its own session, and prints one JSON report with the endpoint, token and versions. The keeper holds the App Server while Turns run, while a Task waits for the user for up to 24 hours, and for 30 minutes after the last App Shell leaves. One keeper runs per state root, guarded by a lease lock. An App Server of a different version is reported as a mismatch instead of being replaced; the launcher then chooses `--replace` or `--join` after asking the user. The handoff gains a reporting policy for incompatible servers, and the protocol gains the `keeper` shell kind.
Add the launcher App Shells share for SSH Computers (ADR-0069). Through a transport each App Shell provides, it checks that the computer is Linux x64, uploads this release's App Server only when the installed one's SHA-256 differs, has the installed binary's keeper attach to or start the App Server, and forwards the loopback endpoint. An older App Server that accepts this release's protocol is joined and offered an update; an older one it cannot use needs a confirmed update; a newer or unordered one is never replaced. The launcher also reports when logind would end the App Server at logout without lingering. Commands are single `sh -c` lines that every common login shell passes through unchanged, and each stage logs its outcome and duration without the token or paths.
The Desktop runs the shared launcher over the system `ssh`. It relays ssh's questions to the WebView through an askpass helper, multiplexes connections on macOS and Linux, and keeps a supervised loopback forward to the App Server. The Linux App Server for the matching release is downloaded once, checked against a SHA-256 pinned at build time, and cached; Windows reuses its bundled WSL copy. New commands list SSH hosts from the SSH configuration, add an SSH Computer, report its status and last failure, confirm an update, stop its keeper, and answer prompts. The launcher now also logs the start of each stage.
Settings lists SSH Computers beside paired ones, adds one by host with suggestions from the SSH configuration, stops OpenAIDE there after confirmation, and offers an update when its App Server is older. The update warning lists what the replacement interrupts, as the App Server reports it. It also suggests `loginctl enable-linger` when logind would end the App Server at logout. The Desktop shows ssh's questions in a dialog above the startup screen and the app, restores an SSH Computer's App Server after sleep like its own, and offers the confirmed update when a start fails because the App Server there is too old.
Adds the openaide-android-ssh crate: russh sessions behind UniFFI bindings for the Mobile App. It reads and pins the host key, adds the phone's key with a password used once, and runs the shared SSH Computer launcher over the session with a loopback forward. Its events are metadata only. The release archive unpacking moves into remote-client behind the release-archive feature so the Desktop and Android verify and unpack the Linux App Server the same way; iroh pairing becomes the default pairing feature, which the Android crate leaves out.
The phone can now add an SSH Computer and work on the App Server it starts there, as ADR-0069 describes. Native dialogs read the computer's host key for the user to confirm, pin it, and add the phone's own key, either by the user or with a password used once and never stored. The key is kept encrypted with Android Keystore. HeldServer takes the launch, heartbeat and relaunch lifecycle that the Termux App Server used, so LocalServer and SshServer share it. The gateway serves the SSH workspace on its own port, and the background service reports its Tasks the same way. A launch that finds an App Server too old asks before replacing it and warns that agents and other apps stop. The Gradle build compiles the Rust library with the NDK and generates its Kotlin bindings. The release build verifies the published Linux App Server against the checksum built into the app before uploading it.
Remote settings on the Mobile App list the phone's SSH Computers beside This phone and paired computers. Each row can be used, stopped or forgotten, and the section adds a computer through the phone's native flow. The confirmation dialogs say what stopping or updating ends, and the section shows the update and linger notices from the last launch.
Test APKs now bundle the static Linux App Server built from the same commit, so SSH Computers work in them before a version is published. Releases still pin the published binary.
Main added a paired computer's Task notifications and the shared AttentionWire helpers. HeldServer now reads Task attention and initializes through AttentionWire, so work on an SSH Computer notifies like work on this phone. The background service treats an SSH Computer as a held workspace: it polls it, reports it as "ssh" in diagnostics, and keeps running when the user switches away from it; only a paired computer's watch stops on a switch. The protocol bindings are regenerated with both client/replacementImpact and task/attentionWait.
ADR-0071 decides what a Terminal is and what it is not; the plan records the approved presentation and how each layer implements it.
Protocol 2.3 adds terminals/open, terminals/attach and terminals/close, the terminals subscription scope, and the open-Terminal count in the replacement impact. Clients stop advertising the unread openTerminal shell capability, which still deserializes for 2.2 clients.
App Server owns each login shell, keeps about 10,000 lines of replay in memory, and streams input and output on a WebSocket per viewer, admitted by a single-use attach token. Timed text pings let both sides notice a silent connection, and Windows marks a shell with a child process busy.
A browser WebSocket cannot carry the gateway token, so the gateway forwards an exact GET /terminal upgrade and leaves admission to the attach token.
Opens from a Project menu or the Task header, shows a Project's Terminals as tabs, reattaches with replay after a lost connection, adds the phone key row, and warns before an update closes Terminals.
Quit asks natively before its exit fallback starts, from the open count the Frontend reports, and only when this Desktop owns its App Server.
The crate joined the workspace unformatted, which fails the Rust CI job.
Terminals: an interactive shell on the App Server's computer
Ports SSH Computers onto the computer switcher (ADR-0070): - Desktop: SSH Computers are rows of the computers list, marked by their `ssh` destination. Bootstrap, switch and resume recovery reach them through the `ssh` forward, and adding one is a switch like pairing. - Frontend: the `ssh` capability moves onto `ComputersCapability`, and Settings shows SSH rows, Stop, Update and the linger notice there. - Android: leaving an SSH Computer through the phone's switch ends its forward. The phone's SSH Computers stay in its Settings state and switch outside the computer switch (TODO). - The Desktop Quit warning counts this computer as owning its App Server unless the window uses another computer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements ADR-0069: an App Shell installs, starts and keeps the App Server on a Linux computer it reaches over SSH, the way the Mobile App runs it in Termux. This is different from pairing with a computer, which needs an App Shell already running there.
What changes
client/replacementImpactreports what replacing the App Server would interrupt. Akeepcommand holds the App Server on the computer while App Shells or waiting Tasks need it, then leaves so it stops by the existing rule.release-archivefeature.ssh, so the user's configuration, jump hosts, agent and prompts keep working.HeldServeris shared with the Termux App Server, and background work and Task notifications cover an SSH Computer too.Verification
-D warnings.Not verified
Known failures outside this change
cargo clippy -D warningsfor the Desktop crate on Linux, in Windows- and macOS-only code.feed_endpoint_is_version_and_channel_specificfails on Linux.Follow-ups: #589 (arm64 Linux computers), #590 (Windows askpass).