CLI & TUI · TUI guide
Terminal UI guide
A full-screen, keyboard-driven UI for working with SecVF when you don't want (or can't reach) the macOS GUI. Written in Python on the Textual framework; talks to the same backend through secvf-cli.
On this page
When to use the TUI
The macOS GUI is the canonical interface — every feature lives there first. Use the TUI when:
- You're SSH'd into the host Mac and don't want to forward AppKit windows.
- You want everything on one screen at a glance — VM table + switch stats + capture log + USB devices in one pane each, no window juggling.
- You're scripting against the CLI and want a quick visual to see what your scripts did.
- You prefer keyboard-driven workflows. The TUI is fully navigable without a mouse.
The TUI is feature-complete for day-to-day work but doesn't expose every advanced option — for that, drop down to secvf-cli or the GUI.
Install
Prerequisites
- Python 3.10 or newer (
brew install python@3.12if you don't have it) - The Swift CLI built and on PATH (see CLI install)
Install the Python package
cd SecVF/cli/tui
pip install -e .
# or with a venv:
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Dependencies are minimal — Textual 0.47+ and Rich 13.7+. The pyproject.toml declares everything; no native compilation.
Launching
Preferred path is via the Swift CLI:
secvf tui
The Swift launcher finds Python, finds the TUI module, passes through its own absolute path so the TUI knows which CLI to call back into. This handles the awkward case where you have multiple SecVF installs side by side.
Direct Python invocation also works:
# with the package installed
secvf-tui
# without install, from source
cd SecVF/cli/tui
python3 -m secvf_tui.app --cli-path /path/to/secvf-cli
Keyboard shortcuts
| Key | Action |
|---|---|
| Tab / Shift+Tab | Switch between top-level tabs (VMs / Network / Capture / USB). |
| r | Refresh the current tab. |
| s | Start the selected VM. |
| x | Stop the selected VM. |
| n | Create a new VM (opens form). |
| d | Delete the selected VM (with confirmation). |
| ↑ / ↓ | Move row selection. |
| Enter | Open detail view for the selected item. |
| / | Focus the filter input on the current tab. |
| q / Ctrl+C | Quit. |
| ? | Show keyboard shortcut overlay. |
Screens
VMs tab
Tabular list of all VMs with state, OS, CPU, RAM, network mode, and last-started time. Filter input narrows the list by name or distro. Detail view (Enter) shows the full metadata, a live "uptime" counter, and quick-action buttons (Start, Stop, SSH, Show window, Delete).
┌─ VMs ──────────────────────────────────────────────────────────────────┐
│ Filter: kali_ │
│ │
│ STATE NAME OS CPU RAM NET UPTIME │
│ ● kali-router Linux 2 2 GB virtual 1h 32m │
│ ○ ubuntu-malware Linux 2 4 GB virtual -- │
│ ○ fedora-analysis Linux 4 8 GB nat -- │
│ │
│ [s] Start [x] Stop [n] New [d] Delete [Enter] Details │
└─────────────────────────────────────────────────────────────────────────┘
Network tab
Three panes:
- Switch status — running/stopped, port count, MACs learned, uptime. Updates every second.
- Port table — every connected VM with RX/TX byte counters.
- MAC table — every learned MAC, mapped to port and VM, with age.
Capture tab
The capture screen has two views:
- Status — current state (capturing/stopped), filter in effect, packet count, duration. Start/Stop buttons.
- Live — scrolling table of packets, just like
secvf capture livebut in a pane with selection support. Highlight a packet → press Enter to see decoded layers.
USB tab
Physical and virtual USB devices, each with a status (Available / Mounted to <VM>). Buttons for Mount, Eject, Create Virtual.
Architecture (Python ↔ Swift CLI)
The TUI is a thin presentation layer over secvf-cli. It never talks to SecVF.app directly.
┌────────────────────────────┐
│ Textual UI │ ← secvf_tui/app.py + screens/ + widgets/
│ │
│ ┌───────────────────────┐ │
│ │ Controller (Python) │ │ ← secvf_controller/controller.py
│ │ - subprocess.run() │ │
│ │ - parse --json │ │
│ │ - map to models │ │
│ └───────┬───────────────┘ │
└──────────┼──────────────────┘
│
▼ argv + stdin/stdout
┌────────────────────────────┐
│ secvf-cli (Swift) │ ← invoked with --json on every call
│ - VMManagerBridge │
│ - SwitchManagerBridge │
│ - CaptureManagerBridge │
│ - USBManagerBridge │
└────────────────────────────┘
│
▼ DistributedNotificationCenter / filesystem
┌────────────────────────────┐
│ SecVF.app │ ← the GUI process holds the actual state
└────────────────────────────┘
Every action the TUI takes is a CLI subprocess call. The controller layer (secvf_controller) wraps secvf-cli invocations, parses the --json output, and presents typed model objects (VM, SwitchStats, USBDevice) to the Textual app.
Practical implications:
- The TUI works headlessly (over SSH) as long as the CLI does.
- Anything the CLI can do, the TUI can do — adding a new feature usually means adding a Textual screen, not a new RPC mechanism.
- You can run the CLI and TUI side by side; both read from the same source of truth.
Running from source
Useful when you're modifying the TUI itself:
cd SecVF/cli/tui
# Run without install
python3 -m secvf_tui.app --cli-path /path/to/secvf-cli
# Optional dev deps
pip install -e ".[dev]"
ruff check secvf_tui secvf_controller
mypy secvf_tui secvf_controller
pytest # the test suite (light)
The Textual devtools (textual console) are great for debugging — keep a separate terminal open with textual console and the TUI emits debug events you can inspect.
Troubleshooting launch issues
| Symptom | Likely cause | Fix |
|---|---|---|
| "Python 3.10+ not found" | System Python is 2.x; no python3 on PATH | brew install python@3.12 |
| "TUI module not found" | Package not installed and CLI can't find source layout | cd SecVF/cli/tui && pip install -e . |
| Blank screen, only border | Terminal doesn't support truecolor | Use a modern terminal (iTerm2, kitty, Terminal.app 14+, WezTerm) |
| "CLI not found at path …" inside TUI | Direct Python invocation without --cli-path | Pass --cli-path /full/path/to/secvf-cli |
| All VMs show "Unknown" | SecVF.app not running, only on-disk metadata visible | Launch SecVF.app on the host first |
| Keys don't work | Terminal multiplexer (tmux/screen) capturing them | Use a non-multiplexed session, or remap the conflict |