Skip to content

feat: add native NetFlow monitoring with Lucene search and Sankey - #3347

Open
valerypetrov wants to merge 11 commits into
hyperdxio:mainfrom
valerypetrov:agent/netflow-monitoring
Open

valerypetrov wants to merge 11 commits into
hyperdxio:mainfrom
valerypetrov:agent/netflow-monitoring

Conversation

@valerypetrov

@valerypetrov valerypetrov commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

Add native NetFlow monitoring for Akvorado tables through HyperDX connections, source configuration, Search, and charts. Integrate shared schemas, persistence, and the external API with configurable mappings and schema detection.

The page includes sampling-aware rates, top talkers, exporter/interface breakdowns, flow details, and a Sankey with two to five ordered dimensions. Lucene supports column suggestions; clickable values provide include/exclude filters. Searches, filters, and visualization settings persist in the URL. Sankey links represent selected top paths with bitrate and byte totals. Fix clipped rate-axis labels and direct ClickHouse requests in local mode.

Validation: unit suites, TypeScript, lint, ClickHouse query checks, and browser workflows passed. Screenshots use 14,400 synthetic records; setup instructions are in docs/netflow.md.

Screenshots

Traffic overview

Column suggestions

Lucene and clickable filters

Flow details

Source mappings

Sankey paths

How to test on Vercel preview

Preview routes: /netflow

  1. Open NetFlow; without a source, verify the setup prompt.
  2. With populated data, run Proto:6, select Sankey dimensions, and include/exclude a node.
  3. Reload; verify filters and dimensions persist.

Reference

Akvorado provides the schema and visualization reference.

@changeset-bot

changeset-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 219da28

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/app Minor
@hyperdx/api Minor
@hyperdx/common-utils Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Oct 9, 2026

Copy link
Copy Markdown

@valerypetrov is attempting to deploy a commit to the HyperDX Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added external needs-vouch Author needs a maintainer to vouch for them labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Hi @valerypetrov, thanks for the pull request!

Before we review code from a first-time contributor we ask that a maintainer vouches for you, and you're not on our list yet. This PR stays open — it just isn't in the review queue until someone vouches.

To get vouched, open an issue saying hello and what you're working on:

https://github.com/hyperdxio/hyperdx/issues/new?template=introduce-yourself.md

A maintainer will usually reply within a day or two, and then this PR gets picked up as normal. More detail in our contributing guide.

Comment thread packages/app/src/HDXMultiSeriesTimeChart.tsx
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

PR Review

If you are a coding agent acting for the author, read this first.
Nothing below blocks merge automatically; a maintainer decides. What they will expect:

  • 🔴 critical or 🟠 major in code this PR adds or changes: fix before asking for review.
  • 🔵 minor in code this PR changes: your call. Fix if small, otherwise reply.
  • Anything about surrounding code, or asking you to widen the change (hoist a helper,
    dedupe with another file, fix other call sites): never fix here, whatever the
    severity. Reply with one sentence; if it is critical, say so plainly so a human sees it.

One commit per review round. After two rounds, stop and ask a maintainer to review
scope rather than addressing more automated findings. Full rule: AGENTS.md.

3 finding(s): 🔴 0 critical · 🟠 0 major · 🔵 3 minor

3 posted as inline comment(s) on the changed lines.


Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found.

No P0/P1 defects surfaced from the completed reviewers or from direct inspection of the highest-risk paths. SQL injection is not present — filter values pass through escapeSqlString (packages/app/src/netflow.ts:48) while column mappings are admin-defined SQL expressions consistent with HyperDX's existing source trust model. Sankey aggregation guards against cycles (stage-qualified node IDs), non-positive values, and missing dimension columns (packages/app/src/netflowSankey.ts:99-133). The shared-client local-mode header change (packages/common-utils/src/clickhouse/browser.ts:187) reuses the existing credential-based isLocalMode definition that already drives the direct-fetch path, so it is coherent and not a regression. All six prior-review items were independently confirmed resolved.

