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:

WidthLayoutPanels & 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

KeyAction
qQuit the TUI (proxy continues in background until SIGINT)
:Open the command palette (try :help)
?Toggle the in-app help overlay
EscReturn to the flow list / dismiss overlay / exit command palette
TabCycle active area (list ↔ detail)
1 / 2 / 3 / 4Jump to detail tab: Overview · Request · Response · Messages
yCopy the selected HTTP flow as a cURL command
Ctrl+CHard-exit the process

Flow list (default focus)

KeyAction
j / Next flow
k / Previous flow
g / HomeJump to newest (top) and re-enable auto-scroll
G / EndJump to oldest (bottom)
Enter / l / Focus the detail panel
mToggle A–Z mark on the selected flow (same key removes it)
' (backtick)Jump to the next marked flow
dDelete the selected flow from the local list
cClear the entire flow list (proxy is unaffected)
pPause/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

KeyAction
j / k or / Scroll by one line
Ctrl+d / PageDownScroll by 10 lines
Ctrl+u / PageUpScroll up 10 lines
g / HomeScroll to top
G / EndScroll to bottom
TabCycle detail tabs (Overview → Request → Response → Messages → Overview)
h / / EscReturn to the flow list
vCycle body view: autoprettyrawhex

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.

CommandAction
:q / :quitQuit the TUI
:c / :clearClear the flow list
:p / :pausePause capture
:r / :resumeResume capture
:f <expr> / :filter <expr>Set a filter expression
:uf / :unfilterClear the filter
:t <name> / :theme <name>Switch theme (relay / slate / high-contrast)
:cp / :copyCopy the selected flow as cURL
:v <mode> / :view <mode>Switch body view (auto / pretty / raw / hex)
:h / :helpList 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_size in the policy; toggle the view with v)
  • 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 IDAliasesDescription
relaybrand, defaultrelaycore.dev brand — cyan accent on near-black (default)
slatelegacyTokyo Night-style slate background with amber focus
high-contrasthc, high_contrast, highcontrastBrighter accents and borders for low-contrast terminals

Resolution order

Theme is selected using the following precedence (highest first):

  1. --theme <name> CLI flag
  2. RELAY_CORE_TUI_THEME environment variable (also wired to the --theme flag via clap)
  3. [tui].theme in ~/.relay-core/config.toml
  4. 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 g frequently to re-engage auto-scroll — manually scrolling with j/k disables it so you can read a specific row while traffic continues.
  • Press m to mark interesting flows (e.g. m then A), 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-port to feed downstream tools (e.g. relay-core-cli flows) over the same proxy instance.