Files
VuongKB/aipa-plans/docs/ssh-tui-architecture-spec.md

23 KiB

created, id, keywords, links, modified, summary, tags, title
created id keywords links modified summary tags title
2026-04-15T05:41:53+00:00 e8450555-eab3-4d48-a654-b5f1708f0517
SSH-TUI
AIPA
architecture
Textual
asyncssh
MCP
Linus
TUI
terminal
AI copilot
Pydantic
aipa-ssh-tui
graceful degradation
command detection
PTY
2026-04-15T05:41:53+00:00 Full architecture specification for the AIPA SSH-TUI application: a Textual-based terminal SSH client with integrated AI copilot, covering component design, data flows, project structure, configuration, error handling, testing, and security model.
aipa-plans
ssh-tui
architecture
specification
textual
asyncssh
linus
AIPA SSH-TUI — Architecture Specification

AIPA SSH-TUI — Architecture Specification

Status: Draft — Pending review
Project: AIPA SSH-TUI
Version: 0.1
Last updated: 2025-07-10
Authors: Linus (Systems Architect), Cara (UI/UX Design)

1. Overview

AIPA SSH-TUI is a terminal-based SSH client with an integrated AI copilot. It wraps SSH sessions in a Textual TUI and connects to the AIPA MCP server to provide real-time AI assistance for system administration tasks. The AI (Linus) monitors terminal output, maintains a plan of action, suggests commands, and responds to natural language queries — but never executes commands without explicit user approval.

Goals

  • SSH client first — fully functional SSH terminal even without AI. Graceful degradation if AI is unavailable.
  • AI copilot second — enhances the SSH experience with context-aware suggestions, intent tracking, and natural language interaction.
  • Safety — the user is always in control. Every AI-suggested command requires explicit approval. Commands are editable before execution.
  • Auditability — AI-suggested commands are visually distinct in the terminal so the user always knows who issued what.

Non-Goals (v1)

  • Multiple concurrent SSH sessions
  • Remote file inspection via AI
  • AI-initiated push notifications
  • Native SSH key management
  • Port forwarding / tunneling UI
  • SSH jump host / proxy support (v1 uses local SSH config or ProxyCommand)

2. Technology Stack

Component Technology Rationale
TUI Framework Textual (Python) Async-native, rich widget system, mature, active development
SSH Library asyncssh Async, native coroutines, plays well with Textual's event loop
MCP Client mcp Python SDK Official MCP client library
Configuration TOML Standard Python config format, human-readable
Data Models Pydantic Validation, serialization, type safety
Python >=3.11 Required for asyncssh and modern type syntax

3. TUI Layout

Designed by Cara (UI/UX). The layout follows sysadmin conventions — minimal chrome, single-line borders, dense information display.

┌─────────────────────────────── Status Bar ────────────────────────────────────┐
│  host: prod-web-03   user: deploy   ● connected   AI: ● online               │
├──────────────────────────────────┬────────────────────────────────────────────┤
│                                  │  ┌── Intent ──────────────────────────┐   │
│                                  │  │ ✓ Check overall disk usage         │   │
│   SSH Session                    │  │ ✓ Identify largest directories      │   │
│   (main interaction surface)     │  │ ▸ List largest files in /var/log    │   │
│                                  │  │ ○ Determine cleanup strategy       │   │
│                                  │  │ ○ Free disk space safely           │   │
│   root@prod:~# df -h             │  │                                    │   │
│   Filesystem  Size  Used  ...    │  │ ── Confidence: 85%                 │   │
│                                  │  └────────────────────────────────────┘   │
│                                  │                                            │
│                                  │  ┌── Suggested Commands ────────────┐    │
│                                  │  │  1. [low]  ls -lhS /var/log |... │    │
│                                  │  │  2. [low]  journalctl --disk-use  │    │
│                                  │  │  3. [med]  apt-get clean && ...   │    │
│                                  │  │                                  │    │
│                                  │  │  [Enter] Send   [e] Edit   [d] Del│    │
│                                  │  └────────────────────────────────────┘   │
├──────────────────────────────────┴────────────────────────────────────────────┤
│  ┌── Chat to AI ──────────────────────────────────────────────────────────┐   │
│  │  > /var is full, what's eating the space?                             │   │
│  │  AI: Looks like /var/log is the culprit at 4.2G. Queued commands to   │   │
│  │      identify the largest files.                                      │   │
│  │  > go ahead with #1                                                   │   │
│  └───────────────────────────────────────────────────────────────────────┘   │
│  > type here...                                                             │
└──────────────────────────────────────────────────────────────────────────────┘

