aiprompts/conn-arch.md
Wave Terminal's connection system is designed to provide a unified interface for running shell processes across local, SSH, and WSL environments. The architecture is built in layers, with clear separation of concerns between connection management, shell process execution, and block-level orchestration.
┌─────────────────────────────────────────────────────────────────┐
│ Block Controllers │
│ (blockcontroller/blockcontroller.go, shellcontroller.go) │
│ - Block lifecycle management │
│ - Controller registry and switching │
│ - Connection status verification │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Connection Controllers (ConnUnion) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Local │ │ SSH │ │ WSL │ │
│ │ │ │ (conncontrol │ │ (wslconn) │ │
│ │ │ │ ler) │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ - Connection lifecycle (init → connecting → connected) │
│ - WSH (Wave Shell Extensions) management │
│ - Domain socket setup for RPC communication │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Shell Process Execution │
│ (shellexec/shellexec.go) │
│ - ShellProc wrapper for running processes │
│ - PTY management │
│ - Process lifecycle (start, wait, kill) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ Low-Level Connection Implementation │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ os/exec │ │golang.org/x/ │ │ pkg/wsl │ │
│ │ │ │ crypto/ssh │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ - Local process spawning │
│ - SSH protocol implementation │
│ - WSL command execution │
└─────────────────────────────────────────────────────────────────┘
pkg/blockcontroller/)Primary Files:
blockcontroller.go - Controller registry and orchestrationshellcontroller.go - Shell/terminal controller implementationResponsibilities:
controllerRegistry)CheckConnStatus())Key Functions:
ResyncController() - Main entry point for synchronizing block state with desired controllerregisterController() - Registers a new controller, stopping any existing onegetController() - Retrieves active controller for a blockShellController Details:
Controller interfaceShellProcConnUnion:
setupAndStartShellProcess() - Sets up and starts shell processgetConnUnion() - Determines connection type and retrieves connection objectmanageRunningShellProcess() - Manages I/O for running processpkg/remote/conncontroller/)Primary File: conncontroller.go
Architecture:
clientControllerMap maintains all SSH connectionsinit → connecting → connected → (running) → disconnected/error
SSHConn.Lock)SSHConn Structure:
type SSHConn struct {
Lock *sync.Mutex
Status string // Connection state
WshEnabled *atomic.Bool // WSH availability flag
Opts *remote.SSHOpts // Connection parameters
Client *ssh.Client // Underlying SSH client
DomainSockName string // Unix socket for RPC
DomainSockListener net.Listener // Socket listener
ConnController *ssh.Session // Runs "wsh connserver"
Error string // Connection error
WshError string // WSH-specific error
WshVersion string // Installed WSH version
// ...
}
Key Responsibilities:
SSH Client Management:
golang.org/x/crypto/sshDomain Socket Setup (OpenDomainSocketListener()):
/tmp/waveterm-*.sock)WSH (Wave Shell Extensions) Management:
StartConnServer()): Runs wsh version to check installationInstallWsh()): Copies appropriate WSH binary to remoteUpdateWsh()): Updates existing WSH installationgetPermissionToInstallWsh()): Asks user for install permissionConnection Server (wsh connserver):
ConnServerCmdTemplateConnection Flow:
1. GetConn(opts) - Retrieve or create connection
2. Connect(ctx) - Initiate connection
3. CheckIfNeedsAuth() - Verify authentication needed
4. OpenDomainSocketListener() - Set up RPC channel
5. StartConnServer() - Launch wsh connserver
6. (Install/Update WSH if needed)
7. Status: Connected - Ready for shell processes
pkg/remote/sshclient.go)Responsibilities:
Authentication Methods:
createPublicKeyCallback())createPasswordCallbackPrompt())createInteractiveKbdInteractiveChallenge())Known Hosts Verification (createHostKeyCallback()):
~/.ssh/known_hosts and global known_hostsProxyJump Support:
SshProxyJumpMaxDepth = 10User Interaction:
userinput systempkg/wslconn/)Primary File: wslconn.go
Architecture:
conncontroller but for WSLclientControllerMap for WSL connectionswsl://[distro-name] (e.g., wsl://Ubuntu)WslConn Structure:
type WslConn struct {
Lock *sync.Mutex
Status string
WshEnabled *atomic.Bool
Name wsl.WslName // Distro name
Client *wsl.Distro // WSL distro interface
DomainSockName string // Uses RemoteFullDomainSocketPath
ConnController *wsl.WslCmd // Runs "wsh connserver"
// ... similar to SSHConn
}
Key Differences from SSH:
wavebase.RemoteFullDomainSocketPath)wsl.exe command-line toolConnection Flow:
1. GetWslConn(distroName) - Get/create WSL connection
2. Connect(ctx) - Start connection process
3. OpenDomainSocketListener() - Set domain socket path (no actual listener)
4. StartConnServer() - Launch wsh connserver in WSL
5. (Install/Update WSH if needed)
6. Status: Connected - Ready for shell processes
pkg/shellexec/)Primary File: shellexec.go
ShellProc Structure:
type ShellProc struct {
ConnName string // Connection identifier
Cmd ConnInterface // Actual process interface
CloseOnce *sync.Once // Ensures single close
DoneCh chan any // Signals process completion
WaitErr error // Process exit status
}
ConnInterface Implementations:
CombinedConnInterface wraps os/exec.Cmd with PTYRemoteConnInterface wraps SSH sessionWslConnInterface wraps WSL commandProcess Startup Functions:
StartLocalShellProc() - Local shell processesStartRemoteShellProc() - SSH remote shells (with WSH)StartRemoteShellProcNoWsh() - SSH remote shells (no WSH)StartWslShellProc() - WSL shells (with WSH)StartWslShellProcNoWsh() - WSL shells (no WSH)Key Features:
pkg/genconn/)Purpose: Provides abstraction layer for running commands across different connection types
Primary File: ssh-impl.go
Interface Hierarchy:
ShellClient -> ShellProcessController
SSHShellClient:
*ssh.ClientSSHProcessController for each commandSSHProcessController:
*ssh.SessionUsage Pattern:
client := genconn.MakeSSHShellClient(sshClient)
proc, _ := client.MakeProcessController(cmdSpec)
stdout, _ := proc.StdoutPipe()
proc.Start()
// Read from stdout...
proc.Wait()
pkg/util/shellutil/)Primary File: shellutil.go
Responsibilities:
Shell Detection:
DetectLocalShellPath() - Finds user's default shellGetShellTypeFromShellPath() - Identifies shell type (bash, zsh, fish, pwsh)DetectShellTypeAndVersion() - Gets shell version infoShell Integration Files:
InitCustomShellStartupFiles() - Creates Wave's shell integration.bashrc in shell/bash/.zshrc, .zprofile, etc. in shell/zsh/wave.fish in shell/fish/wavepwsh.ps1 in shell/pwsh/Environment Management:
WaveshellLocalEnvVars() - Wave-specific environment variablesUpdateCmdEnv() - Updates command environmentWSH Binary Management:
GetLocalWshBinaryPath() - Locates platform-specific WSH binaryGit Bash Detection (Windows):
FindGitBash() - Locates Git Bash installationConnection Name: "local", "local:", or "" (empty)
Workflow:
IsLocalConnName()StartLocalShellProc()os/exec.Cmd with PTYSpecial Case - Git Bash (Windows):
"local:gitbash"Connection Name: "user@host:port" (parsed by remote.ParseOpts())
Full Connection Workflow:
┌─────────────────────────────────────────────────────────────────┐
│ 1. Connection Request (from Block Controller) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 2. GetConn(opts) - Retrieve/Create SSHConn │
│ - Check global registry (clientControllerMap) │
│ - Create new SSHConn if needed │
│ - Status: "init" │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 3. conn.Connect(ctx) - Establish SSH Connection │
│ - Status: "connecting" │
│ - Read SSH config (~/.ssh/config) │
│ - Resolve ProxyJump if configured │
│ - Create SSH client auth methods: │
│ • Public key (with agent support) │
│ • Password │
│ • Keyboard-interactive │
│ - Establish SSH connection │
│ - Verify known_hosts │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 4. OpenDomainSocketListener(ctx) - Set Up RPC Channel │
│ - Create random socket path: /tmp/waveterm-[random].sock │
│ - Use ssh.Client.ListenUnix() for remote forwarding │
│ - Start RPC listener goroutine │
│ - Socket available for all subsequent operations │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 5. StartConnServer(ctx) - Launch Wave Shell Extensions │
│ - Run: "wsh version" to check installation │
│ - If not installed or outdated: │
│ a. Detect remote platform (OS/arch) │
│ b. Get user permission (if configured) │
│ c. InstallWsh() - Copy binary to remote │
│ d. Retry StartConnServer() │
│ - Run: "wsh connserver" on remote │
│ - Pass JWT token for authentication │
│ - Monitor connserver output │
│ - Wait for RPC route registration │
│ - Status: "connected" │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 6. Connection Ready - Can Start Shell Processes │
│ - SSHConn available in registry │
│ - Domain socket active for RPC │
│ - WSH connserver running │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 7. Start Shell Process (from ShellController) │
│ - setupAndStartShellProcess() │
│ - Create swap token (for shell integration) │
│ - StartRemoteShellProc() or StartRemoteShellProcNoWsh() │
│ - SSH session created for shell │
│ - PTY allocated │
│ - Shell starts with Wave integration │
└─────────────────────────────────────────────────────────────────┘
WSH (Wave Shell Extensions) Details:
What is WSH?
wsh) that runs on remote hostsWSH Components:
WSH Installation Process:
wsh versionuname -sm~/.waveterm/bin/wshWith vs Without WSH:
Connection Name: "wsl://[distro]" (e.g., "wsl://Ubuntu")
Workflow:
1. GetWslConn(distroName) - Get/create WslConn
2. conn.Connect(ctx) - Start connection
3. OpenDomainSocketListener() - Set socket path (no actual listener)
4. StartConnServer() - Launch "wsh connserver" via wsl.exe
5. Install/update WSH if needed (similar to SSH)
6. Status: "connected"
7. StartWslShellProc() - Create shell process in WSL
Key Differences from SSH:
wsl.exe command-line toolPurpose: Pass connection-specific environment variables to shell processes
Implementation: shellutil.TokenSwapEntry
Flow:
Purpose:
SSH Connection Errors:
Recovery Mechanisms:
conn.Reconnect(ctx) - Close and re-establish connectionconn.WaitForConnect(ctx) - Block until connectedShell Process Errors:
Cleanup:
ShellProc.Close() - Graceful then forceful killSSHConn.close_nolock() - Cleanup all resourcesdeleteController() - Remove from registrySource: pkg/wconfig/
Per-Connection Settings:
conn:wshenabled - Enable/disable WSHconn:wshpath - Custom WSH binary pathconn:shellpath - Custom shell pathGlobal Settings:
conn:askbeforewshinstall - Prompt before WSH installation~/.waveterm/config/settings.json~/.waveterm/config/connections.jsonSource: ~/.ssh/config
Supported Directives:
Host - Connection matchingHostName - Target hostnamePort - SSH portUser - UsernameIdentityFile - Private key pathsProxyJump - Jump host specificationUserKnownHostsFile - Known hosts fileGlobalKnownHostsFile - System known hostsAddKeysToAgent - Add keys to SSH agentLibrary: github.com/kevinburke/ssh_config
SSHConn/WslConn:
conn.Lock.Lock()
defer conn.Lock.Unlock()
// ... modify connection state
Atomic Flags:
conn.WshEnabled.Load() // Read WSH enabled status
conn.WshEnabled.Store(v) // Update atomically
Controller Registry:
registryLock.RLock() // Read lock for lookups
registryLock.Lock() // Write lock for modifications
ShellProc Completion:
sp.CloseOnce.Do(func() { // Ensure single execution
sp.WaitErr = waitErr
close(sp.DoneCh) // Signal completion
})
Published via: pkg/wps/ (Wave Publish/Subscribe)
Event Types:
Event_ConnChange - Connection status changedEvent_ControllerStatus - Block controller status updateEvent_BlockFile - Block file operation (terminal output)Example:
wps.Broker.Publish(wps.WaveEvent{
Event: wps.Event_ConnChange,
Scopes: []string{fmt.Sprintf("connection:%s", connName)},
Data: connStatus,
})
Frontend Integration:
| Component | Responsibilities |
|---|---|
| blockcontroller/ | Block lifecycle, controller registry, connection coordination |
| shellcontroller | Shell process management, ConnUnion abstraction, I/O handling |
| conncontroller/ | SSH connection lifecycle, WSH management, domain socket setup |
| wslconn/ | WSL connection lifecycle, parallel to SSH but for WSL |
| sshclient.go | Low-level SSH: auth, known_hosts, ProxyJump |
| shellexec/ | Process execution abstraction, PTY management |
| genconn/ | Generic command execution interface |
| shellutil/ | Shell detection, integration files, environment setup |
Layered Architecture: Clear separation between block management, connection management, and process execution
Connection Abstraction: ConnUnion pattern allows uniform handling of Local/SSH/WSL
WSH Optional: System works with and without Wave Shell Extensions, degrading gracefully
Thread Safety: Defensive locking, atomic flags, singleton patterns prevent race conditions
Error Recovery: Multiple retry mechanisms, fallback modes, user prompts for resolution
Configuration Hierarchy: Global → Connection-Specific → Runtime overrides
Event-Driven Updates: Real-time status updates via pub/sub system
User Interaction: Non-blocking prompts for passwords, confirmations, installations
This architecture provides a robust foundation for Wave Terminal's multi-environment shell capabilities, with clear extension points for adding new connection types or capabilities.