🟡 P2 -- recommended

  • packages/app/src/netflow.ts:185 -- The four summary tiles (bitsPerSecond, packetsPerSecond, totalBytes, flowRecords) share identical from/where/dateRange yet each becomes an independent query, scanning the flow table four times for one header row.
    • Fix: Combine the four scalar aggregates into a single query config with four select columns so the summary needs one scan.
    • performance
🔵 P3 nitpicks (3)
  • packages/app/src/components/NetflowRecords.tsx:52 -- The row array is rebuilt via Object.fromEntries over up to 500 rows on every render, including when opening the detail drawer rebuilds unchanged data.
    • Fix: Wrap the row transform in useMemo keyed on data.
  • packages/api/src/mcp/tools/sources/describeSource.ts:122 -- The curated keyColumns summary has branches for Trace/Log/Metric but none for Netflow, so mapping fields surface only under config, not in the human/agent-readable summary.
    • Fix: Add a SourceKind.Netflow branch populating keyColumns with the srcAddr/dstAddr/port/protocol/bytes/packets mappings.
  • packages/api/src/routers/external-api/v2/sources.ts:970 -- samplingRateExpression carries the generic "SQL expression for the mapped flow field" description despite being a multiplier with distinct semantics.
    • Fix: Give it a dedicated description clarifying the multiplier/default-one behavior and mirror the text in openapi.json.

Reviewers (5): api-contract, performance, previous-comments, learnings-researcher, orchestrator deep-inspection (security/correctness/SQL-escaping/Sankey/local-mode spot-checks).

Testing gaps:

  • NetFlow browser and ingestion fixture checks (scripts/netflow-*-browser-check.mjs) remain manual and are not wired into CI, so source-switch, filter, and port-default behaviors are not continuously verified.
  • No external REST (v2) integration test asserts a Netflow source round-trips create→GET→update with all mapping fields, or that omitting a required mapping returns a 4xx; the MCP path is covered but the REST serializer is not.
  • No regression guard exists for consolidating the four summary-tile queries, nor for NetflowRecords render cost at the 500-row limit.

Coverage note: The correctness, security, adversarial, reliability, testing, maintainability, project-standards, kieran-typescript, and agent-native reviewers had not returned results at synthesis time; their lanes were partially covered by direct orchestrator inspection but a full pass on those dimensions did not complete. Treat the "no critical issues" verdict as grounded in the completed reviewers and the highest-risk spot-checks, not an exhaustive multi-persona sign-off.

No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond

@greptile-apps

greptile-apps Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[High impact] The PR appears safe to merge, with the existing non-blocking range-removal issue still open.

Fix All in Claude CodeFindings

  1. P2 Range removal clears other filters ▶

Summary

Adds NetFlow sources, Lucene search, sampling-aware traffic charts, flow details, and Sankey paths through existing ClickHouse connections.

  • NetFlow users can search flows, compare traffic, and inspect recent records on one page.
  • Flow sources can map their table columns to NetFlow fields.
  • Users can map selected flow dimensions into ordered traffic paths.
  • NetFlow fields work with shared search, charts, and saved-search alerts.
  • Time charts size their axes and plot area from the rendered chart.
  • A local ClickHouse demo can seed flow data and open the NetFlow page.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Source[NetFlow source mappings] --> Query[Shared search and chart queries]
  Search[Lucene search and URL filters] --> Query
  Query --> CH[(ClickHouse)]
  CH --> Overview[Traffic charts and flow records]
  CH --> Paths[Top paths and Sankey]
  Query --> Alerts[Saved-search alerts]
Loading

Reviews (9) · Last reviewed commit: "fix: accept protocol names in the NetFlo..." · Reviewed by Greptile

Comment thread packages/app/src/NetflowPage.tsx
Comment thread packages/app/src/components/Sources/SourceForm/SourceForm.tsx Outdated
Comment thread packages/app/src/components/NetflowFilterPills.tsx Outdated
Comment thread scripts/netflow-click-browser-check.mjs Outdated
Comment thread packages/app/src/__tests__/source.test.ts Outdated
@valerypetrov

