Commit graph

1535 commits

Author SHA1 Message Date
Warren Lee
0a4fb15df2
[HDX-4029] Add commonly-used core and contrib components to OTel Collector builder-config (#2121)
## Summary

Update `packages/otel-collector/builder-config.yaml` to include commonly-used components from the upstream [opentelemetry-collector](https://github.com/open-telemetry/opentelemetry-collector) core and [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib) distributions. This gives users more flexibility in their custom OTel configs without pulling in the entire contrib distribution (which causes very long compile times).

Also adds Go module and build cache mounts to the OCB Docker build stage for faster rebuilds, and bumps CI timeouts for integration and smoke test jobs to account for the larger binary.

### Core extensions added (2)

- `memorylimiterextension` — memory-based limiting at the extension level
- `zpagesextension` — zPages debugging endpoints

### Contrib receivers added (4)

- `dockerstatsreceiver` — container metrics from Docker
- `filelogreceiver` — tail log files
- `k8sclusterreceiver` — Kubernetes cluster-level metrics
- `kubeletstatsreceiver` — node/pod/container metrics from kubelet

### Contrib processors added (12)

- `attributesprocessor` — insert/update/delete/hash attributes
- `cumulativetodeltaprocessor` — convert cumulative metrics to delta
- `filterprocessor` — drop unwanted telemetry
- `groupbyattrsprocessor` — reassign resource attributes
- `k8sattributesprocessor` — enrich telemetry with k8s metadata
- `logdedupprocessor` — deduplicate repeated log entries
- `metricstransformprocessor` — rename/aggregate/transform metrics
- `probabilisticsamplerprocessor` — percentage-based sampling
- `redactionprocessor` — mask/remove sensitive data
- `resourceprocessor` — modify resource attributes
- `spanprocessor` — rename spans, extract attributes
- `tailsamplingprocessor` — sample traces based on policies

### Contrib extensions added (1)

- `filestorage` — persistent file-based storage (used by clickhouse exporter sending queue in EE OpAMP controller)

### Other changes

- **Docker cache mounts**: Added `--mount=type=cache` for Go module and build caches in the OCB builder stage of both `docker/otel-collector/Dockerfile` and `docker/hyperdx/Dockerfile`
- **CI timeouts**: Bumped `integration` and `otel-smoke-test` jobs from 8 to 16 minutes in `.github/workflows/main.yml`

All existing HyperDX-specific components are preserved unchanged.

### How to test locally or on Vercel

1. Build the OTel Collector Docker image — verify OCB resolves all listed modules
2. Provide a custom OTel config that uses one of the newly-added components and verify it loads
3. Verify existing HyperDX OTel pipeline still functions

### References

- Linear Issue: https://linear.app/clickhouse/issue/HDX-4029
- Upstream core builder-config: https://github.com/open-telemetry/opentelemetry-collector/blob/main/cmd/otelcorecol/builder-config.yaml
- Upstream contrib builder-config: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/cmd/otelcontribcol/builder-config.yaml
2026-04-15 15:57:44 +00:00
Warren Lee
cb841457f2
[HDX-3994] Deprecate clickhouse.json feature gate in favor of per-exporter json config (#2119)
## Summary

Deprecate the upstream-deprecated `--feature-gates=clickhouse.json` CLI flag in favor of the per-exporter `json: true` config option, as recommended by the OpenTelemetry ClickHouse exporter v0.149.0.

This introduces a new env var `HYPERDX_OTEL_EXPORTER_CLICKHOUSE_JSON_ENABLE` that controls JSON mode at the exporter config level. The old `OTEL_AGENT_FEATURE_GATE_ARG` env var remains backward-compatible — when it contains `clickhouse.json`, the entrypoint strips that gate, maps it to the new env var, and prints a deprecation warning. Other feature gates are preserved and passed through to the collector.

**Key changes:**
- **`docker/otel-collector/entrypoint.sh`** — Detects `clickhouse.json` in `OTEL_AGENT_FEATURE_GATE_ARG`, strips it, sets `HYPERDX_OTEL_EXPORTER_CLICKHOUSE_JSON_ENABLE=true`, and prints a deprecation warning. Remaining feature gates are still passed through to the collector in both standalone and supervisor modes.
- **`docker/otel-collector/config.standalone.yaml`** — Added `json: ${env:HYPERDX_OTEL_EXPORTER_CLICKHOUSE_JSON_ENABLE:-false}` to both ClickHouse exporter configs
- **`packages/api/src/opamp/controllers/opampController.ts`** — Added `json` field to the `CollectorConfig` type and both ClickHouse exporter configs for OpAMP-managed collectors
- **`docker/otel-collector/supervisor_docker.yaml.tmpl`** — Feature gate pass-through preserved for non-`clickhouse.json` gates (entrypoint strips the deprecated gate before supervisor template renders)
- **`smoke-tests/otel-collector/`** — Added a JSON-enabled otel-collector service and smoke tests verifying:
  - `ResourceAttributes` and `LogAttributes` columns in `otel_logs` are `JSON` type (not `Map`)
  - Log data with various attribute types (string, int, boolean) is inserted and queryable via JSON path access

### How to test locally or on Vercel

1. Run `yarn dev` to start the dev stack
2. Verify the `otel-collector-json` container starts without errors (the `clickhouse.json` feature gate is stripped, not passed to the collector)
3. Check container logs for the deprecation warning when `OTEL_AGENT_FEATURE_GATE_ARG` contains `clickhouse.json`
4. Verify the non-JSON `otel-collector` service continues to work normally (json defaults to false)
5. Run smoke tests: `cd smoke-tests/otel-collector && bats json-exporter.bats`

### References

- Linear Issue: https://linear.app/hyperdx/issue/HDX-3994
- Upstream deprecation: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter#experimental-json-support
2026-04-15 15:49:45 +00:00
Himanshu Kapoor
4ca1d472e3
Add direct trace search redirect (#2113)
## Summary

Allow manually constructed /trace URLs to land in the existing search experience with the trace viewer opened from URL state. This keeps trace deep links user-friendly while reusing the search page for source selection, not-found handling, and trace inspection.

### Screenshots or video

https://uploads.linear.app/158526bc-703f-4ea7-a6ff-9dbc61b77628/340aba1f-eab7-45dd-a1f6-216199e96d98/1c0193fa-ebc2-4238-aeb1-7166592483f9

### How to test locally or on Vercel



1. Click a log entry, view the traces tab
2. Copy the trace ID
3. Visit `/trace/:trace_id`: redirects to search with traces search open in the background
4. Select a source
5. Trace visible in a direct access.

### References



Fixes HDX-3961
2026-04-15 13:29:03 +00:00
Drew Davis
1fada918c7
feat: Support alerts on Raw SQL Number Charts (#2114)
## Summary

This PR extends alerting support to Raw SQL Number charts.

Number charts

1. Do not use the interval parameter, and thus return one value for the entirety of the alert time range.
2. Are assumed to not be using a group-by (the value of the chart is taken from the first result row only).

### Screenshots or video

<img width="1383" height="1071" alt="Screenshot 2026-04-14 at 12 54 18 PM" src="https://github.com/user-attachments/assets/e74c3ad8-c95a-4668-8332-86f66f7543ba" />

### How to test locally or on Vercel

To test locally, create a raw-sql based number chart, create an alert on it, and view the alert logs for the output.

You can also run the following to run a "webhook" destination that echos what it receives, for testing notification content:

```bash
npx http-echo-server
```

### References



- Linear Issue: Closes HDX-3987
- Related PRs:
2026-04-15 11:50:45 +00:00
Vineet Ahirkar
6e9a553da2
Support custom resource attributes for Hyperdx frontend app's internal telemetry (#2067) 2026-04-14 17:40:20 -07:00
Warren Lee
c4a1311e86
Fix "Copy entire row as JSON" button TypeError on non-string values (#2116)
## Summary

Fixes an operator-precedence bug in the "Copy entire row as JSON" button on the Search page. The `typeof value === 'string'` guard only applied to `value.startsWith('{')` but not `value.startsWith('[')`, so when a row contained a non-string value (number, boolean, etc.) the second `startsWith` call threw:

```
TypeError: s.startsWith is not a function
```

The fix moves the `typeof` check to guard both `startsWith` calls:

```diff
- (typeof value === 'string' && value.startsWith('{')) ||
- value.startsWith('[')
+ typeof value === 'string' &&
+ (value.startsWith('{') || value.startsWith('['))
```

### How to test locally or on Vercel

1. Open any Search page with results containing non-string column values (e.g. numeric or boolean fields).
2. Hover over a row and click the "Copy entire row as JSON" button (copy icon).
3. Verify the row is copied as valid JSON to the clipboard without a console error.

### References

- Related issue: HDX-4023


<div><a href="https://cursor.com/agents/bc-7aa1e5f4-ced5-4ee0-9585-4f473c5fca69"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-7aa1e5f4-ced5-4ee0-9585-4f473c5fca69"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



Co-authored-by: Cursor Agent <199161495+cursoragent@users.noreply.github.com>
2026-04-14 22:05:23 +00:00
Drew Davis
cc714f909a
fix: Skip rendering empty SQL dashboard filter (#2115)
## Summary

This PR fixes a bug causing the $__filters macro to error when the dashboard's global SQL filter is an empty string or whitespace only.

### Screenshots or video

Before: an empty string in the SQL filter box would cause $__filters to render as `(())`

<img width="1708" height="1042" alt="Screenshot 2026-04-14 at 1 29 41 PM" src="https://github.com/user-attachments/assets/0a4068ad-eaf2-4bf7-b894-b796f642b59a" />


After: the empty SQL filter is ignored

<img width="2330" height="520" alt="Screenshot 2026-04-14 at 1 29 20 PM" src="https://github.com/user-attachments/assets/a1e83091-5522-4745-b717-f1bf51c84a80" />


### How to test locally or on Vercel

This can be tested in the preview environment

### References



- Linear Issue: Closes HDX-4017
- Related PRs:
2026-04-14 20:04:58 +00:00
Warren Lee
28f374ef12
[HDX-3929] Migrate OTel Collector build to use OCB (OpenTelemetry Collector Builder) (#2109) 2026-04-14 12:04:20 -07:00
dependabot[bot]
28b81dd8a9
chore(deps): bump @hono/node-server from 1.19.12 to 1.19.13 (#2111)
Bumps [@hono/node-server](https://github.com/honojs/node-server) from 1.19.12 to 1.19.13.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/honojs/node-server/releases"><code>@​hono/node-server</code>'s releases</a>.</em></p>
<blockquote>
<h2>v1.19.13</h2>
<h2>Security Fix</h2>
<p>Fixed an issue in Serve Static Middleware where inconsistent handling of repeated slashes (<code>//</code>) between the router and static file resolution could allow middleware to be bypassed. Users of Serve Static Middleware are encouraged to upgrade to this version.</p>
<p>See GHSA-92pp-h63x-v22m for details.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="fd64e659a3"><code>fd64e65</code></a> 1.19.13</li>
<li><a href="025c30f55d"><code>025c30f</code></a> Merge commit from fork</li>
<li>See full diff in <a href="https://github.com/honojs/node-server/compare/v1.19.12...v1.19.13">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@hono/node-server&package-manager=npm_and_yarn&previous-version=1.19.12&new-version=1.19.13)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/hyperdxio/hyperdx/network/alerts).

</details>
2026-04-14 18:12:15 +00:00
Warren Lee
418f70c516
[HDX-3964] Add event pattern mining to CLI (Shift+P) (#2106)
## Summary

Adds a pattern mining feature to the CLI, accessible via `Shift+P`. This mirrors the web app's Pattern Table functionality but runs entirely in TypeScript — no Pyodide/Python WASM needed.

**Linear:** https://linear.app/hyperdx/issue/HDX-3964

## What changed

### 1. Drain library in common-utils (`packages/common-utils/src/drain/`)

Ported the [browser-drain](https://github.com/DeploySentinel/browser-drain) TypeScript library into `@hyperdx/common-utils`. This is a pure TypeScript implementation of the Drain3 log template mining algorithm, including:

- `TemplateMiner` / `TemplateMinerConfig` — main API
- `Drain` — core algorithm with prefix tree and LRU cluster cache
- `LogMasker` — regex-based token masking (IPs, numbers, etc.)
- `LruCache` — custom LRU cache matching Python Drain3's eviction semantics
- 11 Jest tests ported from the original `node:test` suite

### 2. CLI pattern view (`packages/cli/src/components/EventViewer/`)

**Keybinding:** `Shift+P` toggles pattern view (pauses follow mode, restores on exit)

**Data flow (mirrors web app's `useGroupedPatterns`):**
- Issues `SELECT ... ORDER BY rand() LIMIT 100000` to randomly sample up to 100K events
- Issues parallel `SELECT count()` to get true total event count
- Feeds sampled log bodies through the TypeScript `TemplateMiner`
- Estimates pattern counts via `sampleMultiplier = totalCount / sampledRowCount`
- Computes time-bucketed trend data per pattern

**UI:**
- Pattern list with columns: Est. Count (with `~` prefix), Pattern
- `l`/`Enter` expands a pattern to show its sample events (full table columns)
- `h`/`Esc` returns to pattern list
- `j/k/G/g/Ctrl+D/Ctrl+U` navigation throughout
- Loading spinner while sampling query runs

**Alias fix:** Pattern and count queries compute `WITH` clauses from the source's `defaultTableSelectExpression` so Lucene searches using aliases (e.g. `level:error` where `level` is an alias for `SeverityText`) resolve correctly.

### New files
- `packages/common-utils/src/drain/` — 7 source files + barrel index
- `packages/common-utils/src/__tests__/drain.test.ts`
- `packages/cli/src/components/EventViewer/usePatternData.ts`
- `packages/cli/src/components/EventViewer/PatternView.tsx`
- `packages/cli/src/components/EventViewer/PatternSamplesView.tsx`

### Modified files
- `packages/cli/src/api/eventQuery.ts` — added `buildPatternSampleQuery`, `buildTotalCountQuery`, `buildAliasWithClauses`
- `packages/cli/src/components/EventViewer/EventViewer.tsx` — wired in pattern state + rendering
- `packages/cli/src/components/EventViewer/useKeybindings.ts` — added P, l, h keybindings + pattern/sample navigation
- `packages/cli/src/components/EventViewer/SubComponents.tsx` — added P to help screen

### Demo

https://github.com/user-attachments/assets/50a2edfc-8891-43ae-ab86-b96fca778c66
2026-04-14 18:03:56 +00:00
dependabot[bot]
7a49c74835
chore(deps): bump hono from 4.12.9 to 4.12.12 (#2112)
Bumps [hono](https://github.com/honojs/hono) from 4.12.9 to 4.12.12.
e notes</summary>
<p><em>Sourced from <a href="https://github.com/honojs/hono/releases">hono's releases</a>.</em></p>
<blockquote>
<h2>v4.12.12</h2>
<h2>Security fixes</h2>
<p>This release includes fixes for the following security issues:</p>
<h3>Middleware bypass via repeated slashes in serveStatic</h3>
<p>Affects: Serve Static middleware. Fixes a path normalization inconsistency where repeated slashes (<code>//</code>) could bypass route-based middleware protections and allow access to protected static files. GHSA-wmmm-f939-6g9c</p>
<h3>Path traversal in toSSG() allows writing files outside the output directory</h3>
<p>Affects: <code>toSSG()</code> for Static Site Generation. Fixes a path traversal issue where crafted <code>ssgParams</code> values could write files outside the configured output directory. GHSA-xf4j-xp2r-rqqx</p>
<h3>Incorrect IP matching in ipRestriction() for IPv4-mapped IPv6 addresses</h3>
<p>Affects: IP Restriction Middleware. Fixes improper handling of IPv4-mapped IPv6 addresses (e.g. <code>::ffff:127.0.0.1</code>) that could cause allow/deny rules to be bypassed. GHSA-xpcf-pg52-r92g</p>
<h3>Missing validation of cookie name on write path in setCookie()</h3>
<p>Affects: <code>setCookie()</code>, <code>serialize()</code>, and <code>serializeSigned()</code> from <code>hono/cookie</code>. Fixes missing validation of cookie names on the write path, preventing inconsistent handling between parsing and serialization. GHSA-26pp-8wgv-hjvm</p>
<h3>Non-breaking space prefix bypass in cookie name handling in getCookie()</h3>
<p>Affects: <code>getCookie()</code> from <code>hono/cookie</code>. Fixes a discrepancy in cookie name handling that could allow attacker-controlled cookies to override legitimate ones and bypass prefix protections. GHSA-r5rp-j6wh-rvv4</p>
<hr />
<p>Users who use Serve Static, Static Site Generation, Cookie utilities, or IP restriction middleware are strongly encouraged to upgrade to this version.</p>
<h2>v4.12.11</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(css): add classNameSlug option to createCssContext by <a href="https://github.com/flow-pie"><code>@​flow-pie</code></a> in <a href="https://redirect.github.com/honojs/hono/pull/4834">honojs/hono#4834</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/flow-pie"><code>@​flow-pie</code></a> made their first contribution in <a href="https://redirect.github.com/honojs/hono/pull/4834">honojs/hono#4834</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/honojs/hono/compare/v4.12.10...v4.12.11">https://github.com/honojs/hono/compare/v4.12.10...v4.12.11</a></p>
<h2>v4.12.10</h2>
<h2>What's Changed</h2>
<ul>
<li>test(router): fix <code>Simple capturing group</code> test by <a href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a href="https://redirect.github.com/honojs/hono/pull/4838">honojs/hono#4838</a></li>
<li>docs: fix impaired -&gt; inspired typo in benchmark READMEs by <a href="https://github.com/Abhi3975"><code>@​Abhi3975</code></a> in <a href="https://redirect.github.com/honojs/hono/pull/4843">honojs/hono#4843</a></li>
<li>fix(jsx/dom): apply select value after children are rendered by <a href="https://github.com/usualoma"><code>@​usualoma</code></a> in <a href="https://redirect.github.com/honojs/hono/pull/4847">honojs/hono#4847</a></li>
<li>fix(compress): convert strong ETag to weak ETag when compressing by <a href="https://github.com/usualoma"><code>@​usualoma</code></a> in <a href="https://redirect.github.com/honojs/hono/pull/4848">honojs/hono#4848</a></li>
<li>docs(ip-restriction): add clear JSDoc examples and param types by <a href="https://github.com/VISHNU7KASIREDDY"><code>@​VISHNU7KASIREDDY</code></a> in <a href="https://redirect.github.com/honojs/hono/pull/4851">honojs/hono#4851</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Abhi3975"><code>@​Abhi3975</code></a> made their first contribution in <a href="https://redirect.github.com/honojs/hono/pull/4843">honojs/hono#4843</a></li>
<li><a href="https://github.com/VISHNU7KASIREDDY"><code>@​VISHNU7KASIREDDY</code></a> made their first contribution in <a href="https://redirect.github.com/honojs/hono/pull/4851">honojs/hono#4851</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="c37ba26da9"><code>c37ba26</code></a> 4.12.12</li>
<li><a href="cc067c8559"><code>cc067c8</code></a> Merge commit from fork</li>
<li><a href="a586cd72e3"><code>a586cd7</code></a> Merge commit from fork</li>
<li><a href="48fa2233bc"><code>48fa223</code></a> Merge commit from fork</li>
<li><a href="b470278920"><code>b470278</code></a> Merge commit from fork</li>
<li><a href="9aff14bd72"><code>9aff14b</code></a> Merge commit from fork</li>
<li><a href="2c403c67eb"><code>2c403c6</code></a> 4.12.11</li>
<li><a href="f82aba8e8e"><code>f82aba8</code></a> feat(css): add classNameSlug option to createCssContext (<a href="https://redirect.github.com/honojs/hono/issues/4834">#4834</a>)</li>
<li><a href="9f374a55b2"><code>9f374a5</code></a> 4.12.10</li>
<li><a href="a8c56a6620"><code>a8c56a6</code></a> docs(ip-restriction): add clear JSDoc examples and param types (<a href="https://redirect.github.com/honojs/hono/issues/4851">#4851</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/honojs/hono/compare/v4.12.9...v4.12.12">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hono&package-manager=npm_and_yarn&previous-version=4.12.9&new-version=4.12.12)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/hyperdxio/hyperdx/network/alerts).

</details>
2026-04-14 17:55:07 +00:00
Brandon Pereira
9781ae6387
feat: integrate Model Context Protocol (MCP) server for dashboards & investigations (#2030)
## Summary

Adds an MCP (Model Context Protocol) server to the HyperDX API, enabling AI assistants (Claude, Cursor, OpenCode, etc.) to query observability data, manage dashboards, and explore data sources directly via standardized tool calls.

Key changes:
- **MCP server** (`packages/api/src/mcp/`) — Streamable HTTP transport at `/api/mcp`, authenticated via Personal API Access Key
- **Tools** — `hyperdx_list_sources`, `hyperdx_query`, `hyperdx_get_dashboard`, `hyperdx_save_dashboard`, `hyperdx_delete_dashboard`, `hyperdx_query_tile`
- **Dashboard prompts** — Detailed prompt templates that guide LLMs in generating valid, high-quality dashboards
- **Shared logic** — Refactored dashboard validation/transformation out of the external API router into reusable utils (`packages/api/src/routers/external-api/v2/utils/dashboards.ts`)
- **Documentation** — `MCP.md` with setup instructions for Claude Code, OpenCode, Cursor, MCP Inspector, and other clients
- **Tests** — Unit tests for dashboard tools, query tools, tracing, and response trimming

### Screenshots

https://github.com/user-attachments/assets/8c5aa582-c79e-47e0-8f75-e03feabdf8a6

### How to test locally

1. Start the dev stack: `yarn dev`
2. Connect an MCP client (e.g. MCP Inspector):
   ```bash
   cd packages/api && yarn dev:mcp
   ```
   Then configure the inspector:
   - **Transport Type:** Streamable HTTP
   - **URL:** `http://localhost:8080/api/mcp`
   - **Header:** `Authorization: Bearer <your-personal-access-key>`
   - Click **Connect**
3. Alternatively, connect via Claude Code or OpenCode:
   ```bash
   claude mcp add --transport http hyperdx http://localhost:8080/api/mcp \
     --header "Authorization: Bearer <your-personal-access-key>"
   ```
4. Try listing sources, querying data, or creating/updating a dashboard through the connected AI assistant.
5. Run unit tests:
   ```bash
   cd packages/api && yarn ci:unit
   ```

### References

- Linear Issue: HDX-3710
2026-04-14 14:39:07 +00:00
Warren Lee
fe3ab41c43
[HDX-3978] Add 'o' keybinding to open trace/span in browser from TUI (#2105)
## Summary

Adds an `o` keybinding to the TUI that opens the currently expanded row in the HyperDX web app browser, deep-linking directly to the same view with the side panel open.

**Linear ticket:** https://linear.app/hyperdx/issue/HDX-3978

## What changed

When a user expands a row in the TUI (via `l`/Enter) and presses `o`, the browser opens to the HyperDX web app's `/search` page with URL parameters that reproduce the exact TUI state:

| URL Parameter | Description |
|---|---|
| `source` | Active source ID |
| `where` | User's search query + `TraceId:<id>` for traces |
| `from` / `to` | Current time range |
| `rowWhere` | SQL WHERE clause identifying the specific row (double-encoded) |
| `rowSource` | Source ID for the expanded row |
| `sidePanelTab` | Maps TUI tab: `overview` → `overview`, `columns` → `parsed`, `trace` → `trace` |
| `eventRowWhere` | Pre-selects the specific span in the trace waterfall (trace tab only, double-encoded JSON) |

### Tab behavior

- **Overview tab** → Opens side panel to Overview for the specific row
- **Column Values tab** → Opens side panel to Parsed (Column Values) for the specific row  
- **Trace tab** → Opens side panel to Trace with waterfall, pre-selecting the highlighted span

### Source support

- **Trace sources**: URL includes `TraceId` filter combined with the user's search query
- **Log sources with trace correlation**: Full trace waterfall deep-linking supported
- **Log sources without trace**: Opens with the user's search query and row identification (no trace filter)

## Files changed

| File | Change |
|---|---|
| `packages/cli/src/utils/openUrl.ts` | **New** — Cross-platform browser open utility |
| `packages/cli/src/api/eventQuery.ts` | `buildFullRowQuery` returns `rowWhere` alongside `ChSql` |
| `packages/cli/src/components/EventViewer/useEventData.ts` | Exposes `expandedRowWhere` state |
| `packages/cli/src/components/TraceWaterfall/types.ts` | Added `onSelectedNodeChange` callback prop |
| `packages/cli/src/components/TraceWaterfall/TraceWaterfall.tsx` | Calls `onSelectedNodeChange` when selected span changes |
| `packages/cli/src/components/EventViewer/DetailPanel.tsx` | Threads `onTraceSelectedNodeChange` to TraceWaterfall |
| `packages/cli/src/components/EventViewer/types.ts` | Added `appUrl` to `EventViewerProps` |
| `packages/cli/src/components/EventViewer/EventViewer.tsx` | Threads `appUrl`, `expandedRowWhere`, `traceSelectedNode`, `submittedQuery` |
| `packages/cli/src/components/EventViewer/utils.ts` | Added `buildBrowserUrl` (full URL builder) and `buildSpanEventRowWhere` |
| `packages/cli/src/components/EventViewer/useKeybindings.ts` | Added `o` keybinding with all new params |
| `packages/cli/src/components/EventViewer/SubComponents.tsx` | Added `o` to help screen |
| `packages/cli/src/App.tsx` | Passes `appUrl` to EventViewer |
| `packages/cli/AGENTS.md` | Added `o` to keybindings table |

## Testing

- Type check: `npx tsc --noEmit` passes
- Lint: `yarn lint:fix` — 0 errors
2026-04-13 22:29:41 +00:00
Warren Lee
07bb29e997
[HDX-3963] Optimize event detail and trace waterfall queries (#2104)
## Summary

Optimizes the queries powering the event details panel and trace waterfall chart in the TUI, and improves the trace waterfall UX.

Fixes HDX-3963
Linear: https://linear.app/clickhouse/issue/HDX-3963

### Query optimizations

**Full row fetch (`SELECT *`)** — Removed the 1-year `dateRange` from `buildFullRowQuery`. The WHERE clause already uniquely identifies the row, so ClickHouse can use the filter directly without scanning time partitions. Matches the web frontend's `useRowData` pattern in `DBRowDataPanel.tsx`.

**Trace waterfall queries** — Replaced raw SQL builders (`buildTraceSpansSql`/`buildTraceLogsSql`) with `renderChartConfig`-based async builders. This enables:
- Time partition pruning via a tight ±1h `dateRange` derived from the event timestamp
- Materialized field optimization
- Query parameterization

**Trace span detail fetch** — Replaced raw SQL `SELECT * FROM ... WHERE ... LIMIT 1` with `renderChartConfig` via `buildTraceRowDetailConfig`, omitting `dateRange`/`timestampValueExpression` so ClickHouse uses the WHERE clause directly.

**Shared logic** — Extracted all trace config construction into `packages/cli/src/shared/traceConfig.ts`.

### UX improvements

**Trace Event Details → dedicated page** — Previously Event Details was rendered inline below the waterfall, consuming screen space. Now:
- Waterfall view gets the full terminal height
- Press `l`/`Enter` to drill into a span's full Event Details
- Press `h`/`Esc` to return to the waterfall
- `Ctrl+D/U` scrolls the detail view using full terminal height

**Trace waterfall scrolling** — Previously the waterfall truncated at `maxRows` with no way to see remaining spans. Now `j`/`k` scrolls the viewport when the cursor reaches the edge, with a scroll position indicator showing spans above/below.

### Files changed

| File | Change |
|---|---|
| `packages/cli/src/shared/traceConfig.ts` | NEW — shared trace config builders |
| `packages/cli/src/api/eventQuery.ts` | Replace raw SQL with `renderChartConfig`; remove date range from full row fetch |
| `packages/cli/src/components/TraceWaterfall/TraceWaterfall.tsx` | Split into waterfall + detail views; add scroll support |
| `packages/cli/src/components/TraceWaterfall/useTraceData.ts` | Use async builders + parameterized queries |
| `packages/cli/src/components/TraceWaterfall/types.ts` | Add `metadata`, `eventTimestamp`, `detailExpanded` props |
| `packages/cli/src/components/EventViewer/useKeybindings.ts` | Add `l`/`h` keybindings for trace detail navigation |
| `packages/cli/src/components/EventViewer/DetailPanel.tsx` | Thread `metadata`, `eventTimestamp`, `traceDetailExpanded` |
| `packages/cli/src/components/EventViewer/EventViewer.tsx` | Add `traceDetailExpanded` state; pass new props |
2026-04-13 21:39:41 +00:00
Brandon Pereira
a5869f0eb7
[HDX-3908] Dedupe source validation issue toasts (#2103)
## Summary

Source validation toasts were emitted from `useSources()` on every query refresh for each invalid source, and each notification had no stable `id`. Because the notifications are persistent (`autoClose: false`), repeated refreshes stacked many identical toasts and made the Sources form hard to use.

This change adds deterministic toast IDs per source validation error so repeated emissions update the same toast instead of creating duplicates.

- Added `getSourceValidationNotificationId(sourceId)` in `source.ts`
- Updated source validation notifications in `useSources()` to pass `id`
- Added a unit regression test that executes the query function twice and verifies the same notification `id` is reused
- Added a patch changeset for `@hyperdx/app`

### Screenshots or video

[hdx_3908_validation_toast_dedupe_demo.mp4](https://cursor.com/agents/bc-28804425-1ca7-4133-91d8-94acfd659f05/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fhdx_3908_validation_toast_dedupe_demo.mp4)

[Single source validation toast after repeated refreshes](https://cursor.com/agents/bc-28804425-1ca7-4133-91d8-94acfd659f05/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fhdx_3908_single_validation_toast_after_refreshes.webp)

### How to test locally or on Vercel

1. Run `yarn ci:unit src/__tests__/source.test.ts` inside `packages/app`.
2. Confirm `useSources validation notifications` test passes.
3. Open Team Settings → Sources with an invalid source and refresh repeatedly; verify only one validation toast remains visible per source.

### References

- Linear Issue: HDX-3908
- Related PRs: none

<sub>To show artifacts inline, <a href="https://cursor.com/dashboard/cloud-agents#team-pull-requests">enable</a> in settings.</sub>


<div><a href="https://cursor.com/agents/bc-28804425-1ca7-4133-91d8-94acfd659f05"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-28804425-1ca7-4133-91d8-94acfd659f05"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



Co-authored-by: Cursor Agent <199161495+cursoragent@users.noreply.github.com>
2026-04-13 20:47:59 +00:00
Drew Davis
085f30743e
feat: Implement alerting for Raw SQL-based dashboard tiles (#2073)
## Summary

This PR implements alerting on Raw SQL-based line/bar charts.

- This is only available for line/bar charts with this change. Number charts will be added in a future PR.
- The threshold is compared to the _last_ numeric column in each result.
- The interval parameter must be used. This is required for line charts to function, and is used for zero-fill and other functionality within the alerts task. This limitation will be removed for Number chart alerts when those are implemented.
- Start and and end date should be used, but are not required because there are some potential use-cases where they may not be desirable.

### Screenshots or video

https://github.com/user-attachments/assets/e2d0cd6c-b040-4490-89af-6a51a7380647

Logs from Check-Alerts evaluating a raw-sql alert

<img width="1241" height="908" alt="Screenshot 2026-04-09 at 3 01 14 PM" src="https://github.com/user-attachments/assets/dbed4e5f-bf27-4179-b8e0-897cc19f3d3a" />

### How to test locally or on Vercel

This must be tested locally, as alerts are not enabled in the preview environment.

<details>
<summary>Query for the "anomaly detection" example</summary>

```sql
WITH buckets AS (
  SELECT
    $__timeInterval(Timestamp) AS ts,
    count() AS bucket_count
  FROM $__sourceTable
  WHERE TimestampTime >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) - toIntervalSecond($__interval_s * 30) -- Fetch 30 intervals back
    AND TimestampTime < fromUnixTimestamp64Milli({endDateMilliseconds:Int64})
    AND SeverityText = 'error'
  GROUP BY ts
  ORDER BY ts
  WITH FILL STEP toIntervalSecond($__interval_s)
),

anomaly_detection as (
  SELECT
    ts,
    bucket_count,
    avg(bucket_count) OVER (ORDER BY ts ROWS BETWEEN 30 PRECEDING AND 1 PRECEDING) as previous_30_avg, -- avg of previous 30 intervals
    stddevPop(bucket_count) OVER (ORDER BY ts ROWS BETWEEN 30 PRECEDING AND 1 PRECEDING) as previous_30_stddev, -- standard deviation of previous 30 intervals
    greatest(bucket_count - (previous_30_avg + 2 * previous_30_stddev), 0) as excess_over_2std -- compare bucket to avg + 2 stddev. clamp at 0.
  FROM buckets
)

SELECT ts, excess_over_2std 
FROM anomaly_detection
WHERE ts >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) AND ts < fromUnixTimestamp64Milli({endDateMilliseconds:Int64})
```

</details>

### References



- Linear Issue: HDX-1605
- Related PRs:
2026-04-13 17:58:22 +00:00
Brandon Pereira
0bfec14830
[HDX-3981] Upgrade Mantine v7 to v9 (#2096)
## Summary

Upgrade all `@mantine/*` packages from v7.17.8 to v9.0.0 (skipping the v8 intermediate step since the breaking changes were manageable in a single pass). This improves React 19 compatibility and keeps the UI library current.

### Breaking changes resolved

**v7 → v8 changes:**
- `DateTimePicker`/`DateInput` `onChange` now returns a date string instead of a `Date` object — updated handlers in `AlertScheduleFields.tsx` and `SourceForm.tsx`
- Updated `postcss-preset-mantine` to ^1.18.0

**v8 → v9 changes:**
- `Collapse`: renamed `in` prop to `expanded` (11 instances across 10 files)
- `Grid`: removed deprecated `overflow="hidden"` prop (5 instances, 3 files) — v9 uses native CSS `gap` instead of negative margins
- `Text`/`Anchor`: renamed `color` prop to `c` style prop (7 instances, 5 files)
- `SourceSchemaPreview`: replaced removed `TextProps` `color` key with `React.CSSProperties`
- Theme: set `defaultRadius: 'sm'` in both theme configs to preserve existing visual appearance (v9 changed default from `sm` to `md`)
- Updated test for Collapse visibility behavior change in jsdom

### Not affected (verified)
- No `@mantine/carousel` or `@mantine/tiptap` usage — embla and Tiptap migrations not needed
- No `TypographyStylesProvider`, `Spoiler`, `positionDependencies`, `useFullscreen`, `useMouse`, `useMutationObserver`, or `zodResolver` from `@mantine/form` usage
- All `useLocalStorage` calls from `@mantine/hooks` already provide `defaultValue`
- `react-hook-form-mantine` has a peer dep mismatch (expects `@mantine/core ^7.0.0`) but its thin wrappers are compatible at runtime

### How to test locally or on Vercel

1. Start the dev stack with `yarn dev`
2. Navigate through key pages: search, dashboards, alerts, services dashboard, Kubernetes pages
3. Verify Collapse animations work correctly (alert details, nav sub-menus, advanced settings)
4. Verify Grid layouts render properly on services dashboard side panels
5. Verify no visual regressions in border-radius, spacing, or color across components

### References

- Linear Issue: https://linear.app/clickhouse/issue/HDX-3981/upgrade-mantine-v7-v9
- [Mantine v9 Changelog](https://mantine.dev/changelog/9-0-0/)
- [7.x → 8.x Migration Guide](https://mantine.dev/guides/7x-to-8x/)
- [8.x → 9.x Migration Guide](https://mantine.dev/guides/8x-to-9x/)
2026-04-13 16:03:29 +00:00
Warren Lee
4dea3621a7
[HDX-3976] CLI: migrate from apiUrl to appUrl with interactive login flow (#2101)
## Summary

Migrates the CLI from using API URLs (`-s, --server`) to app URLs (`-a, --app-url`), and adds interactive login prompts on expired/missing sessions.

Linear: https://linear.app/clickhouse/issue/HDX-3976

---

## Breaking Change

**The `-s` / `--server` flag has been removed.** All commands (except `upload-sourcemaps`) now use `-a` / `--app-url` instead.

The URL semantics have changed: users should now provide the **HyperDX app URL** (e.g. `http://localhost:8080`), not the API URL. The CLI derives the API URL internally by appending `/api`.

| Before | After |
|---|---|
| `hdx auth login -s http://localhost:8080/api` | `hdx auth login -a http://localhost:8080` |
| `hdx tui -s http://localhost:8080/api` | `hdx tui -a http://localhost:8080` |
| `hdx sources -s http://localhost:8080/api` | `hdx sources -a http://localhost:8080` |

> **Note:** `upload-sourcemaps` is unchanged — it still uses `--apiUrl` / `-u` as before.

**Existing saved sessions are auto-migrated** — old `session.json` files with `apiUrl` are converted to `appUrl` on first load.

---

## Changes

### `apiUrl` → `appUrl` migration
- `ApiClient` now accepts `appUrl` and derives `apiUrl` by appending `/api`
- `SessionConfig` stores `appUrl`; legacy sessions with `apiUrl` auto-migrate on load
- All commands use `-a, --app-url` instead of `-s, --server`

### Interactive login flow (HDX-3976)
- `hdx auth login` no longer requires `-a` — it prompts interactively for login method, app URL, then credentials
- Login method selector is extensible (currently Email/Password, designed for future OAuth support)
- **Expired sessions now prompt for re-login** instead of printing an error and exiting
- The app URL field is autofilled with the last used value so users can just hit Enter
- No longer requires manual deletion of `~/.config/hyperdx/cli/session.json` to recover from expired sessions
- Non-TUI commands (`sources`, `dashboards`, `query`) also launch interactive login on expired/missing sessions via `ensureSession()` helper

### TUI (`App.tsx`)
- Detects expired session on mount and shows "Session expired" message with editable URL field
- If the user changes the URL during re-login, the client is recreated
- 401/403 errors during data loading bounce back to the login screen instead of showing raw error messages

### Input validation & error handling
- App URL inputs are validated — rejects non-`http://` or `https://` URLs with a clear inline error
- `ApiClient.login()` catches network/URL errors and returns `false` instead of crashing
- `ApiClient.login()` verifies the session after a 302/200 response by calling `checkSession()` — prevents false "Logged in" messages from servers that return 302 without a valid session (e.g. SSO redirects)
- Login failure messages now mention both credentials and server URL

---

## Files changed

- `packages/cli/src/api/client.ts` — accepts `appUrl`, derives `apiUrl`, exposes `getAppUrl()`, login validation
- `packages/cli/src/utils/config.ts` — `SessionConfig.appUrl`, backward-compat migration
- `packages/cli/src/cli.tsx` — `-a` flag, `LoginPrompt`, `ReLoginPrompt`, `ensureSession()`, URL validation
- `packages/cli/src/App.tsx` — expired session detection, editable URL on re-login, 401/403 handling
- `packages/cli/src/components/LoginForm.tsx` — app URL prompt field, `message` prop, URL validation
2026-04-10 22:45:48 +00:00
Warren Lee
7a9882d421
[HDX-3969] Add alerts page (Shift+A) with overview and recent history (#2093) 2026-04-10 11:36:38 -07:00
github-actions[bot]
bfb455d90a
Release HyperDX (#2094)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 11:08:49 -07:00
Warren Lee
bdca9bd4db
[HDX-3966] Improve TUI error message rendering and add SQL preview (#2095)
## Summary

Improves error message rendering in the CLI/TUI with visible highlighting, structured error display matching the web frontend patterns, and adds a SQL preview feature.

**Linear:** https://linear.app/hyperdx/issue/HDX-3966

## Changes

### Error Display
- **`ErrorDisplay` component** — Reusable error/warning display with bordered boxes, color-coded severity (red/yellow), icons, and responsive rendering based on terminal height (compact < 20 rows, medium 20-35, full > 35)
- **`parseError` utility** — Parses ClickHouse `DB::Exception` strings and HTML error responses into clean messages; accepts `Error | ClickHouseQueryError | string`
- **`useSqlSuggestions`** — Copied from `packages/app/src/hooks/useSqlSuggestions.tsx` to `packages/cli/src/shared/` for detecting common ClickHouse query mistakes (e.g. double quotes)

### Error Object Preservation
- **`useEventData` / `useTraceData`** — Error state changed from `string | null` to `Error | null`, preserving `ClickHouseQueryError` instances with their `.query` property through the entire chain
- **Row detail errors** — Added `expandedRowError` state (replaces `__fetch_error` string embedding in row data) and `selectedRowError` for trace span details
- **Pagination errors** — Now surfaced instead of silently swallowed

### SQL Preview (Shift-D)
- Press `D` to view the generated ClickHouse SQL for the current context
- Context-aware: shows table query, expanded row SELECT *, or trace spans query depending on which view is active
- Uses `parameterizedQueryToSql` from common-utils to resolve query parameters
- Scrollable with Ctrl+D/U for long queries
- Renders as an overlay (display=none) to preserve component state — no re-fetch when toggling

### Other Fixes
- Follow mode (`f` key) disabled in event detail panel (was incorrectly toggleable)
- All error display sites updated: TableView, DetailPanel, TraceWaterfall, Footer, App, LoginForm

## Files Created
| File | Purpose |
|------|---------|
| `packages/cli/src/components/ErrorDisplay.tsx` | Reusable error/warning display component |
| `packages/cli/src/utils/parseError.ts` | Error message parser (ClickHouse, HTML) |
| `packages/cli/src/shared/useSqlSuggestions.ts` | SQL suggestion engines (copied from app) |

## Files Modified
| File | Change |
|------|--------|
| `useEventData.ts` | Error objects, expandedRowError, lastChSql/lastExpandedChSql as state |
| `useTraceData.ts` | Error objects, selectedRowError, eager traceQuery via useMemo |
| `EventViewer.tsx` | showSql overlay, activeChSql, traceChSql state |
| `useKeybindings.ts` | D keybinding, showSql handling, f key guard |
| `SubComponents.tsx` | SqlPreviewScreen, Footer with ErrorDisplay, HelpScreen update |
| `TableView.tsx` | Error object + searchQuery props |
| `DetailPanel.tsx` | expandedRowError prop, onTraceChSqlChange |
| `TraceWaterfall.tsx` | onChSqlChange callback, selectedRowError display |
| `types.ts` (TraceWaterfall) | onChSqlChange prop |
| `App.tsx` | ErrorDisplay |
| `LoginForm.tsx` | ErrorDisplay |

## Testing
- `tsc --noEmit` — passes
- `yarn build` — passes
- `npx lint-staged` — passes
- Manual testing of all error display paths and SQL preview across tabs

## Demo 
<img width="1169" height="322" alt="image" src="https://github.com/user-attachments/assets/b0580f0a-c226-4297-9937-f263afd39f6a" />

<img width="1165" height="250" alt="image" src="https://github.com/user-attachments/assets/d7139a91-0c2e-4b60-b637-3e900447d3fa" />
2026-04-10 17:12:10 +00:00
Drew Davis
0daa52993c
feat: Improve local-mode deeplinking (#2080)
## Summary

When `IS_LOCAL_MODE` sources are created (e.g. via "Connect to Demo Server" in the onboarding modal), IDs are now derived from a stable hash of the item content instead of `Math.random()`. This ensures every user who connects to the same demo server gets the same source IDs, making deep links with `?source=<id>` work across different users and sessions.

This has the added benefit of ensuring that the dashboards created in local mode will not break due to new sources being created on every new session.

Further, the `<OnboardingModal>` has been added to a few pages where it was previous missing, ensuring that users which are deeplinked to those pages see the modal and are able to create the demo connection.

### How to test locally or on Vercel

1. Open the play/demo environment in two separate browser profiles (or incognito windows) so each has fresh localStorage
2. In the first, connect to the demo server, then navigate to a page where a source is selected
3. Copy the link into the second browser. You should see the "connect to demo server" and once connected you should see the same source as was selected in the 1st browser.

### References

- Linear Issue: Closes HDX-3974
- Related PRs: none
2026-04-10 15:21:04 +00:00
Warren Lee
f8f7634552
Fix release CLI workflow: build common-utils before compilation (#2086)
The "Release CLI Binaries" job fails because `bun build --compile` can't resolve `@hyperdx/common-utils/dist/*` imports. In CI it's a fresh checkout — `yarn install` puts source code in place but `common-utils` needs to be compiled (tsup) to produce its `dist/` output.

Adds a `make ci-build` step between `yarn install` and the CLI compile step.

Fixes https://github.com/hyperdxio/hyperdx/actions/runs/24220292098/job/70710194631
2026-04-10 15:04:09 +00:00
Drew Davis
587819b63e
Add Cursor Cloud specific instructions to AGENTS.md (#2081)
## What

Adds a `## Cursor Cloud specific instructions` section to `AGENTS.md` with development environment notes for cloud agents.

## Why

Future Cursor Cloud agents need non-obvious context about running the dev stack in the cloud VM environment, particularly:

- **Docker requirement**: Docker must be installed and running before `yarn dev` or integration/E2E tests
- **dash/bash incompatibility**: `yarn dev` uses `sh -c` to source `scripts/dev-env.sh` which contains bash-specific syntax (`BASH_SOURCE`). On Ubuntu where `/bin/sh` is `dash`, this fails. A bash workaround command is documented.
- **Port mapping**: The default `/workspace` directory produces slot 76, so services run on non-standard ports (App: 30276, API: 30176, etc.)
- **First-time registration**: Fresh MongoDB means the app shows a registration page with no external auth needed

## Evidence

Full dev stack running with all services operational:

[hyperdx_register_and_search_demo.mp4](https://cursor.com/agents/bc-b3139182-3221-4ea1-9a02-ac2df5e9b062/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fhyperdx_register_and_search_demo.mp4)

[HyperDX logged in search page](https://cursor.com/agents/bc-b3139182-3221-4ea1-9a02-ac2df5e9b062/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fhyperdx_logged_in_search.webp)

[HyperDX search query executed](https://cursor.com/agents/bc-b3139182-3221-4ea1-9a02-ac2df5e9b062/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fhyperdx_search_query.webp)

<sub>To show artifacts inline, <a href="https://cursor.com/dashboard/cloud-agents#team-pull-requests">enable</a> in settings.</sub>


<div><a href="https://cursor.com/agents/bc-b3139182-3221-4ea1-9a02-ac2df5e9b062"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-b3139182-3221-4ea1-9a02-ac2df5e9b062"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



Co-authored-by: Cursor Agent <199161495+cursoragent@users.noreply.github.com>
2026-04-10 14:54:52 +00:00
Mike Shi
4ef63cc434
feat: Auto-detect trace duration and render in adaptive time units (HDX-3909) (#2046)
## Summary

When a chart uses a trace source with a Duration Expression, the chart now automatically defaults to adaptive time unit formatting (e.g., `120.41s`, `45ms`, `3µs`) instead of requiring users to manually select a format. Users can still override the format through the existing display settings.

**Key changes:**

1. **New `duration` output type** in `NumberFormatSchema` — renders values adaptively as `µs`, `ms`, `s`, `min`, or `h` based on magnitude, instead of the clock-style `hh:mm:ss` format
2. **Auto-detection via exact match** — `getTraceDurationNumberFormat()` checks if any chart select `valueExpression` exactly equals the trace source's `durationExpression`. Only applies for unit-preserving aggregate functions (`avg`, `min`, `max`, `sum`, `quantile`, `any`, `last_value`, etc.) — skips `count` and `count_distinct`
3. **`useResolvedNumberFormat()` hook** — resolves the effective `numberFormat` for a chart: returns the user's explicit format if set, otherwise auto-detects duration format for trace sources
4. **UI form update** — Added "Duration" option to the number format selector with input unit picker (seconds/ms/µs/ns)
5. **Display settings drawer** — Shows the auto-detected format by default so users can see what's being applied
6. **Heatmap support** — Updated `DBHeatmapChart` tick formatter to use `formatDurationMs` for duration-formatted values

**Components updated:** `DBTimeChart`, `DBNumberChart`, `DBListBarChart`, `DBPieChart`, `DBTableChart`, `DBHeatmapChart`, `DBSearchHeatmapChart`, `DBEditTimeChartForm`, `ChartDisplaySettingsDrawer`

### How to test locally or on Vercel

1. Create a chart from a trace source with a unit-preserving aggFn (e.g., avg/p95/p99/min/max of the Duration column)
2. Verify the chart y-axis and tooltips now show values like `120.41s` or `45ms` instead of raw numbers
3. Open the chart display settings and verify the "Duration" output format is shown as the default
4. Change the aggFn to `count` or `count_distinct` — verify duration formatting is NOT applied
5. Change the format to something else (e.g., "Number") and verify the override persists
6. Switch back to "Duration" and pick different input units (seconds, ms, µs, ns) — the preview should update correctly
7. Check that non-trace-source charts are unaffected (no auto-detection triggers)
8. Verify the search heatmap chart for traces still shows proper duration labels
9. Reset to defaults in the display settings drawer and verify it returns to the auto-detected duration format

### References

- Linear Issue: HDX-3909



Linear Issue: [HDX-3909](https://linear.app/clickhouse/issue/HDX-3909/trace-duration-should-render-in-time-unit-by-default)

<div><a href="https://cursor.com/agents/bc-c39f9186-2593-4675-8f23-190cd148818b"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-c39f9186-2593-4675-8f23-190cd148818b"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>



Co-authored-by: Cursor Agent <199161495+cursoragent@users.noreply.github.com>
2026-04-10 11:51:43 +00:00
github-actions[bot]
edb55b4bca
Release HyperDX (#2060)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-09 17:33:14 -07:00
Warren Lee
d995b78c66
[HDX-3919] Add @hyperdx/cli package — terminal TUI, source map upload, and agent-friendly commands (#2043)
## Summary

Adds `packages/cli` (`@hyperdx/cli`) — a unified CLI for HyperDX that provides an interactive TUI for searching/tailing logs and traces, source map upload (migrated from `hyperdx-js`), and agent-friendly commands for programmatic access.

Ref: HDX-3919
Ref: HDX-3920

### CLI Commands

```
hdx tui -s <url>                    # Interactive TUI (main command)
hdx sources -s <url>                # List sources with ClickHouse schemas
hdx sources -s <url> --json         # JSON output for agents / scripts
hdx dashboards -s <url>             # List dashboards with tile summaries
hdx dashboards -s <url> --json      # JSON output for agents / scripts
hdx query --source "Logs" --sql "SELECT count() FROM default.otel_logs"
hdx upload-sourcemaps -k <key>      # Upload source maps (ported from hyperdx-js)
hdx auth login -s <url>             # Sign in (interactive or -e/-p flags)
hdx auth status                     # Show auth status
hdx auth logout                     # Clear saved session
```

### Key Features

**Interactive TUI (`hdx tui`)**
- Table view with dynamic columns derived from query results (percentage-based widths)
- Follow mode (live tail) enabled by default, auto-pauses when detail panel is open
- Vim-like keybindings: j/k navigation, G/g jump, Ctrl+D/U half-page scroll
- `/` for Lucene search, `s` to edit SELECT clause in `$EDITOR`, `t` to edit time range
- Tab to cycle between sources and saved searches
- `w` to toggle line wrap

**Detail Panel (3 tabs, full-screen with Ctrl+D/U scrolling)**
- **Overview** — Structured view: Top Level Attributes, Log/Span Attributes, Resource Attributes
- **Column Values** — Full `SELECT *` row data with `__hdx_*` aliased columns
- **Trace** — Waterfall chart (port of `DBTraceWaterfallChart` DAG builder) with correlated log events, j/k span navigation, inverse highlight, Event Details section

**Agent-friendly commands**
- `hdx sources --json` — Full source metadata with ClickHouse `CREATE TABLE` DDL, expression mappings, and correlated source IDs. Detailed `--help` describes the JSON schema for LLM consumption. Schema queries run in parallel.
- `hdx dashboards --json` — Dashboard metadata with simplified tile summaries (name, type, source, sql). Resolves source names for human-readable output.
- `hdx query --source <name> --sql <query>` — Raw SQL execution against any source's ClickHouse connection. Supports `--format` for ClickHouse output formats (JSON, JSONEachRow, CSV, etc.).

**Source map upload (`hdx upload-sourcemaps`)**
- Ported from `hyperdx-js/packages/cli` to consolidate on a single `@hyperdx/cli` package
- Authenticates via service account API key (`-k` / `HYPERDX_SERVICE_KEY` env var)
- Globs `.js` and `.js.map` files, handles Next.js route groups
- Uploads to presigned URLs in parallel with retry (3 attempts) and progress
- Modernized: native `fetch` (Node 22+), ESM-compatible, proper TypeScript types

### Architecture

```
packages/cli/
├── src/
│   ├── cli.tsx              # Commander CLI: tui, sources, dashboards, query,
│   │                        #   upload-sourcemaps, auth
│   ├── App.tsx              # Ink app shell (login → source picker → EventViewer)
│   ├── sourcemaps.ts        # Source map upload logic (ported from hyperdx-js)
│   ├── api/
│   │   ├── client.ts        # ApiClient + ProxyClickhouseClient
│   │   └── eventQuery.ts    # Query builders (renderChartConfig, raw SQL)
│   ├── components/
│   │   ├── EventViewer/     # Main TUI (9 files)
│   │   │   ├── EventViewer.tsx    # Orchestrator (state, hooks, render shell)
│   │   │   ├── types.ts           # Shared types & constants
│   │   │   ├── utils.ts           # Row formatting functions
│   │   │   ├── SubComponents.tsx  # Header, TabBar, SearchBar, Footer, HelpScreen, TableHeader
│   │   │   ├── TableView.tsx      # Table rows rendering
│   │   │   ├── DetailPanel.tsx    # Detail panel (overview/columns/trace tabs)
│   │   │   ├── useEventData.ts    # Data fetching hook
│   │   │   └── useKeybindings.ts  # Input handler hook
│   │   ├── TraceWaterfall/  # Trace chart (6 files)
│   │   │   ├── TraceWaterfall.tsx  # Orchestrator + render
│   │   │   ├── types.ts           # SpanRow, SpanNode, props
│   │   │   ├── utils.ts           # Duration/status/bar helpers
│   │   │   ├── buildTree.ts       # DAG builder (port of DBTraceWaterfallChart)
│   │   │   └── useTraceData.ts    # Data fetching hook
│   │   ├── RowOverview.tsx
│   │   ├── ColumnValues.tsx
│   │   ├── LoginForm.tsx
│   │   └── SourcePicker.tsx
│   ├── shared/              # Ported from packages/app (@source annotated)
│   └── utils/               # Config, editor, log silencing
├── AGENTS.md
├── CONTRIBUTING.md
└── README.md
```

### Tech Stack
- **Ink v6.8.0** (React 19 for terminals) + Commander.js
- **@clickhouse/client** via ProxyClickhouseClient (routes through `/clickhouse-proxy`)
- **@hyperdx/common-utils** for query generation (`renderChartConfig`, `chSqlToAliasMap`)
- **glob v13** for source map file discovery
- **tsup** for bundling (all deps bundled via `noExternal: [/.*/]`, zero runtime deps)
- **Bun 1.3.11** for standalone binary compilation
- Session stored at `~/.config/hyperdx/cli/session.json`

### CI/CD (`release.yml`)
- CLI binaries compiled for macOS ARM64, macOS x64, and Linux x64
- GitHub Release created with download instructions
- Version-change gate: skips release if `cli-v{version}` tag already exists
- `softprops/action-gh-release` pinned to full SHA (v2.6.1) for supply chain safety
- Bun pinned to `1.3.11` for reproducible builds
- npm publishing handled by changesets

### Keybindings

| Key | Action |
|---|---|
| `j/k` | Navigate rows (or spans in Trace tab) |
| `l/Enter` | Expand row detail |
| `h/Esc` | Close detail / blur search |
| `G/g` | Jump to newest/oldest |
| `Ctrl+D/U` | Scroll half-page (table, detail panels, Event Details) |
| `/` | Search (global or detail filter) |
| `Tab` | Cycle sources/searches or detail tabs |
| `s` | Edit SELECT clause in $EDITOR |
| `t` | Edit time range in $EDITOR |
| `f` | Toggle follow mode (live tail) |
| `w` | Toggle line wrap |
| `?` | Help screen |

### Demo
#### Main Search View
<img width="1004" height="1014" alt="image" src="https://github.com/user-attachments/assets/bb6a7f00-38c9-4281-9915-c71b65d852f8" />

#### Event Details Overview
<img width="1003" height="1024" alt="image" src="https://github.com/user-attachments/assets/57025fa5-fddb-452a-9320-93465538d5b2" />

#### Trace Waterfall
<img width="1004" height="1029" alt="image" src="https://github.com/user-attachments/assets/3443c898-ea0d-47f3-acc5-edb7cdd31946" />
2026-04-09 20:21:34 +00:00
Drew Davis
5de23e1988
fix: Restore some react-hook lint rules (#2084)
## Summary

This PR:

- Updates the eslint config to restore `react-hook` plugin rules which were removed in #2069. Some of them were removed in without any replacements being _enabled_ in the recommended @eslint-react ruleset
- Reverts #2074, as those lint fixes were possible only because the related rules had been removed

Rule equivalence is documented here: https://www.eslint-react.xyz/docs/migrating-from-eslint-plugin-react-hooks

You can view current effective rule states with `cd packages/app && npx eslint --inspect-config`

### How to test locally or on Vercel

Locally:

```
make ci-lint / make dev-lint
```

### References



- Linear Issue: HDX-3467
- Related PRs:
2026-04-09 19:08:23 +00:00
Drew Davis
61db3e8b43
refactor: Create TileAlertEditor component (#2085)
## Summary

This PR extracts a TileAlertEditor component for future re-use in the Raw-SQL Alert UI. The UI has been updated to make the alert section collapsible and co-locate the "Remove Alert" button within the alert section. The collapsibility will be more important in the Raw SQL case, since the Raw SQL Editor is already pretty vertically tall.

### Screenshots or video

https://github.com/user-attachments/assets/4e595fc6-06f0-4ccd-ab1f-08dcb9895c89

### How to test locally or on Vercel

This must be tested locally, since alerts are not supported in local mode.

### References



- Linear Issue: Related to HDX-1605
- Related PRs:
2026-04-09 17:21:34 +00:00
Tom Alexander
d866e99ed7
chore: don't include test files in line count changes for pr tiering (#2083)
Previously test files contributed to the line count of PRs, possibly elevating their tier unintentionally.
2026-04-09 15:47:08 +00:00
Tom Alexander
8f7d0d09c3
chore: increment max calls allowed for claude (#2082)
Allows longer sessions with the claude agent
2026-04-09 15:31:47 +00:00
Drew Davis
6ef3b48e0c
fix: Fix flaky saved search tests (#2079)
## Summary

This PR fixes failing Saved-Search E2E tests by

1. Fixing a bug that caused the saved search's source to sometime not populate when navigating directly to the saved search
2. Updating the saved search modal to wait until the create saved search mutation completes and invalidates saved searches before attempting to navigate (also, added a loading state and error notifications to the modal)
3. Updated a few test assertions to be more explicit about waiting for the saved search to load

### Screenshots or video

### How to test locally or on Vercel

### References



- Linear Issue:
- Related PRs:
2026-04-09 15:22:54 +00:00
Karl Power
337ebff054
feat: add light mode to dev portal (#2072)
## Summary

Adds a light / dark mode toggle to the dev portal.

<img width="1549" height="922" alt="Screenshot 2026-04-08 at 18 57 29" src="https://github.com/user-attachments/assets/e118d21b-6840-4db3-8309-a8af43ea698b" />
<img width="1549" height="922" alt="Screenshot 2026-04-08 at 18 57 24" src="https://github.com/user-attachments/assets/ff08c270-2ba8-4514-b46d-4b671957e8bf" />
2026-04-08 20:17:33 +00:00
Aaron Knudtson
73b746cbcf
chore: automatic fixes from the linter (#2074)
## Summary

I'm tired of seeing automatic lint fixes so I ran `make dev-lint`
2026-04-08 19:44:32 +00:00
Brandon Pereira
52986a943c
fix(app): fix session replay sub-event modal stacking and tab conflict (#2068)
## Summary

Clicking a log/error event in the session replay event list either reopened the session replay instead of showing event details, or rendered the detail panel behind the replay drawer.

Fixed this by ensuring that isSubPanel is correctly set and using the ZIndexProvider to correctly stack the contexts.

## Steps to Reproduce

From the Sessions page:

1. Go to /sessions, select a session source, open a session card
2. In the session replay drawer, wait for the event list to load
3. Click any event row (e.g. a console.error)
4. Bug A: The detail panel opens behind the session replay drawer (overlay darkens but panel is inaccessible), or 
ESC/close doesn't work correctly

From the Search page (URL conflict):

1. Go to /search, open any trace row to open the detail side panel
2. Click the Session Replay tab — this sets sidePanelTab=replay in the URL
3. In the session event list, click any event row
4. Bug B: The inner detail panel opens to the Session Replay tab again instead of event details (e.g. Overview/Trace)
2026-04-08 16:17:22 +00:00
Drew Davis
74536b3af5
chore: Add @eslint-react/eslint-plugin recommended rules (#2069) 2026-04-08 08:54:18 -04:00
Drew Davis
24767c5886
fix: Ensure correct bounds for date-based timestampValueExpr (#2066)
## Summary

This PR fixes the time filter expression generated when a source's timestamp value expression includes a Date-type column. In such cases, the bounds should be inclusive for the date-type columns. This extends the fix from #1915 to date-type columns.

### Screenshots or video

#### Before - Histogram is empty because of exclusive end bounds

<img width="1758" height="700" alt="Screenshot 2026-04-07 at 12 32 05 PM" src="https://github.com/user-attachments/assets/95898655-21f1-4380-9b23-5333fcea007f" />

#### After - Histogram is populated

<img width="1762" height="717" alt="Screenshot 2026-04-07 at 12 30 25 PM" src="https://github.com/user-attachments/assets/d159b131-1f4f-44f8-982e-b26ca68835ff" />

### How to test locally or on Vercel

The unit tests demonstrate the fix.

<details>
<summary>Alternatively, create a table with the following schema and data</summary>

```sql
CREATE TABLE default.otel_logs_date_pk
(
    `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
    `TimestampTime` DateTime DEFAULT toDateTime(Timestamp),
    `TimestampDate` Date DEFAULT toDate(Timestamp),
    `TraceId` String CODEC(ZSTD(1)),
    `SpanId` String CODEC(ZSTD(1)),
    `TraceFlags` UInt8,
    `SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
    `SeverityNumber` UInt8,
    `ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
    `Body` String CODEC(ZSTD(1)),
    `ResourceSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
    `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
    `ScopeSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
    `ScopeName` String CODEC(ZSTD(1)),
    `ScopeVersion` LowCardinality(String) CODEC(ZSTD(1)),
    `ScopeAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
    `LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.cluster.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.cluster.name'] CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.container.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.container.name'] CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.deployment.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.deployment.name'] CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.namespace.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.namespace.name'] CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.node.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.node.name'] CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.pod.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.pod.name'] CODEC(ZSTD(1)),
    `__hdx_materialized_k8s.pod.uid` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.pod.uid'] CODEC(ZSTD(1)),
    `__hdx_materialized_deployment.environment.name` LowCardinality(String) MATERIALIZED ResourceAttributes['deployment.environment.name'] CODEC(ZSTD(1)),
    INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1,
    INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
    INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
    INDEX idx_scope_attr_key mapKeys(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
    INDEX idx_scope_attr_value mapValues(ScopeAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
    INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
    INDEX idx_log_attr_value mapValues(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
    INDEX idx_lower_body lower(Body) TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 8
)
ENGINE = MergeTree
PARTITION BY toDate(TimestampTime)
PRIMARY KEY (TimestampDate, ServiceName, TimestampTime)
ORDER BY (TimestampDate, ServiceName, TimestampTime, Timestamp)
TTL TimestampTime + toIntervalDay(1)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1;

insert into default.otel_logs_date_pk (Timestamp, SeverityText, Body) VALUES (now(), 'info', 'message');
insert into default.otel_logs_date_pk (Timestamp, SeverityText, Body) VALUES (now()-interval 1 minute, 'info', 'message');
```

Then create a source that points to that table with the timestampValueExpr `TimestampTime, TimestampDate`
</details>

### References



- Linear Issue: Closes HDX-3930
- Related PRs:
2026-04-07 19:09:58 +00:00
Elizabet Oliveira
ad71dc2e91
feat(app): keyboard shortcuts modal from Help menu (#2064)
## Why

Users had no in-app list of keyboard shortcuts. Documenting them under **Help** makes discoverability match other support links and reduces guesswork (e.g. command palette vs search bar).

<img width="467" height="201" alt="Screenshot 2026-04-07 at 17 23 40" src="https://github.com/user-attachments/assets/f03e3379-0feb-4912-8f3a-89fca338d02a" />
<img width="901" height="1003" alt="Screenshot 2026-04-07 at 17 23 46" src="https://github.com/user-attachments/assets/d3f7bb0f-3a10-44c2-8e5f-7259382652b8" />



## What

- **Help → Keyboard shortcuts** opens a modal with shortcuts gathered from current hotkey usage (Spotlight ⌘/Ctrl+K, `/` and `s` for search/WHERE, time picker, find-in-table, log navigation, trace timeline, dashboards, etc.).
- Help dropdown order: documentation and setup first, then shortcuts, then Discord.
- Modal: comfortable width, dividers between rows, **or** vs **+** for alternatives vs chords.
- E2E: help menu includes the new item and opens the modal.
- Changeset: `@hyperdx/app` patch.

## Test plan

- [ ] Open Help → Keyboard shortcuts; confirm list and close behavior.
- [ ] CI: rely on PR checks (`make ci-lint` / `make ci-unit` if running locally).
2026-04-07 17:31:29 +00:00
Drew Davis
ffc961c621
fix: Add error message and edit button when tile source is missing (#2063)
## Summary

This PR updates dashboard tiles so that

1. When a tile references a source that no longer exists, there is an appropriate error message
2. When a tile references a source that no longer exists, the user is able to click the edit tile button to fix the issue

### Screenshots or video

<img width="887" height="429" alt="Screenshot 2026-04-07 at 9 40 53 AM" src="https://github.com/user-attachments/assets/ae0f77bc-3fcc-40c3-bf65-9ed454f31a4b" />

### How to test locally or on Vercel

This can be tested in the preview environment by creating a tile and then deleting the associated source.

### References



- Linear Issue: HDX-3926
- Related PRs:
2026-04-07 16:48:09 +00:00
Karl Power
3ffafced5e
feat: show error details in search event patterns (#2065)
## Summary

- Shows query errors in search page event patterns in the same way as for event deltas.
- Previously, a loading state was shown indefinitely if there was an error.

### Screenshots or video



<img width="1375" height="554" alt="Screenshot 2026-04-07 at 16 14 37" src="https://github.com/user-attachments/assets/25417f1a-bfd3-44ca-bcd6-aa24156fad14" />


### How to test locally or on Vercel



1. Easiest to test locally by manually throwing from the `useQueriedChartConfig` query function.


### References



- Linear Issue: Closes HDX-3933
- Related PRs:
2026-04-07 15:26:06 +00:00
Drew Davis
f8d2edde5a
feat: Show created/updated metadata for saved searches and dashboards (#2031)
## Summary

This PR adds createdAt/By and updatedAt/By metadata to dashboard and saved searches.

### Screenshots or video

<img width="1466" height="342" alt="Screenshot 2026-04-01 at 3 19 07 PM" src="https://github.com/user-attachments/assets/c349a3d5-f8e3-4155-9938-c8f005cdcd52" />
<img width="1216" height="433" alt="Screenshot 2026-04-01 at 3 19 57 PM" src="https://github.com/user-attachments/assets/9542a631-bdda-484c-9cef-6b780667d1dc" />
<img width="1196" height="345" alt="Screenshot 2026-04-01 at 3 19 46 PM" src="https://github.com/user-attachments/assets/c05cd0cc-2ca4-4397-8acb-e31a81b882ec" />
<img width="1409" height="433" alt="Screenshot 2026-04-01 at 3 19 38 PM" src="https://github.com/user-attachments/assets/593a96d7-86be-45b2-9f0a-b3a8f00d1353" />
<img width="1447" height="181" alt="Screenshot 2026-04-01 at 3 20 59 PM" src="https://github.com/user-attachments/assets/88742578-3dbd-4305-921f-e2ecdd11d5d4" />


### How to test locally or on Vercel

This should be tested locally. In the preview environment, these fields are not populated (since they're maintained through automatic MongoDB createdAt/updatedAt values and createdBy/updatedBy values pulled from User accounts.

### References



- Linear Issue: Closes HDX-3461
- Related PRs:
2026-04-07 14:31:44 +00:00
Karl Power
78a433c8ec
chore: refactor DBEditTimeChartForm component (#2026) 2026-04-07 15:55:08 +02:00
Elizabet Oliveira
800689acba
feat: add reusable EmptyState component and adopt across pages (#2017) 2026-04-06 21:26:59 +01:00
Elizabet Oliveira
7d1a8e549a
fix: Show sidebar favorites empty state for discoverability (#2061) 2026-04-06 20:05:16 +01:00
Zeynel
2570ff844c
Change CPU percentage dashboard to CPU Core according to new otel metric (#2028) 2026-04-06 10:30:11 -07:00
Drew Davis
1bcca2cde6
feat: Add alert icons to dashboard list page (#2053)
## Summary

This PR adds alert icons to the dashboard page, matching the implementation from the search page. Similarly, alerts icons have been added to favorited dashboards in the sidebar.

### Screenshots or video

<img width="1257" height="796" alt="Screenshot 2026-04-03 at 3 05 42 PM" src="https://github.com/user-attachments/assets/9e3fe31d-b757-46e8-8034-9be80529c96e" />
<img width="245" height="353" alt="Screenshot 2026-04-03 at 3 17 54 PM" src="https://github.com/user-attachments/assets/d7b06536-646d-4bd6-950c-b9087c3b3dbd" />

### How to test locally or on Vercel

This can be tested locally by creating some dashboards, favoriting them, and creating alerts on those dashboards.

### References



- Linear Issue: Closes HDX-3921
- Related PRs:
2026-04-06 16:21:42 +00:00
Tom Alexander
fcc0d5ec63
chore: fix PR classification tier logic (#2059) 2026-04-06 10:58:08 -04:00
Tom Alexander
bcb02f3364
chore: add pr classification bot (#2055) 2026-04-06 10:12:10 -04:00
github-actions[bot]
8125d249d9
Release HyperDX (#2009)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-03 15:17:53 -07:00
Drew Davis
011a245f4a
fix: Fix error state and table overflows (#2054)
## Summary

This PR fixes a couple of annoying overflow behaviors.

## Screenshots or video

### Search errors

#### Before

When the original query cannot be formatted, it was on one line, which did not scroll:

<img width="1831" height="652" alt="Screenshot 2026-04-03 at 3 26 48 PM" src="https://github.com/user-attachments/assets/5ce36420-3eeb-431b-9964-e8be17f2f1d6" />

#### After 

Now that same error wraps:

<img width="1831" height="673" alt="Screenshot 2026-04-03 at 3 22 19 PM" src="https://github.com/user-attachments/assets/06787d07-3579-4e78-a83e-9204de2e4587" />

### Chart Errors

#### Before

It was sometimes impossible to see the start of the error:

<img width="1712" height="1163" alt="Screenshot 2026-04-03 at 3 27 27 PM" src="https://github.com/user-attachments/assets/a4630c73-454a-4ed0-a9c0-b63ba973fa06" />

#### After 

Now the start of the error is visible and the rest can be seen by scrolling.

<img width="1707" height="1157" alt="Screenshot 2026-04-03 at 3 22 43 PM" src="https://github.com/user-attachments/assets/80f93dd7-2758-47a3-993b-b1658d80c2b4" />

### ClickHouse Slowest Queries Table

#### Before 

The table contents here overflowed the container:

<img width="1826" height="430" alt="Screenshot 2026-04-03 at 3 25 55 PM" src="https://github.com/user-attachments/assets/48847203-caa5-4410-819d-63bdc4bb9fa1" />

#### After 

Overflow is hidden:

<img width="1831" height="420" alt="Screenshot 2026-04-03 at 3 23 36 PM" src="https://github.com/user-attachments/assets/b3f4e1fb-f0f4-4919-9e3b-a0cdced0d9dc" />

### How to test locally or on Vercel

These can all be tested in the preview

### References



- Linear Issue: Closes HDX-3858 Closes HDX-3720
- Related PRs:
2026-04-03 19:51:01 +00:00