The instance Status tab is a spec-driven analytics dashboard built on top of Harper's get_analytics operation. This doc covers the shipped architecture; consult the linked source files for current code.
Health → Traffic → Requests → Database → Replication → Storage → Overview → Jobs (Health is the default landing tab).
Tab + time-range state is mirrored to ?tab=&range=&refresh=&jobsRefresh= so URLs are shareable. Below md the tab strip collapses to a Select. The time-range picker is a sticky sub-toolbar inside each chart tab (hidden on Overview and Jobs, which has its own toolbar).
All analytics data flows through one query:
- Transport:
instanceClient.post('/', { operation: 'get_analytics', metric, start_time, end_time, conditions?, bucket_ms? }). No SQL, no new endpoints. - Query options:
getRawAnalyticsQueryOptionsinsrc/integrations/api/instance/status/getAnalytics.ts. Query key is instance-scoped:[ANALYTICS_QUERY_KEY_PREFIX, instanceId, metric, startTime, endTime, bucketMs, conditions]. - Adapter:
useAnalyticsRecordsinsrc/features/instance/status/analytics/hooks/. Passes rows through verbatim (each spec'stimestamp:field decides which column drives the x-axis), surfaces amissingFieldsschema-drift signal, and applies per-spec startup jitter so concurrent specs don't refire in lockstep. - Capability probe:
useAnalyticsCapabilityruns one cheap probe per instance on mount. While loading, the Status tab shows an inline message; on error, it surfaces an "analytics unavailable" message (no legacy fallback). - Cap: server responses larger than 50 000 rows are tail-truncated client-side with a console warning.
Jobs lists Harper's background jobs (CSV/S3 loads, exports, bulk deletes, backups) from search_jobs_by_start_date — src/features/instance/status/jobs/ plus src/integrations/api/instance/status/searchJobs.ts. It reads no get_analytics, so like Overview it sits in ANALYTICS_FREE_TABS in StatusTabs.tsx: the capability probe neither gates its body nor redirects a ?tab=jobs deep link.
- Its own poll, not the window tick. The analytics tick bakes
[start, end]into every key; Jobs keys on the window width and computes the bounds in the query function, polling withpollUnlessForbidden(jobsRefresh)so a 403 stops the timer. It shares therangesearch param and the option lists, but notTimeRangePicker, whose resolution sub-label and freshness stamp describe analytics queries only. The StatusTabs tick keeps running on Jobs on purpose: stopping it would leave the charts on a stale window when the user switches back. - Auto-refresh off by default, in its own param. Each poll makes Harper run an unlimited
select * from system.hdb_jobover the window (harperserver/jobs/jobs.ts), so Jobs fetches when it opens and on Refresh now, and polls only on an interval picked for it — not on refocus or reconnect either while off. That interval isjobsRefresh, not the charts'refresh: one value cannot default to 60s on the charts and to off on Jobs without a tab switch either starting Jobs polling or resetting the charts' choice. Every navigation carries both unchanged. Push updates are HarperFast/harper#3179. - Unbounded by the server. The operation takes no limit or cursor, so every poll fetches the whole window. Jobs therefore offers windows up to 7 days (a
range=30dlink reads as 7 days there), and renders at mostMAX_RENDERED_JOBSof the newest rows, saying how many it held back. - Per node.
hdb_jobis a non-replicating system table (harperresources/databases.ts), so a cluster route lists the jobs of whichever node its request reached, and says so. - Super user only, by the role catalog's rule. The operation is
requires_su;canListJobshonours an allowlist grant only where the role editor'sgateVerdictsays a listing grants it for the instance's version band (5.3.0-beta.3 on), so the tab and the role editor never disagree. The version comes from theregistration_infothe instance route already loaded; if a refetch of it fails, a listed grant is tried rather than refused, and a 403 shows the same explanation.
The import modal's CSV progress toast uses the same job records: waitForJob reports each poll through onUpdate, and the toast is settled from useImportDataMutation's own callbacks, which still run after the modal unmounts. Harper writes nothing to a job between starting and finishing it, so the toast shows state and elapsed time, never a percentage.
Ported from the analytics-viz project. Key directories under src/features/instance/status/analytics/:
pipeline/— declarativeMetricSpecdefinitions per metric (cpu-usage,bytes-sent,replication-latency,connections, …) plus the runner (pipeline.ts) that buckets records by(dimension, time, node), applies temporal/cross-node aggregators, and emitsSeriesData. Derived metrics (error-rate,request-rate,transaction-log-growth,mqtt-traffic-*) live inpipeline/derived/.primitives/— Recharts wrappers (LineChart,StackedAreaChart,SmallMultiples,HeatmapMatrix,DimensionChipRow,DimensionCombobox) plus the dispatcherMetricRenderer. Tooltip surfaces use the sharedtooltipStyle.tsso every chart matches Studio's--popoversurface.tabs/— one file per tab. Most cards go throughMetricPanel, which wrapsMetricRendererin a<PanelErrorBoundary>so a single broken spec can't tank the whole tab. Storage and Connections use bespoke panels.
Studio's --background is brand-purple, so chart-surface tokens (--chart-bg, --chart-grid, --chart-axis, --chart-tooltip-bg, --chart-tooltip-fg) resolve against --card instead, and chart tooltips share the --popover surface used by Studio's HoverCard / Tooltip. The ported analytics-viz primitives reference semantic aliases (--color-text-secondary, --color-error, --color-warning, …) which are mapped onto Studio tokens in the @theme inline block of src/index.css.
Series colors use analytics-viz's stable node/type palettes (lib/nodeColors.ts, lib/colorAllocators/), keyed so a node keeps the same hue across tabs.
- Error isolation: every panel is wrapped in
PanelErrorBoundarywith aresetKeykeyed to the time-range stamp, so changing the window automatically retries panels that errored on stale data. - Refetch discipline:
staleTime = refreshInterval,refetchOnWindowFocus: false,refetchOnReconnect: false,placeholderData: keepPreviousData. Concurrent specs in one tab are jittered by 0–500 ms. - Aggregator safety:
Math.max/minon per-bucket samples uses a reducer instead of argument spread (the spread formRangeErrors past ~125 k values on V8). - PNG export:
lib/chartExport.tscaptures only the chart body (not the title-bar action buttons) viahtml-to-image; backgrounds are resolved by walking up the DOM so dark-mode panels export as dark, not white.
- Ported pipeline tests live in
__tests__/pipeline/(per-spec correctness, time bucketing, transforms, confidence gating). - Adapter tests cover schema-drift detection, missing-field empty states, instance-scoped cache keys, and conditions-in-key.
- Per-tab RTL smoke tests in
__tests__/tabs/mount each tab against fixture data and assert no thrown errors plus at least one<svg>per spec.
- Server-side Harper changes
- New transports or SQL
- Playwright E2E (follow-up)