Interactive Terminal UI (TUI)
RelayCore ships a full-featured terminal UI for inspecting traffic in real time. It is built on ratatui and behaves like a lightweight Charles/Proxyman for your shell. Starting in 0.8 the TUI was rewritten around a responsive two-pane layout with a column budget system, a colon command palette, A–Z flow marks, a body-view toggle, and priority hints on the status bar.
Launching
relay-core-cli run --ui
# optionally pair with the REST/SSE API so external tools (e.g. flows) can share the same proxy
relay-core-cli run --ui --api-port 8082 When --ui is enabled the TUI takes over the terminal. The proxy itself continues to run in the same process — there is no extra service to start. Minimum terminal size is 60 × 12; below that the TUI refuses to start and asks you to resize.
Layouts
The TUI adapts to your terminal width through five responsive profiles that progressively expand the column budget:
| Width | Layout | Panels & columns |
|---|---|---|
| < 60 columns | — | Terminal too narrow — TUI refuses to start and asks you to resize |
| 60–79 columns | Single-pane (toggle with Tab) | Full-screen flow list or flow detail |
| 80–99 columns | Two-pane compact | Flow list · Detail (Method, Code, Dur, URL) |
| 100–119 columns | Two-pane standard | Longer URL column |
| 120–179 columns | Two-pane wide | Adds Size, Host, Path columns |
| ≥ 180 columns | Two-pane extra wide | Extended Path budget |
The four-pane layout (Flow list / Detail / Rules / Pending intercepts) was removed in 0.8. Rules and intercepts panels only remain in --api-port mode; without the API the TUI always falls back to a local two-pane view.
Keybindings
Global
| Key | Action |
|---|---|
q | Quit the TUI (proxy continues in background until SIGINT) |
: | Open the command palette (try :help) |
? | Toggle the in-app help overlay |
Esc | Return to the flow list / dismiss overlay / exit command palette |
Tab | Cycle active area (list ↔ detail) |
1 / 2 / 3 / 4 | Jump to detail tab: Overview · Request · Response · Messages |
y | Copy the selected HTTP flow as a cURL command |
Ctrl+C | Hard-exit the process |
Flow list (default focus)
| Key | Action |
|---|---|
j / ↓ | Next flow |
k / ↑ | Previous flow |
g / Home | Jump to newest (top) and re-enable auto-scroll |
G / End | Jump to oldest (bottom) |
Enter / l / → | Focus the detail panel |
m | Toggle A–Z mark on the selected flow (same key removes it) |
' (backtick) | Jump to the next marked flow |
d | Delete the selected flow from the local list |
c | Clear the entire flow list (proxy is unaffected) |
p | Pause/resume capture. While paused, new flows are counted as pending instead of being added to the list. |
/ | Open the filter bar |
Marked flows show an Mk column with the assigned letter. On narrow terminals this column collapses into the Status column to save horizontal budget.
Flow detail
| Key | Action |
|---|---|
j / k or ↓ / ↑ | Scroll by one line |
Ctrl+d / PageDown | Scroll by 10 lines |
Ctrl+u / PageUp | Scroll up 10 lines |
g / Home | Scroll to top |
G / End | Scroll to bottom |
Tab | Cycle detail tabs (Overview → Request → Response → Messages → Overview) |
h / ← / Esc | Return to the flow list |
v | Cycle body view: auto → pretty → raw → hex |
Filter bar
Press / in the flow list to start a filter, Enter or Esc to commit/close. See Filter expression syntax below.
Command palette (:)
Press : from anywhere to open the command palette, type a subcommand, and press Enter. It exposes stable text aliases for every common action — theme switching, filter management, copy, and more.
| Command | Action |
|---|---|
:q / :quit | Quit the TUI |
:c / :clear | Clear the flow list |
:p / :pause | Pause capture |
:r / :resume | Resume capture |
:f <expr> / :filter <expr> | Set a filter expression |
:uf / :unfilter | Clear the filter |
:t <name> / :theme <name> | Switch theme (relay / slate / high-contrast) |
:cp / :copy | Copy the selected flow as cURL |
:v <mode> / :view <mode> | Switch body view (auto / pretty / raw / hex) |
:h / :help | List all commands |
Body view (v)
In the detail panel, press v to cycle the request/response body render mode:
- auto — picks based on
Content-Type: JSON →pretty, text and images →raw, unknown binary →hex - pretty — syntax-highlighted (JSON / YAML / XML / HTML / JavaScript / CSS) with JSONPath folding when possible
- raw — raw byte stream (good for compressed or binary payloads)
- hex — hex dump, handy for binary protocol debugging
The WebSocket Messages tab follows the same v cycle: text frames go through pretty/raw, binary frames through hex.
Status bar
The bottom row always shows the live operating picture. In 0.8 it gains priority hints: when there is a paused capture, pending flows, pending intercepts, or a script error, the relevant indicator claims the left side of the status bar with a highlighted style until resolved.
- Live request rate (5-second sliding window, in req/s)
- Total flows captured since startup
- Active area indicator (LIST / DETAIL)
- Uptime in
hh:mm:ss - Pending counter when capture is paused or auto-scroll is off
- Current body view (only when the detail panel is focused)
Mouse support
Mouse capture is enabled. You can scroll the list with the wheel and click to focus rows or panels. Resizes trigger an immediate layout recompute.
Filter expression syntax
The TUI filter bar, the command palette's :filter, relay-core-cli flows --filter, and the HTTP API's search_flows tool all share the same parser. Press / to open the bar, then enter a filter expression. See Filter Expressions for the full syntax.
Quick examples:
host:api method:POST status:>=400 # failing POSTs to api
ws status:500-599 # 5xx WebSocket
health # any flow whose URL contains "health" Detail tabs
- Overview — method, URL, status, duration, size, host, remote address, list of matched rules, and any error message
- Request — start line, headers, and body (subject to
max_body_sizein the policy; toggle the view withv) - Response — status line, headers, and body
- Messages — WebSocket message log (opcode, direction, size, base64 content). Empty for plain HTTP.
Copy as cURL
Press y on any HTTP/WebSocket flow (or run :cp from the command palette) to copy a portable POSIX-shell cURL command to the clipboard. Header values are escaped with single quotes; bodies are written with --data-binary. Requires pbcopy on macOS or wl-copy / xclip on Linux; if none is found the TUI still builds the command and shows a toast indicating the clipboard is unavailable.
Themes
Three color presets ship with the TUI. The current default relay matches the relaycore.dev brand palette (cyan accent on near-black).
| Theme ID | Aliases | Description |
|---|---|---|
relay | brand, default | relaycore.dev brand — cyan accent on near-black (default) |
slate | legacy | Tokyo Night-style slate background with amber focus |
high-contrast | hc, high_contrast, highcontrast | Brighter accents and borders for low-contrast terminals |
Resolution order
Theme is selected using the following precedence (highest first):
--theme <name>CLI flagRELAY_CORE_TUI_THEMEenvironment variable (also wired to the--themeflag via clap)[tui].themein~/.relay-core/config.toml- Default (
relay)
# One-off override
relay-core-cli run --ui --theme slate
# Persistent per-host preference
cat >> ~/.relay-core/config.toml <<'EOF'
[tui]
theme = "high-contrast"
EOF
# Environment override (e.g. in CI)
RELAY_CORE_TUI_THEME=slate relay-core-cli run --ui Color semantics
- HTTP method: GET = green, POST = orange, PUT = cyan, DELETE = red, PATCH = teal, HEAD = purple, OPTIONS = grey, WS = cyan
- Status code: 2xx = green, 3xx = orange, 4xx/5xx = red, in-flight = dim italic
- Duration: < 100 ms = green, < 500 ms = dim, ≥ 500 ms = red
- Hosts: deterministic 8-color palette hashed from the hostname
Names are parsed case-insensitively. An unknown name produces a clear error listing the available choices.
Capture pause & pending counter
Pressing p toggles "paused" mode. The proxy keeps running and the TUI still tracks incoming flow updates, but the list is frozen and the pending counter increments with each new event. Resume with p to drop the pending counter and refollow live traffic.
Tips
- Use
gfrequently to re-engage auto-scroll — manually scrolling withj/kdisables it so you can read a specific row while traffic continues. - Press
mto mark interesting flows (e.g.mthenA), then jump between them with'— replaces the older "remember position" pattern. - Forgot a binding? Hit
:for the command palette — every action has a stable text alias. - The TUI only holds the last 1,000 flows in memory. Use
--save-stream <PATH>to capture every flow as JSONL to disk for offline analysis. - Combine with
--api-portto feed downstream tools (e.g.relay-core-cli flows) over the same proxy instance.