Files
galaxy/specs/APP-4217/TECH.md
T

9.9 KiB

TECH.md — Remote Server Reliability Telemetry (APP-4217)

Context

The SSH remote server feature (gated behind FeatureFlag::SshRemoteServer) has no telemetry. Before releasing beyond dogfood we need client-side reliability metrics covering the full lifecycle: binary check → install → connect → initialize → steady-state operation → disconnection.

Auth state is not yet passed to the remote server, so all tracking is client-side via the existing TelemetryEvent infrastructure.

Relevant code

  • crates/remote_server/src/manager.rsRemoteServerManager singleton; owns session lifecycle, emits RemoteServerManagerEvent
  • crates/remote_server/src/client/mod.rsRemoteServerClient; protocol I/O, reader/writer tasks, request/response correlation
  • crates/remote_server/src/transport.rsRemoteTransport trait (check_binary, install_binary, connect)
  • app/src/remote_server/ssh_transport.rsSshTransport implementation
  • app/src/terminal/writeable_pty/remote_server_controller.rsRemoteServerController; per-pane orchestrator for the SSH init flow
  • app/src/terminal/view.rs (4170-4217)TerminalView subscription to RemoteServerManagerEvent; converts manager events into terminal events and UI
  • app/src/server/telemetry/events.rsTelemetryEvent enum; each variant needs definition, name(), description(), payload(), contains_ugc(), enablement_state()

Current lifecycle

stateDiagram-v2
    [*] --> CheckBinary: SshInitShell
    CheckBinary --> Connect: found
    CheckBinary --> ShowChoiceBlock: not found (AlwaysAsk)
    CheckBinary --> Install: not found (AlwaysAllow)
    CheckBinary --> Skipped: not found (AlwaysSkip)
    CheckBinary --> Skipped: check error
    ShowChoiceBlock --> Install: user clicks Install
    ShowChoiceBlock --> Skipped: user clicks Skip
    Install --> Connect: success
    Install --> Skipped: failure
    Connect --> Initialize: transport.connect() ok
    Connect --> Skipped: transport.connect() error
    Initialize --> Connected: handshake ok
    Initialize --> Skipped: handshake error
    Connected --> Disconnected: reader EOF / error

Every Skipped path falls back to ControlMaster-based command execution. Today none of these transitions emit telemetry.

Reliability Metrics

Seven telemetry events covering every stage of the lifecycle:

1. Binary check outcome — RemoteServerBinaryCheck

Tracks whether the remote binary was found, not found, or the check itself failed (SSH timeout, unreachable host). This is the entry point for every remote server session.

field type description
found bool true if binary exists and is executable
error Option<String> set when check_binary returns Err

Signals: check failure rate (SSH-level issues), install-needed rate.

2. Installation outcome — RemoteServerInstallation

Tracks whether the install script succeeded or failed.

field type description
succeeded bool
error Option<String> set on failure

Signals: install success rate, common failure reasons.

3. Initialization outcome — RemoteServerInitialization

Tracks the two-phase connection flow: transport.connect() (SSH/process spawn) and client.initialize() (protocol handshake). Distinguishing the phase helps diagnose whether failures are transport-level or protocol-level.

field type description
succeeded bool
phase &'static str "connect" or "initialize"
error Option<String> set on failure

Signals: connection success rate, breakdown of failure by phase.

4. Session disconnection — RemoteServerDisconnection

Tracks when an established connection drops (reader EOF or fatal error). This is the most important stability signal — it tells us how reliable connections are after successful setup.

No fields.

Signals: disconnection rate, connection stability.

5. Client request error — RemoteServerClientRequestError

Tracks when a request to the remote server fails (timeout, disconnect, unexpected response, server error).

field type description
operation &'static str e.g. "navigate_to_directory", "run_command"
error_type &'static str e.g. "timeout", "disconnected", "server_error"

Signals: per-operation failure rate, error distribution.

6. Server message decoding error — RemoteServerMessageDecodingError

Tracks when the reader task receives a malformed server message with no parseable request_id (messages with a request_id surface as ClientError::Protocol to the caller and are captured by metric 5).

No fields.

Signals: protocol-level corruption or version mismatch.

7. End-to-end setup duration — RemoteServerSetupDuration

Tracks wall-clock time from check_binary start to SessionConnected. Users see a shimmer loading footer during this window; tracking duration flags when setup is too slow.

field type description
duration_ms u64 elapsed time
installed_binary bool whether install was needed (affects expected duration)

Signals: setup latency, install vs no-install performance.

