Conversation
Ziinc
commented
Sep 27, 2026
Ziinc
left a comment
Collaborator
Author
There was a problem hiding this comment.
Inline notes on why the main changes were made: build gating, platform detection, the connection hook, restore and cutoff, idempotency keys, and deep links.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
tauri-plugin-clihas no Android/iOS support. ThetreqCLI entry point only makes sense on desktop, so the plugin init,CliExtblock,mod cliandis_cli_processare now#[cfg(desktop)], and mobile hard-codescli_process = false.- The matching Cargo change moves the crate into a
cfg(not(any(android, ios)))target table.Cargo.lockdoes not change.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- A capability with no
platformsapplies everywhere. Without this limit it would ask forcli:defaulton mobile, where the plugin is no longer linked, and the mobile build would fail at tauri-build. Mobile permissions stay inmobile.json.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- The shell is a build property. A narrow desktop window still has local repos and PTYs, so viewport width was the wrong signal.
TAURI_ENV_PLATFORMis set by the Tauri CLI for the target being built and exposed throughenvPrefixinvite.config.ts. This follows Tauri's recommended setup and avoids a round trip to the plugin-os crate or a Rust command at startup.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- It reuses
connectExistingReadyInstance/wakeManagedInstance/reauthenticateManagedInstance, so mobile gets the same register -> issue -> activate -> renew sequence as the Dashboard instead of the old one-shot flow with no renewal. - Key registration is keyed on the device key fingerprint. The shared
keystore:devicereference is the same string on every phone, so keying on it would make a second device replay the first device's registration. refreshAfterResumeexists because timers stop while the OS suspends the app. On resume it re-reads instance status and reconnects if the lease is due, or if the instance or generation changed. A cut-off endpoint is never reconnected automatically.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- On launch or resume it remounts
RemoteRepoScreen(viakey={epoch}) instead of trusting mounted state. Every screen then re-reads workspaces, agent status and PTY sessions from the VM (AC7). Short trips to the background (under 30 s) keep an open terminal as it was. restoringRefstops the save effect from overwriting the remembered repo withnullin the gap between "connected" and the probe finishing.- A cutoff replaces the whole repository view with the reauthentication block, so no remote screen or terminal can be used until
reauthenticatesucceeds (AC8).
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- The old
Date.now()keys changed on every tap, so retrying after an ambiguous result looked like a new command to the VM and could apply twice. - A key belongs to one set of inputs until a confirmed outcome (
applied/already_applied). Pending keys live in a map, so doing something else in between does not lose the retry key. - I also applied it to the rebase/commit/push/resolve screens in
RemoteWorkspaceMutationScreens.tsx, which had the same bug.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- The Rust
on_open_urlhandler is desktop-only, so mobile reads links from the deep-link plugin in JS.getCurrentcovers the cold-start link andonOpenUrlcovers links that arrive while the app runs. handledTokensis module-level becausegetCurrentkeeps returning the launch link. Sign-in tokens are single-use, so exchanging one twice would show a spurious error.
Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment.
- The web sign-in page ends at
treq://auth/callback?token=..., so mobile registers that custom scheme (appLink: false, so no hosted verification files are needed). - The deep-link plugin's build script turns this entry into the Android intent filter and the iOS
CFBundleURLTypesin the generated native projects.
Generated by Claude Code
Ziinc
pushed a commit
that referenced
this pull request
Sep 27, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PGXpwptLRetsjFcHkL8x5
…tificate The control plane returns the issued certificate plus an endpoint whose authentication names a server-side key id. The native transport treated that id as a file under ~/.ssh and the certificate was never used, so managed-VM SSH login could not succeed on desktop or mobile. - SshAuthentication::Certificate carries the certificate text inline (optional, falls back to <key>-cert.pub) and a local key reference: a key or .pub path on desktop, or the reserved keystore:device on mobile, resolved through a device-key provider on the pool. - The client composes the endpoint it activates from the server endpoint, the local key reference and the certificate; renewal swaps in the new certificate for future connections without stopping the renewal loop or dropping open channels. - Dashboard Connect/Provision now run the real connect flows and report errors in the setup panel. Wake uses a per-attempt idempotency key. - Mobile RemoteConnectPanel dispatches with the device key and the issued certificate. - The Sprites adapter wakes a paused Sprite with a no-op exec instead of a metadata GET, which the Sprites docs do not count as activity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbYSPHBRmDYwPJpHG33Y4t
Ziinc
force-pushed
the
claude/prd-gaps-managed-ssh-login
branch
from
September 27, 2026 16:58
65102d0 to
ddb7558
Compare
… and cutoff - Pick the shell from the build target (TAURI_ENV_PLATFORM), not the window width; `?shell=mobile` previews it in desktop dev builds. - Mobile flow is sign in -> connect -> choose repository/workspace. The managed instance uses the shared managed-ssh-connection state machine with the keystore device key, starts certificate renewal, and blocks remote screens on a credential cutoff until reauthentication. - Known repositories and user-managed SSH endpoints saved on the device are offered, with a free-text path as fallback. - Persist endpoint, repository path and screen (identifiers only) and on launch or resume re-query instance status, re-issue the certificate when due, and reload remote screens from the VM. - Idempotency keys belong to the user action and are reused on retry; Agent Stop is confirmed and reports ambiguous results. - Mobile sign-in through the auth store plus a `treq` deep-link scheme for Android/iOS. - Gate tauri-plugin-cli and the `cli` module to desktop; limit the desktop capability to desktop platforms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbYSPHBRmDYwPJpHG33Y4t
Ziinc
force-pushed
the
claude/prd-gaps-mobile-remote-shell
branch
from
September 27, 2026 17:00
c19e63e to
ba85f4d
Compare
This branch has not been deployed
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.
Summary
This PR closes gaps against the mobile PRD acceptance criteria 1, 2, 4, 7 and 8:
tauri-plugin-cliand theclimodule now build only on desktop. The desktop capability file is limited to desktop platforms, so it no longer asks forcli:defaulton Android or iOS.TAURI_ENV_PLATFORM) instead of the window width. A narrow desktop window keeps the desktop shell. Desktop dev builds can preview the mobile shell with?shell=mobile.useAuthStore. Thetreq://auth/callbackdeep link is registered for mobile, and its token is exchanged through the deep-link plugin (getCurrent+onOpenUrl).managed-ssh-connection.tswith the keystore device key and starts certificate renewal. The mobile shell listens forremote://cutoffevents. A cutoff showsRemoteStatusBannerin itscutoffstate and hides every remote screen until reauthentication succeeds. The cutoff is cleared only after a new certificate is issued.Date.now(). This applies to create workspace, rebase, commit, push, resolve, agent start and agent input. Agent Stop now needs a confirm tap (MutationButton), goes throughdispatchMutationOverSsh, and shows an ambiguous result.Changes
src-tauri/Cargo.toml,src-tauri/src/lib.rs:tauri-plugin-cliis now a desktop-only target dependency.mod cli, the plugin init, theCliExtblock andis_cli_processare#[cfg(desktop)].src-tauri/capabilities/default.json:platforms: [linux, macOS, windows].src-tauri/tauri.conf.json:plugins.deep-link.mobileregisters the custom schemetreq, hostauth, path/callback, withappLink: false.src/lib/mobile-platform.ts, andvite.config.tsnow usesenvPrefix: ["VITE_", "TAURI_ENV_"].src/hooks/useMobileRemoteConnection.ts(managed and user-managed connection, renewal, reauthentication, resume check) andsrc/hooks/useAppResume.ts(visibilitychange+online).src/lib/mobile-session.ts(persists identifiers only, with a whitelist parser),src/lib/remote-idempotency.tsandsrc/lib/auth-deep-link.ts.RemoteConnectPanelrewritten as the mobile remote flow. The pickers live in the newmobile/MobileRemotePickers.tsx.RemoteRepoScreenexportsRemoteRepoScreenStateand takesinitialScreenandonScreenChange.sshEndpointFromUserManagedmoved fromDashboard.tsxtolib/remote-endpoints.ts. There is no behaviour change on desktop.UI changes: the mobile shell layout changed: it has an account control in the header, an endpoint picker, a repository picker, a status banner and a cutoff block. Agent Stop now takes two taps. I did not run the screenshot harness.
Testing
npx tsc --noEmit: clean.npx eslint/npx oxlint/npx biome formaton the changed files: no new warnings. The remaining eslint warnings are in existingDashboard.tsxcode.npx vitest run --config vitest.unit.config.tson the remote and mobile suites (src/components/remote,src/lib/remote-*,src/components/mobile,MobileShell,Remote*,src/lib/mobile-*,auth-deep-link,managed-ssh,src/stores): 26 files, 147 tests, all passed. The new tests cover:cargo check --locked --all-targets --features tauri-test: OK.cargo clippy --locked --all-targets --all-features -- -D warnings: OK.cargo fmt --check: OK.cargo test --lib identifies_cli_process: OK.cargo check --target aarch64-linux-android. The Android target and the NDK are not installed here, and C dependencies (ring, bundled rusqlite) need the NDK's clang, so the mobile compile is still unverified.Not in this PR
portable-pty(local PTY manager) andnotify(file watcher) stay unconditional. Both are libc/inotify/kqueue crates that list Android and iOS as supported targets. If a mobile build shows they fail, they should be gated along with thecommands::*surface that uses them, which is a bigger refactor. There is no updater plugin in this app.CFBundleURLTypesintosrc-tauri/gen/fromtauri.conf.json. That only happens once the gitignored native projects exist (tauri android init/tauri ios init). HTTPS app links (App Links / Universal Links) would also needassetlinks.json/apple-app-site-associationon the web host. They are not configured here.source=desktop, since its callback is the sametreq://auth/callback. A mobile-specific source value would be a change to the web page.describeMutationOutcomepattern. The globalRemoteAmbiguousMutationDialogis not mounted on mobile.on_open_urlhandler only routestreq://agent/...links to the frontend. Whether auth callbacks reachAppStoreEffectson desktop looks worth checking separately. This PR does not change it.🤖 Generated with Claude Code
https://claude.ai/code/session_01MbYSPHBRmDYwPJpHG33Y4t
Generated by Claude Code