← Cycles
Qflow · c9

c9 · dev-tools-integration

activecreated 2026-03-16· last activity 2026-03-23

Integrate qflow with qtap dev-tools in app.qpoint.io — qflow as bird's-eye view, dev-tools as detail inspector, fed by live production SSE traffic.

Two-Pronged Approach

Prong 1: Standalone qflow experiment

A self-contained experiment in the qflow dev harness that connects directly to qtap's SSE stream via a Vite proxy. Useful for rapid visual iteration without running the full dev-tools app.

Prong 2: Integration into dev-tools app

The adapter and integration code designed to work inside the qtap dev-tools app. qflow gets added as a dependency, and a new Traffic.vue page component is created that instantiates qflow and feeds it events from the existing Pinia stores (connections, HTTP transactions).


Implementation Plan

New files

SseFeed.js — SSE adapter with LiveFeed-compatible interface

SseFeed {
  constructor(url, { batchWindowMs = 200 })
  onBatch(cb)          // same callback as LiveFeed
  start()              // opens EventSource
  stop()               // closes EventSource
  get running()
  get status()         // 'disconnected' | 'connecting' | 'connected' | 'error'
  onStatusChange(cb)
}
  • Uses native EventSource API (auto-reconnect built in)
  • Listens for connection.opened and request.http_transaction SSE events
  • Micro-batches: accumulates events, flushes every ~200ms
  • Transforms events via adapter before emitting

adapter.js — Pure functions: qtap events → qflow audit-log format

  • connectionToAuditLog(connection) — source.address.ip → sourceAddress, source.exe → sourceExe, destination.address.ip → destinationAddress, etc.
  • httpTransactionToAuditLog(transaction) — adds request.method → method, derives action from response status (2xx/3xx = ALLOW, 4xx/5xx = DENY)
  • Defensive optional chaining on all nested fields

App.vue — Replicated from c5 with changes:

  • Add feedMode toggle ('mock' | 'live') in the Feed control group
  • When 'live': create SseFeed instead of LiveFeed, hide mock-only Generation controls
  • Connection status indicator (colored dot: green/yellow/red + text) in header
  • Both feeds share the onBatch(cb) / start() / stop() interface — handleBatch() works unchanged

Replicated from c5:

  • LiveFeed.js, NodeRegistry.js, mock-data.js, shapes/, main.js, index.html

Modified shared files

dev/vite.config.js — Add SSE proxy:

proxy: {
  '/devtools/api': {
    target: `http://localhost:${process.env.QTAP_PORT || 10001}`,
    changeOrigin: true,
  }
}

Dev-Tools Integration Design

The dev-tools app (qtap/pkg/devtools/app/) already has:

  • SSE events flowing through a Web Worker → useEvents() composable → Pinia stores
  • Tab-based UI in App.vue with panels: Welcome, Processes, Connections, Requests, Redis, MySQL, Kafka, gRPC
  • Connection and HTTP transaction data in useConnectionsStore() and useHttpStore()

Integration points

New tab: "Traffic"

  • Add { id: 'traffic', label: 'Traffic' } to the tabs array in App.vue
  • Add <Traffic v-else-if="activeTab === 'traffic'" /> to the panel content

New page: components/pages/Traffic.vue

  • Imports useTrafficViz (or TrafficViz component) from @qpoint-io/qflow
  • Watches useConnectionsStore().connectionsBuffer and useHttpStore().requestsBuffer
  • On new connection/HTTP events, transforms via adapter and feeds into qflow's stage

Data flow:

qtap SSE → Web Worker → useEvents() → Pinia stores
                                          ├── connectionsStore → Traffic.vue → adapter → qflow
                                          └── httpStore        → Traffic.vue → adapter → qflow

No SseFeed or EventSource needed — dev-tools already handles SSE. The Traffic panel simply watches existing stores and routes new items into qflow.

What moves to dev-tools later

Built in c7 experimentMoves to dev-tools app
adapter.jsYes — same transformation logic
SseFeed.jsNo — dev-tools already has SSE via useEvents()
App.vue (standalone)No — replaced by Traffic.vue page
Visualization tuningYes — best config gets baked into Traffic.vue

Execution Order

  1. Create adapter.js (pure functions, no dependencies)
  2. Create SseFeed.js (depends on adapter)
  3. Replicate c5 files: LiveFeed.js, NodeRegistry.js, mock-data.js, shapes/, main.js
  4. Create App.vue (based on c5, add feed mode toggle + SSE integration)
  5. Create index.html
  6. Update dev/vite.config.js (add proxy)
  7. Update dev/experiments/CYCLES.md

Verification

  1. npm run dev — confirm c7 appears in experiment list at localhost:3334
  2. Mock mode: select mock, Play — behaves like c5
  3. Live mode: sudo qtap --enable-dev-tools, select live, Play — real traffic visualized
  4. Disconnect: stop qtap — status shows "Connecting...", auto-reconnects when restarted

Outcome:

Qpoint Brand Style Guide