Proposed Changes

1. TelemetryEvent variants (app/src/server/telemetry/events.rs)

Add seven new variants to the TelemetryEvent enum, all gated behind EnablementState::Flag(FeatureFlag::SshRemoteServer).

Each variant needs entries in:

  • name() (discriminant → human-readable name)
  • description() (discriminant → doc string)
  • payload() (variant → json!({...}))
  • contains_ugc() (all return false)
  • enablement_state() (all → Flag(FeatureFlag::SshRemoteServer))

2. Propagate decode errors from RemoteServerClient

File: crates/remote_server/src/client/mod.rs

  • Add ClientEvent::MessageDecodingError variant.
  • In reader_task, on ProtocolError::Decode(_, None) (no parseable request_id), send the new event through the event channel alongside the existing log::warn!.

3. New manager events

File: crates/remote_server/src/manager.rs

  • Add RemoteServerManagerEvent::ClientRequestFailed { session_id, operation, error }
  • Add RemoteServerManagerEvent::ServerMessageDecodingError { session_id }
  • Emit ClientRequestFailed at existing log::error! sites in navigate_to_directory and load_repo_metadata_directory.
  • Forward ClientEvent::MessageDecodingError in forward_client_event.

4. Propagate failure phase in SessionConnectionFailed

File: crates/remote_server/src/manager.rs

Add phase: &'static str field to RemoteServerManagerEvent::SessionConnectionFailed ("connect" or "initialize"), set at the two Err arms in connect_session (lines 359 and 342). Update the single downstream consumer in terminal/view.rs.

5. Track telemetry in TerminalView subscription

File: app/src/terminal/view.rs (4170-4217)

Expand the existing RemoteServerManagerEvent match arms to add send_telemetry_from_ctx! calls:

  • BinaryCheckComplete { Ok(true) }RemoteServerBinaryCheck { found: true, error: None }
  • BinaryCheckComplete { Ok(false) }RemoteServerBinaryCheck { found: false, error: None }
  • BinaryCheckComplete { Err(e) }RemoteServerBinaryCheck { found: false, error: Some(e) }
  • BinaryInstallComplete { Ok(()) }RemoteServerInstallation { succeeded: true, error: None }
  • BinaryInstallComplete { Err(e) }RemoteServerInstallation { succeeded: false, error: Some(e) }
  • SessionConnectedRemoteServerInitialization { succeeded: true, phase: "initialize", error: None }
  • SessionConnectionFailed { phase }RemoteServerInitialization { succeeded: false, phase, error: None }
  • SessionDisconnectedRemoteServerDisconnection
  • ClientRequestFailedRemoteServerClientRequestError { operation, error_type }
  • ServerMessageDecodingErrorRemoteServerMessageDecodingError

6. Track setup duration in RemoteServerController

File: app/src/terminal/writeable_pty/remote_server_controller.rs

  • Add setup_start: Option<Instant> and did_install: bool to SshInitState or as fields on the controller.
  • Record Instant::now() at the start of on_ssh_init_shell_requested.
  • Set did_install = true when entering the install path.
  • Subscribe to RemoteServerManagerEvent::SessionConnected (new subscription). On match, compute duration_ms and emit RemoteServerSetupDuration.

Testing and Validation

  1. Unit tests for telemetry payload correctness: verify each new TelemetryEvent variant produces the expected name(), payload(), and enablement_state(). Follow the pattern of existing telemetry tests.

  2. Manual validation on dogfood: SSH to a remote host and verify each metric fires by checking the Rudderstack live event stream (use the telemetry stream workflow). Scenarios to exercise:

    • Clean install (binary not present) — expect BinaryCheck(not found), Installation(success), Initialization(success), SetupDuration.
    • Binary already present — expect BinaryCheck(found), Initialization(success), SetupDuration (shorter).
    • Kill the remote server process mid-session — expect Disconnection.
    • SSH to an unreachable host — expect BinaryCheck(error).
  3. Compile check: run cargo check --workspace to confirm the new variants are exhaustively handled in all match arms (name(), payload(), contains_ugc(), enablement_state(), description()).

Parallelism

Steps 1 (telemetry variants) and 2-3 (client/manager event plumbing) touch independent files and can be worked on in parallel. Steps 4-6 depend on both.

Follow-ups

  • Server-side reliability telemetry once auth state is forwarded.
  • Per-request latency tracking (p50/p99 for navigate_to_directory, run_command, etc.).
  • Reconnect success/failure tracking once client-side reconnect is implemented (APP-4068 follow-up).