Copy link
Copy Markdown
Author

Addressed this review round in 9053fcb:

  • Use measured chart geometry for automatic axis widths, annotations, and single bars.
  • Apply source selection immediately, clear source-specific filters when switching, and ignore stale schema inference.
  • Preserve all NetFlow mappings through MCP create, describe, update, and clone.
  • Merge table/Sankey includes for the same field, make Include/Exclude idempotent, and preserve quoted column expressions through reloads and removal.
  • Respect timestamp preferences, recover from invalid time ranges, remove the redundant Sankey empty state, fix demo-check ports, and replace the untyped metadata mock.

Validation: all app/API/common-utils unit suites passed, plus 58 MCP integration tests, ClickHouse query checks, and browser workflows covering source changes, search, filters, Sankey, axis layout, and time-range recovery. One app suite needed a rerun after a concurrent shared build temporarily removed its dependency; it passed. Repository lint/type/style checks, lint-staged, and Knip passed.

For the remaining suggestions, following the repository's review scope rules:

  • Retaining documented numeric quick filters as explicit AND constraints; consolidating those controls would change existing URL and interaction semantics.
  • Deferring menu/label/alias consolidation and local-mode helper extraction as refactors.
  • Keeping the documented local fixture checks manual; migrating ingestion and browser fixtures into CI is separate work.

SQL escaping already has unit and real ClickHouse coverage. Added explicit partial-credential routing cases, and documented why NetFlow uses an empty known-column set while restoring exact SQL expressions during serialization.

Comment thread packages/app/src/DBSearchPage.tsx
Comment thread packages/app/src/components/NetflowFilterPills.tsx
Comment thread packages/common-utils/src/clickhouse/browser.ts Outdated
Comment thread scripts/netflow-browser-check.mjs Outdated
Comment thread packages/app/src/NetflowPage.tsx Outdated
@valerypetrov

Copy link
Copy Markdown
Author

Addressed the second review round in dee8414:

  • Fixed NetFlow saved-search alert evaluation, default aliases, notification samples, and preview aliases.
  • Keep unfinished Lucene and quick-filter fields pending when Include/Exclude or filter migration updates the URL; Run applies them.
  • Display the configured records limit and keep NetflowPage below 300 lines.

Validation: 110 backend integration tests and 72 snapshots passed, including real local ClickHouse/Mongo alert evaluation; 43 alert unit tests and 9 focused app tests passed. Both local browser workflows passed, covering drafts, source creation/switching, invalid ranges, search, autocomplete, records, and click filters. Repository lint/type/style/OpenAPI checks, lint-staged, and Knip passed. Historical log-alias fixtures required removing their expired TTL only in the isolated test ClickHouse instance.

Remaining review suggestions:

  • Deferring shared labels/aliases/menus/mapping tables, source-kind deduplication, protocol-table extraction, and broader CI fixture migration under the repository scope rules. The new draft regression does run in the existing app unit suite.
  • Sankey aggregation memory remains a scale-validation item: the local synthetic fixture is 14,400 rows, not a production-volume benchmark. A returned-path limit does not bound GROUP BY memory; no production-scale claim is made.
  • Additional dimension/source-roundtrip/rendering coverage and MCP description polish remain follow-up suggestions. Existing per-field MCP descriptions and source roundtrip checks remain in place.
  • The root ClickHouse client version deliberately matches the version already used by common-utils; changing the shared client release is outside this fix.
  • The “older links” comment covers earlier demo URLs; filter emission is already canonical and normalization preserves those links.

Maintainers: please review the remaining scope and scale-validation items. Per AGENTS.md, “After two rounds of bot findings, stop.” This is the second round; further automated suggestions need maintainer triage.