Layout Proportions

  • Top row: 75% of terminal height
    • SSH Session: ~60% width (left)
    • Intent + Suggested Commands: ~40% width (right), stacked vertically with Intent taking ~55% and Commands taking ~45%
  • Bottom row: 25% of terminal height (minimum 6 lines, resizable)
    • Chat to AI: full width

Key UX Rules (from Cara)

  1. Escape always returns focus to SSH — panic button for when something goes wrong. The sysadmin needs Ctrl+C in the shell immediately.
  2. Focus cycling: Tab cycles SSH → Commands → Chat (skips Intent, it's read-only). Ctrl+J/Ctrl+K for up/down cycling.
  3. Intent is a living checklist — uses ✓ (done), ▸ (current), ○ (pending). Not overwritten each turn; steps are added/updated incrementally.
  4. Suggested commands are editable — press e to inline-edit before sending. Enter sends the selected command.
  5. AI-sent commands are visually distinct — in the SSH panel, commands from the suggestions are prefixed with → and dimmed.
  6. Scroll isolation — each panel scrolls only when focused. Unfocused panels show a ▼ N new lines indicator.
  7. Graceful degradation — if AI drops, Intent shows "AI offline", Commands show "(commands may be stale)", Chat queues messages, but SSH stays fully functional.
  8. Minimal chrome — single-line borders, single-character labels. This lives next to vim and htop — it should look like it belongs.

Key Bindings

Key Context Action
Escape Anywhere Return focus to SSH session
Tab Anywhere Cycle focus: SSH → Commands → Chat → SSH
Ctrl+J Anywhere Focus previous panel
Ctrl+K Anywhere Focus next panel
Enter SSH Session Send input (passthrough)
Enter Commands Panel Send selected command
Enter Chat Panel Send message to AI
e Commands Panel Inline-edit selected command
d Commands Panel Delete selected command
↑/↓ Commands Panel Select command
Ctrl+L Anywhere Force redraw

4. System Architecture

4.1 Component Diagram

┌─────────────────────────────────────────────────────────────────┐
│                        SSH-TUI Application                      │
│                                                                  │
│  ┌──────────┐   ┌──────────────┐   ┌────────────────────────┐   │
│  │  TUI      │   │  Session     │   │  MCP Client             │   │
│  │  Layer    │◄──┤  Manager     │   │                         │   │
│  │          │   │              │   │  ┌───────────────────┐  │   │
│  │ ┌──────┐ │   │  ┌────────┐  │   │  │ Connection Mgmt   │  │   │
│  │ │SSH   │ │   │  │ asyncssh│  │   │  │ Message Queue     │  │   │
│  │ │Panel │ │   │  │ session │  │   │  │ Debounce Logic    │  │   │
│  │ └──────┘ │   │  └────────┘  │   │  └───────────────────┘  │   │
│  │ ┌──────┐ │   │  ┌────────┐  │   │                         │   │
│  │ │Intent│ │   │  │Terminal│  │   └───────────┬─────────────┘   │
│  │ │Panel │ │   │  │Buffer  │  │               │                 │
│  │ └──────┘ │   │  └────────┘  │               │                 │
│  │ ┌──────┐ │   │  ┌────────┐  │               │                 │
│  │ │Sugg. │ │   │  │Command │  │               │                 │
│  │ │Cmds  │ │   │  │Log     │  │               │                 │
│  │ └──────┘ │   │  └────────┘  │               │                 │
│  │ ┌──────┐ │   └──────────────┘               │                 │
│  │ │Chat  │ │                                   │                 │
│  │ │Panel │ │                                   │                 │
│  │ └──────┘ │                                   │                 │
│  └──────────┘                                   │                 │
└─────────────────────────────────────────────────┼─────────────────┘
                                                  │ MCP Protocol
                                                  │
┌─────────────────────────────────────────────────┼─────────────────┐
│                        AIPA MCP Server           │                 │
│                                                  ▼                 │
│  ┌──────────┐   ┌──────────────┐   ┌────────────────────────┐     │
│  │  Agent   │   │  Tool        │   │  Linus Agent            │     │
│  │  Router  │──┤  Registry    │──┤  ┌───────────────────┐  │     │
│  │          │   │              │   │  │ linus_chat        │  │     │
│  └──────────┘   └──────────────┘   │  │ linus_term_update │  │     │
│                                    │  │ linus_cmd_exec    │  │     │
│                                    │  │ linus_session_st  │  │     │
│                                    │  └───────────────────┘  │     │
│                                    └────────────────────────┘     │
└───────────────────────────────────────────────────────────────────┘

4.2 Component Responsibilities

TUI Layer (app.py, panels/)

  • Manages the Textual application lifecycle
  • Renders all four panels and the status bar
  • Handles keyboard input and focus management
  • Dispatches user actions to the Session Manager or MCP Client

Session Manager (ssh_session.py)

  • Manages the asyncssh connection lifecycle (connect, authenticate, disconnect)
  • Creates and manages the PTY (pseudo-terminal) on the remote host
  • Captures all terminal output into a ring buffer (configurable, default 2000 lines)
  • Tracks the current working directory (by parsing prompts, best-effort)
  • Provides a terminal tail snapshot (last N lines) for MCP context
  • Tracks command execution: command text, output, exit code, timestamp
  • Injects commands into the PTY (from user typing or from sending suggestions)

MCP Client (mcp_client.py)

  • Connects to the AIPA MCP server (configurable URL)
  • Manages the message queue for outgoing messages
  • Implements debouncing for terminal_update messages (1 second)
  • Queues chat messages when offline, sends on reconnect
  • Discards terminal_update messages when offline (ephemeral)
  • Parses Linus's structured responses and dispatches updates to the appropriate TUI panels
  • Handles connection errors and implements retry with exponential backoff

Data Models (models.py)

  • Pydantic models for all message types (see MCP Interface Contract)
  • Session state tracking
  • Command log entries

4.3 Data Flow

Flow 1: User sends a chat message

User types in Chat Panel → TUI dispatches to MCP Client
  → MCP Client calls linus_chat(message, context)
    → MCP Server routes to Linus
      → Linus processes and returns { chat_response, intent_update, suggested_commands }
    → MCP Client receives response
      → Updates Chat Panel with chat_response.message
      → Updates Intent Panel with intent_update.steps
      → Updates Commands Panel with suggested_commands.commands

Flow 2: User types in SSH session

User types in SSH Panel → TUI passes keystrokes to Session Manager
  → Session Manager writes to asyncssh PTY
    → Remote host processes and produces output
      → Session Manager captures output into ring buffer
        → Session Manager signals MCP Client (terminal output changed)
          → MCP Client debounces (1 second)
            → MCP Client calls linus_terminal_update(new_output, terminal_tail, context)
              → Linus may return { intent_update, suggested_commands } or null
                → If updates: TUI panels refreshed

Flow 3: User sends a suggested command

User selects command + presses Enter → TUI reads selected command
  → TUI sends command to Session Manager
    → Session Manager writes command + newline to asyncssh PTY
      → Session Manager logs: { command, source: "suggested", suggestion_id, was_edited }
      → Output flows back (see Flow 2)
      → When command completes (prompt detected):
          → Session Manager sends to MCP Client
            → MCP Client calls linus_command_executed(command, output, exit_code, context)
              → Linus returns updated { chat_response, intent_update, suggested_commands, clear_suggestions }

4.4 Terminal Output Detection

Detecting when a command has finished executing (to capture exit code and complete output) is non-trivial in a PTY. Strategy:

  1. Prompt detection (primary): Parse the shell prompt to detect when a new prompt appears after a command. Configurable prompt pattern (default: $ or # at line start).
  2. Exit code capture: Before each user/suggested command, inject \n echo "EXIT:$?" after the command to capture the exit code. Parse this from the output stream.
  3. Timeout fallback: If no prompt is detected within a configurable timeout (default: 30 seconds), consider the command "still running" and send what output is available.

5. Project Structure

aipa-ssh-tui/
├── pyproject.toml              # Project metadata, dependencies, entry point
├── README.md                   # Quick start, usage, screenshots
├── config/
│   └── default.toml            # Default configuration
├── src/
│   └── aipa_ssh_tui/
│       ├── __init__.py
│       ├── app.py              # Textual App class, layout, CSS
│       ├── ssh_session.py      # asyncssh connection, PTY, output capture
│       ├── mcp_client.py       # MCP client, message queue, debouncing
│       ├── command_detector.py # Prompt detection, exit code parsing
│       ├── models.py           # Pydantic models for all message types
│       ├── config.py           # Configuration loading (TOML)
│       └── panels/
│           ├── __init__.py
│           ├── session.py      # SSH session panel (RichLog or similar)
│           ├── intent.py       # Intent checklist panel
│           ├── suggestions.py  # Suggested commands panel + send/edit
│           ├── chat.py         # Chat to AI panel
│           └── status_bar.py   # Top status bar
└── tests/
    ├── conftest.py
    ├── test_models.py
    ├── test_ssh_session.py
    ├── test_mcp_client.py
    ├── test_command_detector.py
    └── test_app.py

6. Configuration

# config/default.toml

[ssh]
# Default SSH settings (can be overridden via CLI args)
host = ""
port = 22
user = ""
# Path to SSH config file (uses ~/.ssh/config by default)
config_file = ""

[mcp]
# AIPA MCP server connection
server_url = "http://localhost:8000"
# Retry settings
retry_base_delay = 1.0        # seconds
retry_max_delay = 30.0        # seconds
retry_max_attempts = 5

[terminal]
# Terminal buffer settings
ring_buffer_size = 2000        # lines to keep in memory
tail_lines = 100               # lines sent to AI as context
# Prompt detection (regex)
prompt_pattern = "^[^@]*@[^:]*:[^#]*[#$] "

[mcp_debounce]
# Debounce settings for terminal updates
terminal_update_interval = 1.0  # seconds
command_complete_timeout = 30.0  # seconds before considering a command "still running"

[ui]
# TUI appearance
theme = "nord"                  # Textual theme
ai_command_prefix = "→ "       # prefix for AI-sent commands in SSH panel
show_line_numbers = false       # line numbers in SSH panel

7. CLI Interface

aipa-ssh-tui [user@]hostname [-p port] [-i identity_file] [--config config.toml]

Examples:
  aipa-ssh-tui root@10.250.50.70
  aipa-ssh-tui deploy@prod-web-03 -p 2222 -i ~/.ssh/id_prod
  aipa-ssh-tui web-01 --config ~/my-ssh-tui.toml

If no MCP server is configured or reachable, the TUI operates as a plain SSH client with the Intent and Commands panels hidden or showing "AI not configured".

8. Error Handling

Scenario Behavior
SSH connection fails Show error in SSH panel. Offer retry. Chat/Intent/Commands non-functional.
SSH connection drops mid-session Show disconnection in status bar and SSH panel. Attempt reconnect (configurable). Queue chat messages.
MCP server unreachable SSH works normally. Intent shows "AI offline". Commands show "(stale)". Chat queues messages. Auto-retry MCP connection.
MCP server returns malformed response Log error. Skip update for malformed component. Show warning in status bar.
asyncssh authentication fails Show error. Do not retry automatically. Prompt for different credentials or key.
Terminal resize Textual handles natively. SSH PTY resized via window-change request.

9. Dependencies

# pyproject.toml (core dependencies)
[project]
dependencies = [
    "textual>=0.47.0",
    "asyncssh>=2.14.0",
    "pydantic>=2.0.0",
    "tomli>=2.0.0; python_version < '3.11'",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-asyncio>=0.23.0",
    "ruff>=0.4.0",
    "mypy>=1.10.0",
]

10. Testing Strategy

Layer What to test Tools
Data models Pydantic validation, serialization, defaults pytest
Command detector Prompt regex matching, exit code parsing pytest
SSH session Connect/disconnect, PTY I/O, output capture pytest-asyncio + mock SSH server
MCP client Message queueing, debouncing, offline handling pytest + mock MCP server
TUI panels Rendering, focus management, key bindings pytest + Textual's test driver

11. Security Model

  1. No credential exposure — SSH passwords and keys never appear in MCP messages. The MCP client only sends ssh_host and ssh_user in the context object.
  2. User approval required — every AI-suggested command requires explicit Enter to send. No auto-execution.
  3. Command editing — the user can modify any suggested command before sending.
  4. Risk labels — every command carries a risk level (low, medium, high). High-risk commands are visually highlighted.
  5. Audit trail — AI-suggested commands are visually distinct in the SSH panel (prefixed with →).
  6. Command logging — all executed commands are logged locally with source, timestamp, and whether they were edited.

12. Future Scope (v2+)

  • Multiple concurrent SSH sessions (tabs)
  • SSH jump host / ProxyCommand support
  • AI-driven file inspection (Linus requests file contents from remote host)
  • Push notifications from Linus (e.g., "I detected a kernel OOM in dmesg")
  • Bash history integration for smarter suggestions
  • Saved session profiles (host + common tasks)
  • Shared intent/state between sessions

References