parasite

Client Architecture

Module Map

src/
├── main.rs                 Entry point, terminal setup
├── tui.rs                  Terminal initialization/restore
├── event.rs                Crossterm event loop → EventHandler
├── action.rs               Action enum (73 variants)
├── connection.rs           AgentConnection (TLS reader/writer)
├── credentials.rs          Credential storage (keychain)
├── ssh.rs                  SSH helpers (sshpass, known_hosts)
├── connect.rs              Agent connection flow (SSH + start agent)
├── install.rs              Remote agent installation
├── shutdown.rs             Remote agent shutdown
├── code.rs                 VS Code Remote-SSH integration
├── local_pty.rs            Local PTY for editor/code processes
│
├── app/                    Application state & dispatch
│   ├── mod.rs              App struct, run(), draw(), handle_event()
│   ├── session.rs          AgentSession, SubSession, SshCredentials
│   ├── cleanup.rs          Connection/session cleanup
│   ├── discovery.rs        mDNS agent discovery
│   └── dispatch/
│       ├── mod.rs           dispatch_action() router
│       ├── connection.rs    Connect/disconnect/reconnect
│       ├── shell.rs         Shell open/input/resize
│       ├── editor.rs        Editor sync lifecycle
│       ├── code.rs          VS Code lifecycle
│       ├── file_browser.rs  File browser actions
│       ├── operations.rs    Install/SSH connect/shutdown
│       └── network.rs       Network message handling
│
├── editor/                 Mutagen editor sync
│   ├── mod.rs              Re-exports
│   ├── workspace.rs        Hash computation, paths
│   ├── ssh_config.rs       SSH config injection/removal
│   ├── control_master.rs   SSH ControlMaster management
│   └── sync.rs             Mutagen sync lifecycle
│
└── components/             UI components
    ├── mod.rs              Component trait
    ├── shell.rs            VT100 terminal emulator
    ├── status_bar.rs       Bottom status bar
    ├── command_palette.rs  Command input modal
    ├── file_browser.rs     File/directory browser
    └── dashboard/
        ├── mod.rs           DashboardView struct
        ├── types.rs         SessionInfo, SystemInfoData
        ├── render_agents.rs Agent list rendering
        ├── render_panels.rs Operation panels (install/connect/etc.)
        ├── render_layout.rs Workspace, session, system info layout
        ├── key_handling.rs  Keyboard event dispatch
        └── action_handling.rs Action processing

Views

The client has four views:

View Purpose
Workspace Agent discovery, connection management
Dashboard Active session info, system stats, sub-sessions
Shell Full-screen terminal emulator
FileBrowser Remote directory navigation

Action Flow

  1. Input: Crossterm key events → EventHandler
  2. Routing: App::handle_event() → active component’s handle_key_event()
  3. Action: Component returns an Action variant
  4. Dispatch: dispatch_action() matches the action to a handler module
  5. State: Handler modifies App state, may send network messages
  6. Render: App::draw() calls Component::render() on the active view

Action Categories

Component Trait

pub trait Component {
    fn handle_key_event(&mut self, key: KeyEvent) -> Option<Action>;
    fn handle_action(&mut self, action: &Action);
    fn render(&mut self, frame: &mut Frame, area: Rect);
}

Components are passive renderers. They don’t own connections or spawn tasks — that’s the App’s responsibility via the dispatch system.

Session Model

struct AgentSession {
    connection: Arc<AgentConnection>,   // Shared TLS connection
    sub_sessions: Vec<SubSession>,      // Shells, editors, code sessions
    ssh_creds: Option<SshCredentials>,  // For editor/code SSH operations
}

struct SubSession {
    id: SessionId,          // UUID
    shell: ShellView,       // VT100 terminal state
    label: String,          // Display name
    sync_name: Option<String>,  // Mutagen sync name (editors)
    sync_hash: Option<String>,  // SSH config hash (code)
    local_pty: Option<LocalPty>, // Local PTY for editor/code processes
}