Skip to content

Live Traffic tab

The streaming, dense audit view. Every AI request that came through the firewall, newest first. Click any row to inspect.

Screenshot: Live Traffic page with rows + drawer opendocs/public/screenshots/live-traffic.png

Layout

  • Header — title, count-strip of verdicts (allow / log / redact / approve / deny), Live/Paused toggle.
  • Metrics strip — 5 KPIs above the table: Decisions / Block rate / p95 latency / Active agents / Top matched rule.
  • Decisions-per-minute chart — 60 bars, last 60 minutes, stacked by verdict.
  • Filter bar — text search + chip filters (agent / verdict / action / destination / rule).
  • Table — Verdict / Time / Agent / Action / Destination / Rule / Latency. 32 px rows, mono data, sans chrome.
  • Drawer (right side, click any row to open) — three tabs: Request / Decision / Context.

Color language

Verdicts use the same palette everywhere in the SPA:

VerdictPillRow left-edge stripe
allowgreen
logslate
redactamberamber tint
require_approvalcyancyan tint
denyredred tint

The single-character verdict "gutter" on the far left (A / L / R / ? / D) lets you scan a long table at a glance.

Live tail

The pulse dot in the header indicates the SSE connection is open. New decisions stream in via /api/events and prepend to the list.

If you scroll down, the list pauses — a small "↑ N new decisions" pill appears at the top so the visible rows don't shove around while you're reading them. Click the pill to flush + scroll back to top.

Keyboard:

  • L — toggle live tail.
  • Esc — close the drawer.
  • Cmd/Ctrl-K — command palette → "Show denied", "Resume live tail", etc.

Filter chips

The chips are conjunctive — you can stack any combination, and they AND together. Click any agent / destination / rule cell in a row to add it as a chip.

Examples:

  • "Show me every deny from qa-eval-loop to api.openai.com" → click deny in the verdict count strip, click the agent cell on any matching row, click the destination cell.
  • "Everything not matched by a rule" → search for no rule matched.

The "shown / total" count next to the search box tells you what fraction of the loaded events the current filter is showing.

The drawer

Click any row → a 480px-wide drawer slides in on the right with three tabs:

  • Request — the full request JSON (agent / action / destination / args) plus all metadata (source IP / hostname / user-agent / identity).
  • Decision — the verdict, matched rule, reason, and (for redact) the list of redacted field names.
  • Context — the last 8 decisions from the same agent, in mono — useful for "is this misfire isolated or part of a pattern".

The agent / destination / rule values in the drawer are buttons — clicking them adds the value as a filter chip and brings the drawer to that filtered view.

Forensic columns (since 3dc3198)

In addition to the original columns, the Live Traffic table now shows:

  • Source IP — the LAN address that originated the request.
  • MethodGET / POST / PUT / DELETE.
  • URL — full URL (path only after the host, no query strings).

These come from request.metadata populated by the collector's mitm addon.

Performance

The in-memory list caps at 800 events. New SSE messages prepend; old ones fall off the bottom. The page never paginates — it's a tail, not an archive. For deeper history, use Audit Log or the API.

When the live tail looks dead

The connection pill in the top bar tells you the truth:

  • green "Collector connected" — SSE open, new events flowing.
  • amber "Connecting…" — EventSource is in retry-with-backoff.
  • red "Collector offline" — backend unreachable, the page is showing stale data.

Quiet network, not dead: if the pill is green but no new rows arrive, your network simply isn't sending traffic. Drop a python3 dev/generate-traffic.py synthetic burst to confirm the path is alive.

→ See Generate test traffic.

Documentation for kilasec — the AI Agent Firewall.