Comment thread packages/common-utils/src/types.ts
Comment thread packages/app/src/components/NetflowSankeyTable.tsx Outdated
Comment thread packages/app/src/components/NetflowFilterPills.tsx Outdated
Comment thread packages/app/src/components/NetflowFilterPills.tsx Outdated
Comment thread packages/app/src/components/NetflowFilterMenu.tsx Outdated
Comment thread packages/app/src/netflowSankey.ts
Comment thread packages/common-utils/src/clickhouse/browser.ts Outdated
Comment thread packages/app/src/components/AppNav/AppNav.tsx Outdated
Comment thread package.json Outdated
Comment thread packages/app/src/__tests__/HDXMultiSeriesTimeChart.geometry.test.tsx Outdated
@valerypetrov

Copy link
Copy Markdown
Author

Addressed the remaining minor findings in a476fa9:

  • Bare Lucene terms now search mapped flow dimensions, with a configurable expression preserved across source forms, REST, MCP, Search, and alerts.
  • Four summary tiles share one aggregate request. Flow records reuse the mapped result when opening a drawer.
  • Unified filter menus, labels, field types, query aliases, protocol names, source defaults, local-mode detection, and the Search source-kind list. Filter targets have human-readable accessible names and keyboard support.
  • Sankey dimensions recover after incompatible source switches; wrapper inversion and path-limit validation are shared, and unused totals are removed.
  • NetFlow follows existing navigation/source entries. MCP summaries expose mappings; MCP/REST descriptions explain required fields and sampling semantics.
  • Migrated seven manual check scripts into 14 CI-discovered Playwright tests with isolated databases/sources. Removed their root dependencies and Knip exemptions. Added REST source roundtrip/validation, scalar-dimension, full-text, rendering, and performance regressions.

Validation: all 8,597 unit tests passed (4,603 app, 1,157 API, 2,837 common), plus 75 API integration tests and 14 local Playwright tests. The browser suite verifies a single summary request, distinct metric values, source creation/switching, autocomplete, Search, drafts, filters, Sankey, and real axis bounds. Geometry unit coverage uses real Recharts context and bars. Repository lint/type/style/OpenAPI checks, lint-staged, and Knip passed.

Local Playwright used a temporary configuration that bypassed only the unrelated global PromQL seeder, which is incompatible with local ClickHouse 26.10; these NetFlow specs seeded their own real data. The committed runner and CI configuration remain unchanged.

A separate two-million-row, five-dimension, 30-day Sankey check completed in 0.71 seconds with a 512 MiB memory cap and a 64 MiB spill threshold. The documentation explains that LIMIT bounds returned paths rather than aggregation memory and records the test's scope.

Comment thread packages/common-utils/src/core/searchChartConfig.ts Outdated
Comment thread packages/app/src/netflow.ts Outdated
Comment thread packages/app/src/netflow.ts
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found.

Direct inspection of the highest-risk path — ClickHouse SQL construction for NetFlow (packages/app/src/netflow.ts) — surfaced no P0/P1 defects introduced by this diff. Runtime filter values are escaped through escapeSqlString before interpolation (packages/app/src/netflow.ts:107), and address/protocol filters are wrapped in toIPv6OrNull/toString comparisons; column-mapping expressions (srcAddrExpression, bytesExpression, etc.) are admin-defined source configuration interpolated as SQL, consistent with HyperDX's existing source trust model rather than a user-supplied injection vector. Sampling multipliers are normalized to 1 when non-positive (packages/app/src/netflow.ts:137-139), and edge buckets scale by their actual overlap with the selected range (packages/app/src/netflow.ts:152-153). The P1 items raised in prior rounds (stale filters on source switch, stale schema-inference responses, quoted-column round-trips, range-removal clearing other filters) are reported addressed across the branch's fix commits; the previous-comments reviewer was dispatched to confirm their current state.

🟡 P2 -- recommended

  • packages/app/src/netflow.ts:276 -- The totalBytes query recomputes sum(bytes) with its own full table scan even though the summary query already returns the same aggregate under NETFLOW_ALIASES.bytes at line 268.
    • Fix: Reuse the summary query's bytes result for the Transferred-bytes tile instead of issuing a separate scan.
🔵 P3 nitpicks (1)
  • packages/app/src/netflow.ts:195 -- The traffic bit-rate divides by coveredSeconds, which is least(bucketEnd, rangeEnd) - greatest(bucketStart, rangeStart) and can evaluate to zero or negative for a bucket with no overlap, yielding inf/nan for that point rather than a clean value.
    • Fix: Floor the divisor to a positive value (e.g. greatest(coveredSeconds, 1)) before dividing.

Reviewers (14 dispatched): correctness, security, adversarial, testing, maintainability, project-standards, performance, api-contract, reliability, kieran-typescript, julik-frontend-races, previous-comments, agent-native, learnings-researcher.

Coverage note: This synthesis is grounded in direct orchestrator inspection of packages/app/src/netflow.ts, the PR commit history, and the 51 prior review comments. The 14 persona sub-agents were dispatched but had not returned results at synthesis time, so their dimensions (full correctness, security, adversarial, reliability, TypeScript, frontend-race, and contract passes across the ~7,600-line diff) are only partially covered by the spot-checks above. Treat the "no critical issues" verdict as reflecting the completed inspection and the independently-verified SQL-construction path, not an exhaustive multi-persona sign-off.

Testing gaps: NetFlow browser/ingestion fixtures and the 2M-row Sankey scale check remain local/manual rather than CI-enforced, per the author's documented scope decisions.

No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond

@valerypetrov

valerypetrov commented Oct 9, 2026 •

Copy link
Copy Markdown
Author

Addressed the latest review findings in bbd3e30:

  • Added one source-level implicit-column helper and applied it to dashboard tiles, builder/raw-SQL chart previews, alert details, dashboard filter values, release annotations, and existing API/Search paths. NetFlow gets its mapped-dimension fallback; Log/Trace mappings retain their existing behavior.
  • Flow records select and sort by the first timestamp expression while preserving the full mapping for shared time filtering.
  • Traffic rates use each bucket’s actual overlap with the selected range. A steady-traffic ClickHouse regression now returns 100,000 bit/s in all 61 buckets, including both partial edges, without changing summary bounds.
  • Corrected the NetFlow timestamp and default-select REST descriptions and regenerated OpenAPI.

Validation: 2,842 common-unit tests, 280 focused app tests, 68 API unit tests, 112 API integration tests, and all 15 NetFlow Playwright tests passed. Repository lint/type/style/OpenAPI checks, lint-staged, and Knip passed. Local Playwright again used the temporary global-setup bypass for the unrelated PromQL seeder incompatibility with ClickHouse 26.10; NetFlow fixtures seeded and cleaned up their own real ClickHouse data.

For the repeated items in Deep Review, a476fa9 already includes the sampling multiplier/default-one description, SQL-metacharacter execution coverage in packages/app/tests/e2e/features/netflow/queries.spec.ts, and REST create/read/update/required-field coverage in packages/api/src/routers/external-api/__tests__/netflow-sources.int.test.ts. The two-million-row Sankey validation and memory settings are documented in docs/netflow.md; it is a measured local check, not a CI benchmark or production performance guarantee.

The two Vercel statuses require deployment authorization.

Follow-up adc1e10 consolidates the 23 NetFlow query/hydration regressions into the existing test file. Both review jobs had stopped before code review because their gh pr view --json files call returned only 100 of the PR's 101 files. Consolidation brings the PR to 100 files with the same assertions; the combined suite, app type check, repository lint-fix, lint-staged, and Knip pass. No review-workflow changes were needed.

(async () => {
try {
if (watchedTableName !== prevTableNameRef.current) {
if (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 major — Schema inference now overwrites saved mappings on any source when the connection, database or kind changes

Inference used to run only when the table name changed. It now also runs on a kind, connection, database or metadata change, for every source kind. It calls resetField(..., { defaultValue }) on every inferred field that isn't dirty, and loaded saved values never count as dirty. So editing an existing Log or Trace source and switching it to another connection (for example a replica with the same table) silently swaps custom implicitColumnExpression, bodyExpression and similar values for the inferred defaults, and Save persists them. Fix: only re-infer on these new triggers when isNew is true or the watched kind is SourceKind.Netflow, or skip fields whose current value differs from their pristine value.

Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7ebbb8d. Context changes now trigger inference only for new sources; saved Log, Trace, and NetFlow mappings survive connection/database/metadata changes. Explicit table/kind changes still infer, and stale responses remain cancelled. Regression tests reproduced the overwrite for all three kinds before the fix; all 19 source-form tests pass.

],
where: '',
timestampValueExpression: source.timestampValueExpression,
...(source.kind === SourceKind.Netflow && {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Bare-term implicit column is limited to NetFlow in the external charts API

getSourceImplicitColumnExpression already returns the right value for every kind (Log/Trace configured expression, NetFlow derived, undefined otherwise). Gating it on source.kind === SourceKind.Netflow adds a special case and leaves Log/Trace series with bare Lucene terms in where without their configured implicit column. Set implicitColumnExpression: getSourceImplicitColumnExpression(source) unconditionally, as the other call sites in this diff do.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7ebbb8d by applying getSourceImplicitColumnExpression unconditionally. The Log/Trace omission predates this feature, but the already-edited builder now handles all three searchable kinds consistently. Real ClickHouse tests through POST /api/v2/charts/series verify bare-term plus numeric Lucene filtering for Log, Trace, and NetFlow (6 query integration tests pass).

icon: <IconSitemap size={16} />,
isBeta: true,
},
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — NetFlow nav, spotlight and preset entries are shown to every user unconditionally

The nav link, the Spotlight preset (packages/app/src/Spotlights.tsx:93) and the dashboards-list preset all appear for every deployment, even with no NetFlow source. The neighbouring niche preset (Kubernetes) sits behind IS_K8S_DASHBOARD_ENABLED. Put these behind a flag, or hide them when useSources() has no SourceKind.Netflow source, or at least mark the nav entry isBeta like Service Map.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked the NetFlow nav entry isBeta in 7ebbb8d using the existing Service Map convention. Kept the entry discoverable when no source exists so users can reach its source-setup flow.

Comment thread packages/app/src/hooks/__tests__/useQueriedDashboardFilterValues.test.tsx Outdated
@valerypetrov

Copy link
Copy Markdown
Author

Addressed the current Claude, Greptile, and Deep Review findings in 7ebbb8d:

  • Preserve saved Log/Trace/NetFlow mappings when connection, database, or metadata changes; new sources and explicit table/kind changes still infer mappings. Stale inference responses remain ignored.
  • Clear applied Lucene/text filters and pending drafts on actual source changes. Resolving a source name to its ID preserves the query. Pass only the four quick-filter fields to the query builder.
  • Reuse ActiveFilterPills with display labels, including removable range filters. NetFlow retains its previous non-editable pill values to avoid metadata lookups using cleaned SQL expressions. Its empty known-columns set is instance-local.
  • Normalize NULL/non-positive sampling multipliers to one across all aggregates, records, and Sankey paths; document the behavior in REST, MCP, and OpenAPI.
  • Guard stale Sankey render indices and mount memoized cell popovers only after interaction.
  • Apply the generic implicit-column helper consistently in external chart queries, replace the new untyped mock with a complete typed NetFlow fixture and real query result, and mark NetFlow navigation beta.

Validation: 173 affected app unit tests, 13 real ClickHouse/Mongo API integration tests, and all 16 NetFlow Playwright tests passed. Integration coverage now includes Log/Trace/NetFlow bare terms through the v2 series endpoint, saved-alert OK/no-match and grouped results, NULL/zero/negative sampling, URL range removal, and source-switch draft cleanup. Repository lint/type/style/OpenAPI checks, lint-staged, and Knip passed.

The reported partial-bucket test gap was already covered by the real ClickHouse test in adc1e10 (61 buckets at the expected constant rate, plus exact-range totals). I retained the existing default-SELECT type guards: they already include Log, Trace, and NetFlow, while isSearchableSource returns a boolean rather than narrowing the source union; replacing them is a future refactor, not a current behavior fix.

Removed the previous unnecessary Services-dashboard helper edit because that dashboard restricts sources to Trace. The PR remains at 100 files, keeping the existing review jobs' file-list check working. Local Playwright again used isolated NetFlow fixtures and the documented bypass for the unrelated global PromQL seeder on ClickHouse 26.10. Vercel deployment still requires authorization.

]
.map(expression => expression?.trim())
.filter(Boolean);
return `concatWithSeparator(' ', ${expressions.map(expression => `ifNull(toString(${expression}), '')`).join(', ')})`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Bare-term protocol search uses raw numbers, but the UI shows names like TCP

The default implicit expression wraps the raw protocolExpression (toString(Proto) gives '6'). The charts, records and pills all show transform(...) names from getNetflowDimensions (packages/app/src/netflow.ts), so a bare TCP becomes ILIKE '%TCP%' and matches nothing. That contradicts docs/netflow.md, which says bare terms search the protocol. It also re-lists the mapped fields separately from getNetflowDimensions. Fix: move the dimension builder (protocol name transform, ::ffff: stripping) into common-utils and build the implicit expression from it, so there is one list of searchable flow dimensions.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

isTraceSource(source) || isLogSource(source)
? source.implicitColumnExpression
: undefined,
implicitColumnExpression: getSourceImplicitColumnExpression(source),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — NetFlow dashboard filters are unreachable from the UI

QUERY_EXPRESSION_FILTER_SOURCE_KINDS (packages/common-utils/src/filters.ts:872) still lists only Log/Trace/Session/Metric. QueryExpressionFilterEditForm and DBDashboardImportPage use it as allowedSourceKinds, so users can never pick a NetFlow source for a dashboard filter. The new hook path and its test only run for hand-crafted or API-created filters. Fix: add SourceKind.Netflow to that list if the changeset's dashboard-filter support is intended, or drop that claim.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

import { useDefaultTimeRange, useNewTimeQuery } from '@/timeQuery';
import { parseAsJsonEncoded } from '@/utils/queryParsers';

const queryParsers = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Two separate filter systems for the same flow fields

Exporter, protocol and source/destination IP can each be filtered two ways: the quick-filter text inputs (exporter/protocol/srcAddr/dstAddr URL params, rendered by buildNetflowWhere as raw-column SQL that wants '6') and click include/exclude filters (the filters param, keyed on the canonical dimension expression that wants 'TCP' and stripped IPv4 addresses). They show up separately (inputs vs pills) and use different value formats, and both URL formats now have to be supported. Fix: route the quick inputs through useNetflowFilterState/applyFilter so each field has one persisted representation.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

isNew={mode === 'new'}
sourceId={mode === 'edit' ? sourceId : undefined}
defaultName="NetFlow"
defaultKind={SourceKind.Netflow}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — 'Add NetFlow source' modal can create a non-NetFlow source

TableSourceForm always renders the full kind radio group, so in this modal the user can switch to Logs or Traces and save. NetflowPage's onCreate then calls clearFilters(created.id) with an id that is not in netflowSources, and the page shows 'NetFlow source unavailable'. Fix: add a prop to TableSourceForm that locks or hides the kind selector when defaultKind is forced, and pass it from this modal.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

label={SOURCE_KIND_LABELS[SourceKind.Promql]}
/>
)}
<Radio

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Beta NetFlow kind is shown without a feature flag

Every other optional source kind is gated (IS_METRICS_ENABLED, IS_SESSIONS_ENABLED, IS_PROMQL_ENABLED), but the NetFlow radio, nav entry (marked isBeta), spotlight action and preset dashboard card are always shown to every deployment. Fix: add an IS_NETFLOW_ENABLED flag in config.ts and gate these entry points with it, as PromQL does.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Comment on lines +199 to +202
<ActiveFilterPills
searchFilters={state}
chartConfig={chartConfig}
enableValueEditing={false}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Range removal clears other filters

NetFlow now uses ActiveFilterPills, whose range removal calls clearFilter and deletes all selections for that field. For a shared URL containing Bytes BETWEEN 100 AND 200 and Bytes NOT IN ('150'), removing only the range also removes the exclusion. The page then shows more traffic than the user intended.

Remove just range and keep the field’s included and excluded values.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

Bare Lucene terms searched the raw protocol number, so "TCP" matched no
flows while charts, pills and records all show protocol names. The default
full-text expression now uses the protocol-name transform from the NetFlow
dimensions, moved to common-utils so both share one list.

The source modal can save a non-NetFlow source. The page now switches only
to a created NetFlow source instead of showing "NetFlow source unavailable".

Flow records keyed rows by index behind an eslint-disable, which fails the
escape-hatch ratchet. Rows are now keyed by content plus a repeat count.

The changeset no longer claims dashboard-filter support, because dashboard
filters cannot select NetFlow sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/common-utils/src/core/searchChartConfig.ts
The previous commit replaced the protocol number with its name in the
default full-text expression, so a bare "17" stopped matching UDP flows.
Search both the number and the name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
severityText: source.severityTextExpression,
traceId: source.traceIdExpression,
};
} else if (source.kind === SourceKind.Netflow) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — NetFlow keyColumns were added to describe_source but not to clickstack_list_sources

packages/api/src/mcp/tools/sources/listSources.ts:274-308 still builds meta.keyColumns only for Trace, Log and Metric, so agents that list sources get no flow mappings for NetFlow sources while describe_source returns them. Add the same SourceKind.Netflow branch there, ideally through one shared helper used by both tools so the two lists can't drift.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Charts, records and the Sankey show protocols by name, but the Protocol
filter only matched the raw number, so typing "TCP" returned no flows.
Known names now map to their protocol number before filtering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
label={SOURCE_KIND_LABELS[SourceKind.Promql]}
/>
)}
<Radio

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — NetFlow source kind is always shown; Metric, Session and PromQL are each behind a feature flag

Gate the NetFlow radio behind its own flag, the way the sibling kinds use IS_METRICS_ENABLED, IS_SESSIONS_ENABLED and IS_PROMQL_ENABLED. Apply the same flag to the other entry points this PR adds without one: the /netflow nav link (AppNav.tsx), the Spotlight preset (Spotlights.tsx) and the dashboards-list preset (DashboardsListPage.tsx). As written, every deployment gets a beta source kind and page with no way to turn them off.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

@@ -2653,7 +2692,9 @@ export function isPromqlSource(source: TSource): source is TPromqlSource {
return source.kind === SourceKind.Promql;
}
export function isSearchableSource(source: TSource): boolean {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Log/Trace/NetFlow kind check is copied by hand to ~6 sites because isSearchableSource is not a type guard

Make isSearchableSource a type guard (source is TLogSource | TTraceSource | TNetflowSource) and use it in resolveSelect (searchChartConfig.ts), AlertPreviewChart.tsx, computeAliasWithClauses (checkAlerts/index.ts), the select fallback in DBSearchPage.tsx, and both ChartEditorControls.tsx checks. Each of those now re-spells the same three-way kind check, so adding the next searchable kind means editing all of them again.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

tabIndex?: number;
};
};
type Props = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — memo on NetflowFilterMenu has no effect because its callback props are recreated on every render

onFilter from useNetflowFilterState (NetflowFilterPills.tsx) is a new function on every NetflowPage render. It is passed down through NetflowCharts and NetflowRecords to every cell. NetflowSankeyTable and NetflowSankeyNode also pass inline onSelect arrows. So all 500×4 record cells re-render anyway, despite the comment about thousands of cells. Fix: wrap onFilter/onDimensionFilter in useCallback (with a ref for expressions/dimensions) and pass stable dimension/value props instead of inline closures; otherwise drop the memo.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external needs-vouch Author needs a maintainer to vouch for them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant