diff --git a/benchmarks-website/Dockerfile b/benchmarks-website/Dockerfile
index 1f87a7148b5..094d9a9a822 100644
--- a/benchmarks-website/Dockerfile
+++ b/benchmarks-website/Dockerfile
@@ -1,16 +1,17 @@
-FROM node:24-alpine AS build
+FROM node:24-bookworm-slim AS build
 WORKDIR /app
 COPY package.json package-lock.json ./
 RUN npm ci
 COPY . .
 RUN npm run build
 
-FROM node:24-alpine
+FROM node:24-bookworm-slim
 WORKDIR /app
 COPY package.json package-lock.json ./
 RUN npm ci --omit=dev
 COPY --from=build /app/dist ./dist
 COPY server.js .
+COPY store ./store
 COPY src/config.js ./src/config.js
 EXPOSE 3000
 CMD ["node", "server.js"]
diff --git a/benchmarks-website/ETL.md b/benchmarks-website/ETL.md
new file mode 100644
index 00000000000..3087e683d60
--- /dev/null
+++ b/benchmarks-website/ETL.md
@@ -0,0 +1,302 @@
+# Benchmark Website ETL
+
+For the exact normalized table definitions, see [SCHEMA.md](./SCHEMA.md).
+
+## Source Artifacts
+
+The website consumes two append-only JSON artifacts from S3:
+
+- `commits.json`
+- `data.json.gz`
+
+Those artifacts are produced by GitHub Actions on every push to `develop`:
+
+- [`../.github/workflows/bench.yml`](../.github/workflows/bench.yml)
+  runs the random-access and compression benchmarks
+- [`../.github/workflows/sql-benchmarks.yml`](../.github/workflows/sql-benchmarks.yml)
+  runs ClickBench, TPC-H, TPC-DS, StatPopGen, PolarSignals, and other SQL workloads
+
+Both workflows append into the S3 objects with [`../scripts/cat-s3.sh`](../scripts/cat-s3.sh),
+which uses S3 `ETag` compare-and-swap to preserve append-only semantics under concurrent writers.
+
+## Artifact Generation
+
+### `commits.json`
+
+`commits.json` is generated by [`../scripts/commit-json.sh`](../scripts/commit-json.sh).
+
+Important detail: `timestamp` comes from the Git **committer date**:
+
+```bash
+git log -1 --format=%cd --date=iso-strict "$GITHUB_SHA"
+```
+
+So the website orders commits by committer timestamp, not author timestamp.
+
+Logical emitted schema:
+
+```sql
+create table commits_json_artifact (
+  id          varchar primary key,
+  message     varchar,
+  timestamp   varchar,  -- ISO 8601 with timezone, from git %cd
+  author      json,
+  committer   json,
+  tree_id     varchar,
+  url         varchar
+);
+```
+
+Representative record:
+
+```json
+{
+  "author": { "email": "alice@example.com", "name": "Alice" },
+  "committer": { "email": "alice@example.com", "name": "Alice" },
+  "id": "abc123...",
+  "message": "Improve Vortex scan planning",
+  "timestamp": "2026-04-17T09:41:52+00:00",
+  "tree_id": "def456...",
+  "url": "https://github.com/vortex-data/vortex/commit/abc123..."
+}
+```
+
+### `data.json.gz`
+
+`data.json.gz` is gzipped newline-delimited JSON. Each line is one benchmark sample emitted by
+the benchmark runners in `vortex-bench`.
+
+The website intentionally projects only the subset it needs:
+
+```sql
+create table benchmark_json_projection (
+  name       varchar,
+  unit       varchar,
+  value      double,
+  storage    varchar,
+  dataset    json,
+  commit     json,
+  commit_id  varchar
+);
+```
+
+Fields present upstream but ignored by the website include `target`, `time`, `bytes`,
+`all_runtimes`, and `env_triple`. See
+[`../vortex-bench/src/measurements.rs`](../vortex-bench/src/measurements.rs).
+
+Two raw fields carry most of the semantic structure:
+
+- `name`
+  encodes benchmark family, query number, chart identity, and series identity
+- `dataset`
+  is used for suite fan-out such as TPC-H / TPC-DS scale factors
+
+Representative naming patterns:
+
+```text
+random-access/<dataset>/<pattern>/<series>
+clickbench_q01/<engine>:<format>
+tpch_q07/<engine>:<format>
+compress time/<dataset>
+vortex size/<dataset>
+```
+
+## Refresh Pipeline
+
+At refresh time, the server:
+
+1. downloads or reuses cached copies of `commits.json` and `data.json.gz`
+2. exposes them to DuckDB as NDJSON views
+3. materializes the normalized relational model in `:memory:` DuckDB
+4. computes metadata and latest-value projections
+5. serves chart slices and summaries from those tables
+
+The refresh uses conditional HTTP requests against the cached `etag` and `last-modified` values.
+If both files are unchanged and the server already has an in-memory state, the rebuild is skipped.
+
+## Unicode Relation Diagrams
+
+```text
+                           Refresh / Materialization Graph
+
+┌──────────────────────────────┐        ┌──────────────────────────────┐
+│ raw_commits                  │        │ raw_benchmarks               │
+│ view over commits.json       │        │ view over data.json.gz       │
+│ ──────────────────────────── │        │ ──────────────────────────── │
+│ id                           │        │ benchmark_row                │
+│ message                      │        │ name                         │
+│ timestamp                    │        │ unit                         │
+│ author                       │        │ value                        │
+│ url                          │        │ storage                      │
+└──────────────┬───────────────┘        │ dataset                      │
+               │                        │ commit                       │
+               │ order by timestamp     │ commit_id                    │
+               ▼                        └──────────────┬───────────────┘
+┌──────────────────────────────┐                       │
+│ commit_dim                   │                       │ split name / keep raw JSON
+│ materialized table           │                       ▼
+│ ──────────────────────────── │        ┌──────────────────────────────┐
+│ PK commit_idx                │        │ benchmarks_base              │
+│ UK id                        │        │ materialized table           │
+│ message                      │        │ ──────────────────────────── │
+│ timestamp_text               │        │ PK benchmark_row             │
+│ commit_ts                    │        │ name / name_lower            │
+│ author                       │        │ part1..part4 / part_count    │
+│ url                          │        │ unit / raw_value             │
+└──────────────┬───────────────┘        │ storage                      │
+               │                        │ dataset_json                 │
+               │ join on id =           │ commit_json                  │
+               │      resolved_commit_id│ commit_id                    │
+               │                        └──────────────┬───────────────┘
+               │                                       │
+               │                 ┌─────────────────────┼─────────────────────┐
+               │                 │                     │                     │
+               │                 ▼                     ▼                     ▼
+               │       ┌───────────────────┐  ┌───────────────────┐  ┌──────────────────────┐
+               │       │ query_suites      │  │ engine_renames    │  │ valid_groups         │
+               │       │ config table      │  │ config table      │  │ config table         │
+               │       │ prefix            │  │ src               │  │ PK group_name        │
+               │       │ display_name      │  │ dst               │  └──────────┬───────────┘
+               │       │ query_prefix      │  └─────────┬─────────┘             │ allowlist
+               │       │ dataset_key       │            │ rename series          │
+               │       │ fan_out / skip    │            │                        │
+               │       └─────────┬─────────┘            │                        │
+               │                 │ match on prefix      │                        │
+               │                 ▼                      │                        │
+               │       ┌──────────────────────────────┐ │                        │
+               │       │ matched_suites               │ │                        │
+               │       │ materialized table           │ │                        │
+               │       │ PK benchmark_row             │ │                        │
+               │       │ prefix / display_name        │ │                        │
+               │       │ query_prefix                 │ │                        │
+               │       │ dataset_key / fan_out        │ │                        │
+               │       └──────────────┬───────────────┘ │                        │
+               │                      │                 │                        │
+               │                      └────────┬────────┴──────────────┐         │
+               │                               ▼                       │         │
+               │       ┌──────────────────────────────────────────────┐ │         │
+               │       │ classified_benchmarks                        │◄┘         │
+               │       │ materialized table                           │           │
+               │       │ ──────────────────────────────────────────── │           │
+               │       │ PK benchmark_row                             │           │
+               │       │ name                                         │           │
+               │       │ resolved_commit_id                           │           │
+               │       │ group_name                                   │           │
+               │       │ chart_name                                   │           │
+               │       │ series_name                                  │           │
+               │       │ sort_position                                │           │
+               │       │ unit / value                                 │           │
+               │       └──────────────────────┬─────────────────────────┘          │
+               │                              │                                    │
+               │                              │ join commit + apply valid_groups   │
+               │                              ▼                                    │
+               │       ┌──────────────────────────────────────────────┐◄───────────┘
+               │       │ benchmark_points                             │
+               │       │ first fact table                             │
+               │       │ ──────────────────────────────────────────── │
+               │       │ group_name                                   │
+               │       │ chart_name                                   │
+               │       │ series_name                                  │
+               │       │ sort_position                                │
+               │       │ unit / value                                 │
+               │       │ FK commit_idx -> commit_dim.commit_idx       │
+               │       └──────────────────────┬───────────────────────┘
+               │                              │ trim leading empty commits
+               │                              ▼
+               │       ┌──────────────────────────────────────────────┐
+               │       │ active_commits                               │
+               │       │ rebased commit axis                          │
+               │       │ ──────────────────────────────────────────── │
+               │       │ PK original_commit_idx                       │
+               │       │ UK commit_idx                                │
+               │       │ id / message / timestamp / author / url      │
+               │       └──────────────────────┬───────────────────────┘
+               │                              │ join on original_commit_idx
+               └──────────────────────────────┴─────────────────────────────────────┐
+                                                                                   ▼
+                                             ┌──────────────────────────────────────────────┐
+                                             │ benchmark_points_active                      │
+                                             │ canonical chart fact table                  │
+                                             │ ──────────────────────────────────────────── │
+                                             │ group_name                                   │
+                                             │ chart_name                                   │
+                                             │ series_name                                  │
+                                             │ sort_position                                │
+                                             │ unit / value                                 │
+                                             │ commit_idx                                   │
+                                             │ practical key:                               │
+                                             │ (group_name, chart_name, series_name,        │
+                                             │  commit_idx)                                 │
+                                             └──────────────────────────────────────────────┘
+```
+
+```text
+                              Serving / Derived Projection Graph
+
+                                             ┌──────────────────────────────────────────────┐
+                                             │ benchmark_points_active                      │
+                                             │ group_name, chart_name, series_name,         │
+                                             │ sort_position, unit, value, commit_idx       │
+                                             └───────┬──────────────────────┬───────────────┘
+                                                     │                      │
+                           group by chart            │                      │ arg_max(value, commit_idx)
+                                                     │                      │
+                                                     ▼                      ▼
+                      ┌──────────────────────────────┐        ┌──────────────────────────────┐
+                      │ chart_defs                   │        │ chart_series_latest_values    │
+                      │ PK (group_name, chart_name) │        │ PK (group_name, chart_name,   │
+                      │ sort_position               │        │     series_name)              │
+                      │ unit                        │        │ latest_value                  │
+                      └──────────────┬──────────────┘        └──────────────┬───────────────┘
+                                     │                                      │
+                                     │ latest commit per chart              │
+                                     ▼                                      │
+                      ┌──────────────────────────────┐                      │
+                      │ chart_latest_idx             │                      │
+                      │ PK (group_name, chart_name) │                      │
+                      │ latest_commit_idx           │                      │
+                      └──────────────┬──────────────┘                      │
+                                     │ join on latest_commit_idx           │
+                                     ▼                                      │
+                      ┌──────────────────────────────┐                      │
+                      │ chart_latest_values          │──────────────────────┘
+                      │ group_name                   │
+                      │ chart_name                   │
+                      │ series_name                  │
+                      │ value                        │
+                      └──────────────┬──────────────┘
+                                     │
+                                     │ + active_commits
+                                     ▼
+                      ┌──────────────────────────────┐
+                      │ /api/metadata                │
+                      │ groups, charts, summaries,  │
+                      │ commits, lastUpdated        │
+                      └──────────────────────────────┘
+
+                      ┌──────────────────────────────┐
+                      │ active_commits               │
+                      │ commit_idx, id, message,    │
+                      │ timestamp, author, url      │
+                      └──────────────┬──────────────┘
+                                     │ range slice
+                                     ▼
+                      ┌──────────────────────────────┐
+                      │ /api/data/:group/:chart      │
+                      │ dense series arrays over     │
+                      │ requested commit range       │
+                      └──────────────────────────────┘
+```
+
+## Transformation Notes
+
+- `commit_dim` is the first place timestamps become typed and ordered.
+- `benchmarks_base` is the string-parsing layer over benchmark `name`.
+- `classified_benchmarks` is the semantic step where the row becomes:
+  - a group
+  - a chart
+  - a series
+  - a normalized display unit/value
+- `benchmark_points_active` is the canonical long-form chart fact table.
+- The API never stores dense charts on disk; dense series arrays are produced on demand from the
+  long-form fact table.
diff --git a/benchmarks-website/README.md b/benchmarks-website/README.md
new file mode 100644
index 00000000000..2b67382e7e3
--- /dev/null
+++ b/benchmarks-website/README.md
@@ -0,0 +1,206 @@
+# Benchmarks Website
+
+This directory contains the benchmark website frontend, the Node HTTP server, and the DuckDB-based
+refresh pipeline that turns the raw benchmark artifacts into chartable time series.
+
+For the data model and table relationships, start with [SCHEMA.md](./SCHEMA.md).
+For the upstream artifact generation and refresh/materialization flow, see [ETL.md](./ETL.md).
+
+## Prerequisites
+
+- Node.js `>=18`
+- npm
+- Optional: DuckDB CLI, if you want to query the cached artifacts directly
+
+Install dependencies:
+
+```bash
+cd benchmarks-website
+npm install
+```
+
+## Development Server
+
+Run the frontend and backend together:
+
+```bash
+cd benchmarks-website
+npm run dev
+```
+
+That starts:
+
+- Vite on `http://localhost:5173`
+- the API/static server on `http://localhost:3000`
+
+Useful endpoints:
+
+- `http://localhost:3000/api/metadata`
+- `http://localhost:3000/api/health`
+
+The backend refreshes from these artifact URLs by default:
+
+- `https://vortex-ci-benchmark-results.s3.amazonaws.com/data.json.gz`
+- `https://vortex-ci-benchmark-results.s3.amazonaws.com/commits.json`
+
+Relevant environment variables:
+
+```bash
+PORT=3000
+REFRESH_INTERVAL=300000
+DATA_URL=https://vortex-ci-benchmark-results.s3.amazonaws.com/data.json.gz
+COMMITS_URL=https://vortex-ci-benchmark-results.s3.amazonaws.com/commits.json
+CACHE_DIR=/path/to/local/cache
+```
+
+`CACHE_DIR` is the most useful one during development. If it is unset, the server uses a temp
+directory under `os.tmpdir()`.
+
+## Pull The Data Locally
+
+If you want a predictable local copy for exploration, populate a cache directory yourself and point
+the server at it.
+
+```bash
+cd benchmarks-website
+mkdir -p .cache/benchmarks
+
+curl -L \
+  https://vortex-ci-benchmark-results.s3.amazonaws.com/data.json.gz \
+  -o .cache/benchmarks/data.json.gz
+
+curl -L \
+  https://vortex-ci-benchmark-results.s3.amazonaws.com/commits.json \
+  -o .cache/benchmarks/commits.json
+```
+
+Then start the dev server against that cache:
+
+```bash
+cd benchmarks-website
+CACHE_DIR="$PWD/.cache/benchmarks" npm run dev
+```
+
+On first startup, the server will use the cached files immediately and then asynchronously
+revalidate them against S3.
+
+## Explore The Cached Data Directly
+
+Once `data.json.gz` and `commits.json` exist locally, you can query them with DuckDB without
+running the website.
+
+Example with the DuckDB CLI:
+
+```sql
+create view raw_commits as
+select *
+from read_json(
+  '.cache/benchmarks/commits.json',
+  format = 'newline_delimited',
+  compression = 'auto_detect',
+  columns = {
+    id: 'VARCHAR',
+    message: 'VARCHAR',
+    timestamp: 'VARCHAR',
+    author: 'JSON',
+    url: 'VARCHAR'
+  }
+);
+
+create view raw_benchmarks as
+select *
+from read_json(
+  '.cache/benchmarks/data.json.gz',
+  format = 'newline_delimited',
+  compression = 'auto_detect',
+  columns = {
+    name: 'VARCHAR',
+    unit: 'VARCHAR',
+    value: 'DOUBLE',
+    storage: 'VARCHAR',
+    dataset: 'JSON',
+    commit: 'JSON',
+    commit_id: 'VARCHAR'
+  }
+);
+```
+
+Useful starter queries:
+
+```sql
+select count(*) as commit_count from raw_commits;
+
+select count(*) as benchmark_count from raw_benchmarks;
+
+select split_part(name, '/', 1) as prefix, count(*) as rows
+from raw_benchmarks
+group by 1
+order by 2 desc
+limit 20;
+
+select
+  coalesce(json_extract_string(commit, '$.id'), commit_id) as resolved_commit_id,
+  count(*) as rows
+from raw_benchmarks
+group by 1
+order by 2 desc
+limit 20;
+```
+
+If you want the normalized relational model rather than the raw JSON views, follow the pipeline in
+[SCHEMA.md](./SCHEMA.md) and [`store/sql.js`](./store/sql.js).
+
+## Export The Full Bootstrap SQL
+
+If you want the exact SQL that the server uses to create all config tables, raw views, normalized
+tables, and derived projections, export it from the shared SQL builder:
+
+```bash
+cd benchmarks-website
+npm run export-sql -- \
+  --data-path "$PWD/.cache/benchmarks/data.json.gz" \
+  --commits-path "$PWD/.cache/benchmarks/commits.json" \
+  --output "$PWD/.cache/benchmarks/bootstrap.sql"
+```
+
+Then load it in DuckDB:
+
+```bash
+duckdb benchmark-explore.duckdb < .cache/benchmarks/bootstrap.sql
+```
+
+That creates the same tables and views the server uses, including:
+
+- `query_suites`
+- `valid_groups`
+- `engine_renames`
+- `raw_commits`
+- `raw_benchmarks`
+- `commit_dim`
+- `benchmarks_base`
+- `matched_suites`
+- `classified_benchmarks`
+- `benchmark_points`
+- `active_commits`
+- `benchmark_points_active`
+- `chart_defs`
+- `chart_latest_idx`
+- `chart_latest_values`
+- `chart_series_latest_values`
+
+If you want a portable template instead of path-specific SQL:
+
+```bash
+cd benchmarks-website
+npm run export-sql -- --placeholders --output bootstrap.template.sql
+```
+
+That emits a script using `__DATA_PATH__` and `__COMMITS_PATH__` placeholders.
+
+## Notes
+
+- The website only projects the subset of the raw benchmark JSON it needs for grouping, charting,
+  and summaries.
+- Benchmark names are part of the schema. Group, chart, and series identity are inferred from the
+  `name`, `storage`, and `dataset` fields during refresh.
+- The server returns `503` with `Retry-After` while the initial refresh is still loading.
diff --git a/benchmarks-website/SCHEMA.md b/benchmarks-website/SCHEMA.md
new file mode 100644
index 00000000000..d54fd168182
--- /dev/null
+++ b/benchmarks-website/SCHEMA.md
@@ -0,0 +1,298 @@
+# Benchmark Website Schema
+
+This file is the reference for the **normalized DuckDB schema** used by the benchmark website.
+
+For the upstream artifact generation, refresh pipeline, and table-to-table flow, see
+[ETL.md](./ETL.md).
+
+The implementation lives primarily in [`store/sql.js`](./store/sql.js),
+[`store/metadata.js`](./store/metadata.js), and [`store/benchmark-store.js`](./store/benchmark-store.js).
+For DuckDB's NDJSON reader semantics, see:
+<https://duckdb.org/docs/stable/data/json/loading_json>.
+
+## Core Fact Model
+
+The important point is that the website does **not** store nested charts directly. It stores a
+long-form fact table, `benchmark_points_active`, keyed in practice by:
+
+```sql
+(group_name, chart_name, series_name, commit_idx)
+```
+
+Everything else is metadata, lookup/config data, or a projection over that fact table.
+
+## Configuration Tables
+
+These are synthesized from [`src/config.js`](./src/config.js), not loaded from the JSON artifacts.
+
+```sql
+create table query_suites (
+  prefix         varchar,
+  display_name   varchar,
+  query_prefix   varchar,
+  dataset_key    varchar,
+  fan_out        boolean,
+  skip           boolean
+);
+
+create table valid_groups (
+  group_name     varchar primary key
+);
+
+create table engine_renames (
+  src            varchar primary key,
+  dst            varchar
+);
+```
+
+## Raw Views
+
+These are views over the cached artifact files.
+
+```sql
+create view raw_commits as
+select
+  id,
+  message,
+  timestamp,
+  author,
+  url
+from read_json(...);
+
+create view raw_benchmarks as
+select
+  row_number() over () as benchmark_row,
+  name,
+  unit,
+  value,
+  storage,
+  dataset,
+  commit,
+  commit_id
+from read_json(...);
+```
+
+## Stage 1: Commit Ordering and Name Parsing
+
+```sql
+create table commit_dim (
+  commit_idx      bigint primary key,
+  id              varchar unique,
+  message         varchar,
+  timestamp_text  varchar,
+  commit_ts       timestamptz,
+  author          varchar,
+  url             varchar
+);
+
+create table benchmarks_base (
+  benchmark_row   bigint primary key,
+  name            varchar,
+  name_lower      varchar,
+  part1           varchar,
+  part1_lower     varchar,
+  part2           varchar,
+  part3           varchar,
+  part4           varchar,
+  part_count      bigint,
+  unit            varchar,
+  raw_value       double,
+  storage         varchar,
+  dataset_json    json,
+  commit_json     json,
+  commit_id       varchar
+);
+
+create table matched_suites (
+  benchmark_row   bigint primary key,
+  prefix          varchar,
+  display_name    varchar,
+  query_prefix    varchar,
+  dataset_key     varchar,
+  fan_out         boolean
+);
+```
+
+Notes:
+
+- `commit_dim` orders commits by parsed timestamp, then `id`.
+- `benchmarks_base` is the normalized string-parsing layer over benchmark `name`.
+- `matched_suites` assigns a benchmark row to the longest matching configured suite prefix.
+
+## Stage 2: Classification
+
+```sql
+create table classified_benchmarks (
+  benchmark_row       bigint primary key,
+  name                varchar,
+  resolved_commit_id  varchar,
+  group_name          varchar,
+  chart_name          varchar,
+  series_name         varchar,
+  sort_position       integer,
+  unit                varchar,
+  value               double
+);
+```
+
+This is the semantic classification step:
+
+- `resolved_commit_id` is `coalesce(commit.id, commit_id)`
+- `group_name` is inferred from benchmark name prefixes, query suite config, storage, and scale
+  factor metadata
+- `chart_name` and `series_name` come from benchmark-name parsing
+- `series_name` is passed through `engine_renames`
+- `unit` and `value` are normalized for display:
+  - `ns -> ms/iter`
+  - `bytes -> MiB`
+
+In other words, the benchmark-name grammar is part of the schema.
+
+## Stage 3: Time-Series Fact Tables
+
+```sql
+create table benchmark_points (
+  group_name      varchar,
+  chart_name      varchar,
+  series_name     varchar,
+  sort_position   integer,
+  unit            varchar,
+  value           double,
+  commit_idx      bigint
+);
+
+create table active_commits (
+  original_commit_idx  bigint primary key,
+  commit_idx           bigint unique,
+  id                   varchar,
+  message              varchar,
+  timestamp            varchar,
+  author               varchar,
+  url                  varchar
+);
+
+create table benchmark_points_active (
+  group_name      varchar,
+  chart_name      varchar,
+  series_name     varchar,
+  sort_position   integer,
+  unit            varchar,
+  value           double,
+  commit_idx      bigint
+);
+```
+
+Notes:
+
+- `benchmark_points` is the first fact table after commit resolution and group filtering.
+- `active_commits` trims leading commits with no benchmark data and re-bases the visible commit
+  axis to `0..N-1`.
+- `benchmark_points_active` is the canonical fact table used for charting and summaries.
+
+## Stage 4: Chart Metadata and Latest-Value Projections
+
+```sql
+create table chart_defs (
+  group_name      varchar,
+  chart_name      varchar,
+  sort_position   integer,
+  unit            varchar,
+  primary key (group_name, chart_name)
+);
+
+create table chart_latest_idx (
+  group_name        varchar,
+  chart_name        varchar,
+  latest_commit_idx bigint,
+  primary key (group_name, chart_name)
+);
+
+create table chart_latest_values (
+  group_name      varchar,
+  chart_name      varchar,
+  series_name     varchar,
+  value           double
+);
+
+create table chart_series_latest_values (
+  group_name      varchar,
+  chart_name      varchar,
+  series_name     varchar,
+  latest_value    double,
+  primary key (group_name, chart_name, series_name)
+);
+```
+
+There are two different "latest" concepts:
+
+- `chart_latest_values`
+  values at the chart's latest commit index
+- `chart_series_latest_values`
+  latest non-null value seen for each series, via `arg_max(value, commit_idx)`
+
+## API-Facing Reads
+
+### `/api/metadata`
+
+Built in [`store/metadata.js`](./store/metadata.js).
+
+Primary sources:
+
+- `active_commits`
+- `chart_defs`
+- `chart_series_latest_values`
+- `chart_latest_values`
+- `benchmark_points_active`
+
+Logical output shape:
+
+```sql
+metadata (
+  total_commits bigint,
+  last_updated  timestamptz,
+  commits       json,
+  groups        json
+)
+```
+
+Each group contains:
+
+```sql
+group_metadata (
+  charts       json,
+  total_charts bigint,
+  has_data     boolean,
+  summary      json
+)
+```
+
+Summary sections are computed from SQL aggregates:
+
+- random access ranking from one anchor chart
+- compression geomeans from Vortex/Parquet ratio charts
+- compression-size min/geomean/max
+- query-suite geomeans with a missing-query penalty
+
+### `/api/data/:group/:chart`
+
+Built in [`store/benchmark-store.js`](./store/benchmark-store.js).
+
+The query:
+
+1. selects the requested commit range from `active_commits`
+2. enumerates the series present for that chart
+3. cross joins series against commits
+4. left joins `benchmark_points_active`
+5. aggregates with `list(value order by commit_idx)`
+
+That produces a dense matrix with explicit `null` gaps:
+
+```sql
+series_points (
+  series_name varchar,
+  values      list<double>
+)
+```
+
+The server then serializes the commit slice, applies downsampling if needed, and returns
+`{ commits, series, requestedRange, downsampleLevel, ... }`.
diff --git a/benchmarks-website/package-lock.json b/benchmarks-website/package-lock.json
index d140b73d225..b64b131bb36 100644
--- a/benchmarks-website/package-lock.json
+++ b/benchmarks-website/package-lock.json
@@ -8,6 +8,7 @@
       "name": "vortex-benchmarks-website",
       "version": "2.0.0",
       "dependencies": {
+        "@duckdb/node-api": "^1.5.2-r.1",
         "chart.js": "^4.4.4",
         "chartjs-plugin-zoom": "^2.0.1",
         "downsample": "^1.4.0",
@@ -320,6 +321,107 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@duckdb/node-api": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-api/-/node-api-1.5.2-r.1.tgz",
+      "integrity": "sha512-OzBBnS0JGXMoS5mzKNY/Ylr7SshcRQiLFIoxQ4AlePwJ2fNeDL/fbHu/knjxUrXwW1fJBTUgwWftmxDdnZZb3A==",
+      "license": "MIT",
+      "dependencies": {
+        "@duckdb/node-bindings": "1.5.2-r.1"
+      }
+    },
+    "node_modules/@duckdb/node-bindings": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings/-/node-bindings-1.5.2-r.1.tgz",
+      "integrity": "sha512-bUg3bLVj70YVku6fKyQJS8ASORl7kM7YFVFznsEB9pWbtazPj+ME2x2FUk0WiTzjJdutjzSSGXF066mB4bGGZA==",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@duckdb/node-bindings-darwin-arm64": "1.5.2-r.1",
+        "@duckdb/node-bindings-darwin-x64": "1.5.2-r.1",
+        "@duckdb/node-bindings-linux-arm64": "1.5.2-r.1",
+        "@duckdb/node-bindings-linux-x64": "1.5.2-r.1",
+        "@duckdb/node-bindings-win32-arm64": "1.5.2-r.1",
+        "@duckdb/node-bindings-win32-x64": "1.5.2-r.1"
+      }
+    },
+    "node_modules/@duckdb/node-bindings-darwin-arm64": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings-darwin-arm64/-/node-bindings-darwin-arm64-1.5.2-r.1.tgz",
+      "integrity": "sha512-v35FyKOb8EJCvaiPF7k0gvKiJTXR7PPQDNoWR0Gu+YSX5O9b+DIguzt1348Of3HebHy6ATSMzlUekaVA9YXu+g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@duckdb/node-bindings-darwin-x64": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings-darwin-x64/-/node-bindings-darwin-x64-1.5.2-r.1.tgz",
+      "integrity": "sha512-SU9dIJ1BluKkkGxi4UsP4keqkkstB2YDySF9KcYu3EZKIVM3FTv2zc7XO38dXnHOq6+F3WqhWWZvD+XU945p7A==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@duckdb/node-bindings-linux-arm64": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings-linux-arm64/-/node-bindings-linux-arm64-1.5.2-r.1.tgz",
+      "integrity": "sha512-3Tra9xM3aM3denaER4KhJ6//6PpmPbik9ECBQ+sh9PyKaEgHw/0kAcKnLm5EzWUnXF0qYmZlewvkCrse8KmOYw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@duckdb/node-bindings-linux-x64": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings-linux-x64/-/node-bindings-linux-x64-1.5.2-r.1.tgz",
+      "integrity": "sha512-pcQvZRHiIfJ9cq8parkSQczQHEml/IeGfnDCMAbEgD6+jaV9Y9Y5Ph1kP9aR+bm6him1S5ZIEr3kZbihjKnWbA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@duckdb/node-bindings-win32-arm64": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings-win32-arm64/-/node-bindings-win32-arm64-1.5.2-r.1.tgz",
+      "integrity": "sha512-Ji8tym+N3LkrhVt0Up3bsacD/kpg4/JXFJQqxswiYvBaNCQOk+D+aiVS0GN5pcqvmnG7V7TpsDRzkLEFaWp1vw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@duckdb/node-bindings-win32-x64": {
+      "version": "1.5.2-r.1",
+      "resolved": "https://registry.npmjs.org/@duckdb/node-bindings-win32-x64/-/node-bindings-win32-x64-1.5.2-r.1.tgz",
+      "integrity": "sha512-5XqcqC+4R8ghBEEbnc2a0sqfz1zyPBRb9YcmIWfiuDoCYSYFbKhmHcEyNftZDHcwCoLOHXnUin45jraex4STqQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
     "node_modules/@esbuild/aix-ppc64": {
       "version": "0.25.12",
       "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
diff --git a/benchmarks-website/package.json b/benchmarks-website/package.json
index 6cda687b838..13280ae7898 100644
--- a/benchmarks-website/package.json
+++ b/benchmarks-website/package.json
@@ -7,12 +7,14 @@
     "vite": "vite",
     "server": "node server.js",
     "build": "vite build",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "export-sql": "node scripts/export-bootstrap-sql.js"
   },
   "engines": {
     "node": ">=18.0.0"
   },
   "dependencies": {
+    "@duckdb/node-api": "^1.5.2-r.1",
     "chart.js": "^4.4.4",
     "chartjs-plugin-zoom": "^2.0.1",
     "downsample": "^1.4.0",
diff --git a/benchmarks-website/scripts/export-bootstrap-sql.js b/benchmarks-website/scripts/export-bootstrap-sql.js
new file mode 100644
index 00000000000..9bc2b5eee1d
--- /dev/null
+++ b/benchmarks-website/scripts/export-bootstrap-sql.js
@@ -0,0 +1,89 @@
+#!/usr/bin/env node
+
+import fs from "fs/promises";
+import path from "path";
+import { buildBootstrapSql } from "../store/sql.js";
+
+function parseArgs(argv) {
+  const options = {
+    dataPath: null,
+    commitsPath: null,
+    outputPath: null,
+    placeholders: false,
+  };
+
+  for (let i = 0; i < argv.length; i++) {
+    const arg = argv[i];
+    switch (arg) {
+      case "--data-path":
+        options.dataPath = argv[++i] || null;
+        break;
+      case "--commits-path":
+        options.commitsPath = argv[++i] || null;
+        break;
+      case "--output":
+        options.outputPath = argv[++i] || null;
+        break;
+      case "--placeholders":
+        options.placeholders = true;
+        break;
+      case "--help":
+      case "-h":
+        printHelp();
+        process.exit(0);
+        break;
+      default:
+        throw new Error(`Unknown argument: ${arg}`);
+    }
+  }
+
+  return options;
+}
+
+function printHelp() {
+  process.stdout.write(`Usage:
+  node scripts/export-bootstrap-sql.js --data-path <path> --commits-path <path> [--output <path>]
+  node scripts/export-bootstrap-sql.js --placeholders [--output <path>]
+
+Options:
+  --data-path <path>      Path to data.json.gz
+  --commits-path <path>   Path to commits.json
+  --output <path>         Write SQL to a file instead of stdout
+  --placeholders          Emit __DATA_PATH__ / __COMMITS_PATH__ placeholders
+  --help, -h              Show this help
+`);
+}
+
+async function main() {
+  const options = parseArgs(process.argv.slice(2));
+
+  const dataPath = options.placeholders ? "__DATA_PATH__" : options.dataPath;
+  const commitsPath = options.placeholders
+    ? "__COMMITS_PATH__"
+    : options.commitsPath;
+
+  if (!dataPath || !commitsPath) {
+    printHelp();
+    process.stderr.write(
+      "\nerror: either provide --data-path and --commits-path, or use --placeholders\n",
+    );
+    process.exit(1);
+  }
+
+  const sql = `${buildBootstrapSql(dataPath, commitsPath)}\n`;
+
+  if (options.outputPath) {
+    const outputPath = path.resolve(options.outputPath);
+    await fs.mkdir(path.dirname(outputPath), { recursive: true });
+    await fs.writeFile(outputPath, sql, "utf8");
+    process.stdout.write(`${outputPath}\n`);
+    return;
+  }
+
+  process.stdout.write(sql);
+}
+
+main().catch((error) => {
+  process.stderr.write(`${error.message}\n`);
+  process.exit(1);
+});
diff --git a/benchmarks-website/server.js b/benchmarks-website/server.js
index a9af96234fe..80ce55e9e82 100644
--- a/benchmarks-website/server.js
+++ b/benchmarks-website/server.js
@@ -2,16 +2,11 @@ import http from "http";
 import fs from "fs";
 import path from "path";
 import { fileURLToPath } from "url";
-import zlib from "zlib";
-import readline from "readline";
-import { Readable } from "stream";
-import { LTTB } from "downsample";
-import { QUERY_SUITES, FAN_OUT_GROUPS, ENGINE_RENAMES } from "./src/config.js";
+import { BenchmarkStore } from "./store/benchmark-store.js";
 
 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);
 
-// Configuration
 const PORT = process.env.PORT || 3000;
 const DATA_URL =
   process.env.DATA_URL ||
@@ -20,17 +15,9 @@ const COMMITS_URL =
   process.env.COMMITS_URL ||
   "https://vortex-ci-benchmark-results.s3.amazonaws.com/commits.json";
 const REFRESH_INTERVAL = process.env.REFRESH_INTERVAL || 5 * 60 * 1000;
-const MAX_POINTS = 200;
 const USE_LOCAL_DATA = process.env.USE_LOCAL_DATA === "true";
-
-// Benchmark groups: non-query groups + simple suites + fan-out suites
-const GROUPS = [
-  "Random Access",
-  "Compression",
-  "Compression Size",
-  ...QUERY_SUITES.filter((s) => !s.skip && !s.fanOut).map((s) => s.displayName),
-  ...FAN_OUT_GROUPS,
-];
+const CACHE_DIR = process.env.CACHE_DIR || undefined;
+const LOADING_RETRY_AFTER_SECONDS = 1;
 
 const MIME = {
   ".html": "text/html",
@@ -46,688 +33,103 @@ const MIME = {
   ".webmanifest": "application/manifest+json",
 };
 
-let store = {
-  commits: [],
-  groups: {},
-  metadata: null,
-  downsampled: {},
-  lastUpdated: null,
-};
-
-// Utilities
-const rename = (s) => ENGINE_RENAMES[s.toLowerCase()] || ENGINE_RENAMES[s] || s;
-const geoMean = (arr) =>
-  arr.length
-    ? Math.pow(
-        arr.reduce((a, v) => a * v, 1),
-        1 / arr.length,
-      )
-    : null;
-
-// Categorize benchmarks based on name patterns and metadata
-function getGroup(benchmark) {
-  const name = benchmark.name;
-  const lower = name.toLowerCase();
-
-  // Random Access: "random-access/..." or "random access/..."
-  if (
-    lower.startsWith("random-access/") ||
-    lower.startsWith("random access/")
-  ) {
-    return "Random Access";
-  }
-
-  // Compression Size: size measurements
-  if (
-    lower.startsWith("vortex size/") ||
-    lower.startsWith("vortex-file-compressed size/") ||
-    lower.startsWith("parquet size/") ||
-    lower.startsWith("lance size/") ||
-    lower.includes(":raw size/") ||
-    lower.includes(":parquet-zstd size/") ||
-    lower.includes(":lance size/")
-  ) {
-    return "Compression Size";
-  }
-
-  // Compression: compress/decompress time and ratio measurements
-  if (
-    lower.startsWith("compress time/") ||
-    lower.startsWith("decompress time/") ||
-    lower.startsWith("parquet_rs-zstd compress") ||
-    lower.startsWith("parquet_rs-zstd decompress") ||
-    lower.startsWith("lance compress") ||
-    lower.startsWith("lance decompress") ||
-    lower.startsWith("vortex:lance ratio") ||
-    lower.startsWith("vortex:parquet-zstd ratio") ||
-    lower.startsWith("vortex:raw ratio")
-  ) {
-    return "Compression";
-  }
-
-  // SQL query suites: match "{prefix}_q..." or "{prefix}/..."
-  for (const suite of QUERY_SUITES) {
-    if (
-      !lower.startsWith(suite.prefix + "_q") &&
-      !lower.startsWith(suite.prefix + "/")
-    )
-      continue;
-    if (suite.skip) return null;
-    if (!suite.fanOut) return suite.displayName;
-    // Fan-out suites: expand by storage and scale factor
-    const storage = benchmark.storage?.toUpperCase() === "S3" ? "S3" : "NVMe";
-    const rawSf = benchmark.dataset?.[suite.datasetKey]?.scale_factor;
-    const sf = rawSf ? Math.round(parseFloat(rawSf)) : 1;
-    return `${suite.displayName} (${storage}) (SF=${sf})`;
-  }
-
-  return null;
-}
-
-// Format query name for display: "{prefix}_q00" -> "{QUERY_PREFIX} Q0"
-function formatQuery(q) {
-  const lower = q.toLowerCase();
-  for (const suite of QUERY_SUITES) {
-    if (suite.skip) continue;
-    const m = lower.match(new RegExp(`^${suite.prefix}[_ ]?q(\\d+)`, "i"));
-    if (m) return `${suite.queryPrefix} Q${parseInt(m[1], 10)}`;
-  }
-  return q.toUpperCase().replace(/[_-]/g, " ");
-}
-
-function normalizeChartName(group, chartName) {
-  if (group === "Compression Size" && chartName === "VORTEX FILE COMPRESSED SIZE") {
-    return "VORTEX SIZE";
-  }
-  return chartName;
-}
+const benchmarks = new BenchmarkStore({
+  dataUrl: DATA_URL,
+  commitsUrl: COMMITS_URL,
+  useLocalData: USE_LOCAL_DATA,
+  cacheDir: CACHE_DIR,
+});
+let refreshIntervalId = null;
 
-// LTTB downsampling
-function lttbIndices(seriesMap, target) {
-  const keys = [...seriesMap.keys()];
-  if (!keys.length) return [];
-  const len = seriesMap.get(keys[0])?.length || 0;
-  if (len <= target) return [...Array(len).keys()];
+const json = (res, code, data, headers = {}) => {
+  res.writeHead(code, {
+    "Content-Type": "application/json",
+    "Access-Control-Allow-Origin": "*",
+    "Cache-Control": "no-store",
+    Pragma: "no-cache",
+    ...headers,
+  });
+  res.end(JSON.stringify(data));
+};
 
-  const avg = Array(len);
-  for (let i = 0; i < len; i++) {
-    let sum = 0,
-      n = 0;
-    for (const arr of seriesMap.values()) {
-      const v = arr[i]?.value ?? arr[i];
-      if (v != null && !isNaN(v)) {
-        sum += v;
-        n++;
-      }
-    }
-    avg[i] = [i, n ? sum / n : 0];
+function loadingHeaders(status) {
+  if (status.state !== "loading") {
+    return {};
   }
 
-  const idx = LTTB(avg, target).map((p) => Math.round(p[0]));
-  if (!idx.includes(0)) idx.unshift(0);
-  if (!idx.includes(len - 1)) idx.push(len - 1);
-  return idx.sort((a, b) => a - b);
-}
-
-function downsample(data, factor) {
-  const target = Math.ceil(data.commits.length / factor);
-  if (target >= data.commits.length) return data;
-
-  const idx = lttbIndices(data.series, target);
-  const series = new Map();
-  for (const [k, v] of data.series)
-    series.set(
-      k,
-      idx.map((i) => v[i]),
-    );
-
   return {
-    ...data,
-    commits: idx.map((i) => data.commits[i]),
-    series,
-    originalLength: data.commits.length,
+    "Retry-After": String(LOADING_RETRY_AFTER_SECONDS),
   };
 }
 
-// Data fetching — streams response body directly instead of buffering
-async function fetchJsonl(url) {
-  const res = await fetch(url);
-  if (!res.ok) throw new Error(`Fetch failed: ${url} ${res.status}`);
-  return new Promise((resolve, reject) => {
-    const results = [];
-    const rl = readline.createInterface({
-      input: Readable.fromWeb(res.body),
-      crlfDelay: Infinity,
-    });
-    rl.on("line", (l) => {
-      if (l.trim())
-        try {
-          results.push(JSON.parse(l));
-        } catch {}
-    });
-    rl.on("close", () => resolve(results));
-    rl.on("error", reject);
-  });
-}
-
-function readLocalJsonl(fp) {
-  return new Promise((resolve, reject) => {
-    const results = [];
-    const rl = readline.createInterface({
-      input: fs.createReadStream(fp),
-      crlfDelay: Infinity,
-    });
-    rl.on("line", (l) => {
-      if (l.trim())
-        try {
-          results.push(JSON.parse(l));
-        } catch {}
-    });
-    rl.on("close", () => resolve(results));
-    rl.on("error", reject);
-  });
-}
-
-// Stream benchmark data record-by-record without buffering the entire dataset
-async function forEachBenchmark(callback) {
-  let stream;
-  if (USE_LOCAL_DATA) {
-    stream = fs.createReadStream(path.join(__dirname, "sample/data.json"));
-  } else {
-    const res = await fetch(DATA_URL);
-    if (!res.ok) throw new Error(`Fetch failed: ${DATA_URL} ${res.status}`);
-    stream = Readable.fromWeb(res.body).pipe(zlib.createGunzip());
-  }
-  return new Promise((resolve, reject) => {
-    const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
-    rl.on("line", (l) => {
-      if (l.trim())
-        try {
-          callback(JSON.parse(l));
-        } catch {}
-    });
-    rl.on("close", resolve);
-    rl.on("error", reject);
-  });
-}
-
-// Main data processing
-async function refresh() {
-  console.log("Refreshing data...");
-  const t0 = Date.now();
-
-  try {
-    // Load commits first (small dataset, must be fully in memory for indexing)
-    const commitsArr = USE_LOCAL_DATA
-      ? await readLocalJsonl(path.join(__dirname, "sample/commits.json"))
-      : await fetchJsonl(COMMITS_URL);
-
-    // Build commit index (O(1) lookup)
-    const commitMap = new Map(commitsArr.map((c) => [c.id, c]));
-    const commits = commitsArr.sort(
-      (a, b) => new Date(a.timestamp) - new Date(b.timestamp),
-    );
-    const commitIdx = new Map(commits.map((c, i) => [c.id, i]));
-
-    const groups = Object.fromEntries(GROUPS.map((g) => [g, new Map()]));
-    let missing = 0;
-    let benchmarkCount = 0;
-    const uncategorized = new Set();
-
-    // Stream benchmarks one record at a time to avoid loading all into memory
-    await forEachBenchmark((b) => {
-      benchmarkCount++;
-      const commit = b.commit || commitMap.get(b.commit_id);
-      if (!commit) {
-        missing++;
-        return;
-      }
-
-      const group = getGroup(b);
-      if (!group) {
-        uncategorized.add(b.name.split("/")[0]);
-        return;
-      }
-      if (!groups[group]) return;
-
-      // Random access names have the form: random-access/{dataset}/{pattern}/{format}
-      // Historical random access names: random-access/{format}
-      // Other benchmarks use: {query}/{series}
-      let seriesName, chartName;
-      const parts = b.name.split("/");
-      if (group === "Random Access" && parts.length === 4) {
-        chartName = `${parts[1]}/${parts[2]}`.toUpperCase().replace(/[_-]/g, " ");
-        seriesName = rename(parts[3] || "default");
-      } else if (group === "Random Access" && parts.length === 2) {
-        chartName = "RANDOM ACCESS";
-        seriesName = rename(parts[1] || "default");
-      } else {
-        seriesName = rename(parts[1] || "default");
-        chartName = formatQuery(parts[0]);
-      }
-      chartName = normalizeChartName(group, chartName);
-      if (chartName.includes("PARQUET-UNC")) return;
-
-      // Skip throughput metrics (keep only time/size)
-      if (b.name.includes(" throughput")) return;
-
-      let unit = b.unit;
-      if (!unit) {
-        if (b.name.toLowerCase().includes(" size/")) unit = "bytes";
-        else if (b.name.toLowerCase().includes(" ratio ")) unit = "ratio";
-        else unit = "ns";
-      }
-
-      const sortPos = parts[0].match(/q(\d+)$/i)?.[1]
-        ? parseInt(RegExp.$1, 10)
-        : 0;
-      const idx = commitIdx.get(commit.id);
-      if (idx === undefined) return;
-
-      let chart = groups[group].get(chartName);
-      if (!chart) {
-        let displayUnit = unit;
-        if (unit === "ns") displayUnit = "ms/iter";
-        else if (unit === "bytes") displayUnit = "MiB";
-        chart = {
-          sort_position: sortPos,
-          commits,
-          unit: displayUnit,
-          series: new Map(),
-        };
-        groups[group].set(chartName, chart);
-      }
-
-      if (!chart.series.has(seriesName)) {
-        chart.series.set(seriesName, Array(commits.length).fill(null));
-      }
-
-      // Convert values: ns -> ms, bytes -> MiB
-      let val = b.value;
-      if (unit === "ns" && typeof val === "number") {
-        val = val / 1e6; // ns to ms
-      } else if (unit === "bytes" && typeof val === "number") {
-        val = val / (1024 * 1024); // bytes to MiB
-      }
-
-      chart.series.get(seriesName)[idx] = { value: val };
-    });
-
-    console.log(
-      `Processed ${benchmarkCount} benchmarks, ${commitsArr.length} commits`,
-    );
-
-    // Log uncategorized benchmarks for debugging
-    if (uncategorized.size > 0) {
-      console.log(
-        `Uncategorized benchmark prefixes (${uncategorized.size}):`,
-        [...uncategorized].slice(0, 20).join(", "),
-      );
-    }
-
-    // Trim leading empty commits
-    let firstIdx = commits.length;
-    for (const gc of Object.values(groups)) {
-      for (const cd of gc.values()) {
-        for (const sd of cd.series.values()) {
-          const i = sd.findIndex((d) => d !== null);
-          if (i !== -1 && i < firstIdx) firstIdx = i;
-        }
-      }
-    }
-
-    if (firstIdx > 0 && firstIdx < commits.length) {
-      console.log(`Trimming ${firstIdx} empty commits`);
-      commits.splice(0, firstIdx);
-      for (const gc of Object.values(groups)) {
-        for (const cd of gc.values()) {
-          cd.commits = commits;
-          for (const [k, v] of cd.series) cd.series.set(k, v.slice(firstIdx));
-        }
-      }
-    }
-
-    // Sort charts within groups
-    for (const gc of Object.values(groups)) {
-      const sorted = [...gc.entries()].sort(
-        (a, b) =>
-          a[1].sort_position - b[1].sort_position || a[0].localeCompare(b[0]),
-      );
-      gc.clear();
-      for (const [k, v] of sorted) gc.set(k, v);
-    }
-
-    // Precompute downsampled versions
-    const downsampled = {};
-    for (const [gn, gc] of Object.entries(groups)) {
-      downsampled[gn] = {};
-      for (const [cn, cd] of gc) {
-        downsampled[gn][cn] = {
-          "1x": cd,
-          "2x": downsample(cd, 2),
-          "4x": downsample(cd, 4),
-          "8x": downsample(cd, 8),
-        };
-      }
-    }
-
-    // Count charts per group for logging
-    const groupCounts = Object.entries(groups)
-      .map(([n, g]) => `${n}: ${g.size}`)
-      .filter((s) => !s.endsWith(": 0"));
-    console.log("Charts per group:", groupCounts.join(", "));
-
-    store = {
-      commits,
-      groups,
-      metadata: buildMeta(groups, commits),
-      downsampled,
-      lastUpdated: new Date().toISOString(),
-    };
-    console.log(
-      `Refresh done in ${Date.now() - t0}ms (${missing} missing commits)`,
-    );
-  } catch (e) {
-    console.error("Refresh error:", e);
-  }
-}
-
-// Summary calculations
-function latestIdx(chart) {
-  for (let i = chart.commits.length - 1; i >= 0; i--) {
-    for (const s of chart.series.values()) if (s[i]?.value != null) return i;
-  }
-  return -1;
-}
-
-function calcSummary(name, charts) {
-  if (name === "Random Access") {
-    for (const q of charts.values()) {
-      const i = latestIdx(q);
-      if (i === -1) continue;
-      const vals = new Map();
-      for (const [n, d] of q.series)
-        if (d[i]?.value != null) vals.set(n, d[i].value);
-      if (!vals.size) continue;
-      const min = Math.min(...vals.values());
-      return {
-        type: "randomAccess",
-        title: "Random Access Performance",
-        rankings: [...vals]
-          .map(([n, t]) => ({ name: n, time: t, ratio: t / min }))
-          .sort((a, b) => a.time - b.time),
-        explanation: "Random access time | Ratio to fastest (lower is better)",
-      };
-    }
-    return null;
-  }
-
-  if (name === "Compression") {
-    const cc = charts.get("VORTEX:PARQUET ZSTD RATIO COMPRESS TIME");
-    const dc = charts.get("VORTEX:PARQUET ZSTD RATIO DECOMPRESS TIME");
-    if (!cc && !dc) return null;
-    const i = latestIdx(cc || dc);
-    if (i === -1) return null;
-    const collect = (c) =>
-      c
-        ? [...c.series]
-            .filter(([n]) => !n.toLowerCase().includes("wide table"))
-            .map(([, d]) => d[i]?.value)
-            .filter((v) => v > 0)
-            .map((v) => 1 / v)
-        : [];
-    return {
-      type: "compression",
-      title: "Compression Throughput vs Parquet",
-      compressRatio: geoMean(collect(cc)),
-      decompressRatio: geoMean(collect(dc)),
-      datasetCount: collect(cc).length,
-      explanation:
-        "Inverse geomean of Vortex/Parquet ratios (higher is better)",
-    };
-  }
-
-  if (name === "Compression Size") {
-    const c = charts.get("VORTEX:PARQUET ZSTD SIZE");
-    if (!c) return null;
-    const i = latestIdx(c);
-    if (i === -1) return null;
-    const ratios = [...c.series]
-      .filter(([n]) => !n.toLowerCase().includes("wide table"))
-      .map(([, d]) => d[i]?.value)
-      .filter((v) => v > 0);
-    return ratios.length
-      ? {
-          type: "compressionSize",
-          title: "Compression Size Summary",
-          minRatio: Math.min(...ratios),
-          meanRatio: geoMean(ratios),
-          maxRatio: Math.max(...ratios),
-          datasetCount: ratios.length,
-          explanation:
-            "Geomean of Vortex/Parquet size ratios (lower is better)",
-        }
-      : null;
-  }
-
-  if (
-    QUERY_SUITES.some(
-      (s) =>
-        !s.skip &&
-        (name === s.displayName || name.startsWith(s.displayName + " ")),
-    )
-  ) {
-    const all = new Map();
-    for (const q of charts.values())
-      for (const n of q.series.keys()) if (!all.has(n)) all.set(n, new Map());
-    for (const [qn, qd] of charts) {
-      for (const [sn, sd] of qd.series) {
-        for (let i = sd.length - 1; i >= 0; i--) {
-          if (sd[i]?.value != null) {
-            all.get(sn).set(qn, sd[i].value);
-            break;
-          }
-        }
-      }
-    }
-    if (!all.size) return null;
-
-    const scores = new Map();
-    for (const [sn, qr] of all) {
-      let total = 0,
-        max = 0;
-      for (const v of qr.values()) {
-        total += v;
-        max = Math.max(max, v);
-      }
-      const penalty = Math.max(300000, max) * 2;
-      const ratios = [];
-      for (const qn of charts.keys()) {
-        let base = Infinity;
-        for (const m of all.values())
-          if (m.has(qn)) base = Math.min(base, m.get(qn));
-        if (base < Infinity)
-          ratios.push((10 + (qr.get(qn) ?? penalty)) / (10 + base));
-      }
-      if (ratios.length)
-        scores.set(sn, { score: geoMean(ratios), totalRuntime: total });
-    }
-
-    return scores.size
-      ? {
-          type: "queryBenchmark",
-          title: "Performance Summary",
-          rankings: [...scores]
-            .map(([n, d]) => ({ name: n, ...d }))
-            .sort((a, b) => a.score - b.score),
-          explanation:
-            "Geomean of query time ratio to fastest (lower is better)",
-        }
-      : null;
-  }
-  return null;
-}
-
-function buildMeta(groups, commits) {
-  const meta = {};
-  for (const [gn, gc] of Object.entries(groups)) {
-    const charts = [...gc].map(([cn, cd]) => {
-      const latest = {};
-      for (const [sn, sd] of cd.series) {
-        for (let i = sd.length - 1; i >= 0; i--)
-          if (sd[i]?.value != null) {
-            latest[sn] = sd[i].value;
-            break;
-          }
-      }
-      return {
-        name: cn,
-        unit: cd.unit,
-        series: [...cd.series.keys()],
-        sortPosition: cd.sort_position,
-        totalPoints: cd.commits.length,
-        latestValues: latest,
-      };
-    });
-    meta[gn] = {
-      charts,
-      totalCharts: charts.length,
-      hasData: charts.length > 0,
-      summary: calcSummary(gn, gc),
-    };
-  }
+function getLoadingPayload() {
+  const status = benchmarks.status;
   return {
-    groups: meta,
-    totalCommits: commits.length,
-    commits: commits.map((c) => ({
-      id: c.id,
-      message: c.message?.split("\n")[0] || "",
-      timestamp: c.timestamp,
-      author: c.author?.name || "Unknown",
-    })),
-    lastUpdated: new Date().toISOString(),
+    error: status.state === "error" ? "Initial refresh failed" : "Loading",
+    status: status.state,
+    ready: status.ready,
+    refreshing: status.refreshing,
+    hasData: status.hasData,
+    lastUpdated: status.lastUpdated,
+    lastRefreshStartedAt: status.lastRefreshStartedAt,
+    lastRefreshCompletedAt: status.lastRefreshCompletedAt,
+    lastRefreshError: status.lastRefreshError,
   };
 }
 
-// HTTP handlers
-const json = (res, code, data) => {
-  res.writeHead(code, {
-    "Content-Type": "application/json",
-    "Access-Control-Allow-Origin": "*",
-  });
-  res.end(JSON.stringify(data));
-};
+function triggerRefresh() {
+  console.log("Refreshing data...");
+  benchmarks.refresh().catch(() => {});
+}
 
-function serveFile(res, fp) {
-  fs.readFile(fp, (err, data) => {
+function serveFile(res, filePath) {
+  fs.readFile(filePath, (err, data) => {
     if (err) {
       res.writeHead(err.code === "ENOENT" ? 404 : 500);
-      return res.end(err.code === "ENOENT" ? "Not Found" : "Error");
+      res.end(err.code === "ENOENT" ? "Not Found" : "Error");
+      return;
     }
-    const ext = path.extname(fp).toLowerCase();
-    const hdrs = { "Content-Type": MIME[ext] || "application/octet-stream" };
+
+    const ext = path.extname(filePath).toLowerCase();
+    const headers = { "Content-Type": MIME[ext] || "application/octet-stream" };
     if (ext === ".js") {
-      hdrs["Cache-Control"] = "no-cache";
-      hdrs["Pragma"] = "no-cache";
+      headers["Cache-Control"] = "no-cache";
+      headers.Pragma = "no-cache";
     }
-    res.writeHead(200, hdrs);
+
+    res.writeHead(200, headers);
     res.end(data);
   });
 }
 
-function handleData(res, group, chart, start, end, last, startIdx, endIdx) {
-  if (!store.downsampled) return json(res, 503, { error: "Loading" });
-  const gd = store.downsampled[group];
-  if (!gd) return json(res, 404, { error: "Group not found" });
-  const cv = gd[chart];
-  if (!cv) return json(res, 404, { error: "Chart not found" });
-
-  const full = cv["1x"];
-  const ts = (c) =>
-    typeof c?.timestamp === "number"
-      ? c.timestamp
-      : new Date(c?.timestamp).getTime();
-
-  let si = 0,
-    ei = full.commits.length - 1;
-
-  // Support "last=N" parameter to get the last N commits
-  if (last && !start && !end && startIdx === null && endIdx === null) {
-    const n = parseInt(last, 10);
-    if (n > 0 && n < full.commits.length) {
-      si = full.commits.length - n;
-    }
-  } else if (startIdx !== null || endIdx !== null) {
-    // Support index-based range (startIdx, endIdx)
-    if (startIdx !== null) si = Math.max(0, parseInt(startIdx, 10));
-    if (endIdx !== null)
-      ei = Math.min(full.commits.length - 1, parseInt(endIdx, 10));
-  } else {
-    // Timestamp-based range
-    if (start) {
-      const t = +start,
-        i = full.commits.findIndex((c) => ts(c) >= t);
-      if (i !== -1) si = i;
+async function handleData(res, groupName, chartName, params) {
+  try {
+    const payload = await benchmarks.getChartData(groupName, chartName, {
+      start: params.get("start"),
+      end: params.get("end"),
+      last: params.get("last"),
+      startIdx: params.has("startIdx") ? params.get("startIdx") : null,
+      endIdx: params.has("endIdx") ? params.get("endIdx") : null,
+    });
+    json(res, 200, payload);
+  } catch (error) {
+    if (error.message === "Loading") {
+      const status = benchmarks.status;
+      json(res, 503, getLoadingPayload(), loadingHeaders(status));
+      return;
     }
-    if (end) {
-      const t = +end;
-      for (let i = ei; i >= 0; i--)
-        if (ts(full.commits[i]) <= t) {
-          ei = i;
-          break;
-        }
+    if (error.statusCode === 404) {
+      json(res, 404, { error: error.message });
+      return;
     }
+    console.error("Data request error:", error);
+    json(res, 500, { error: "Internal server error" });
   }
-
-  const len = ei - si + 1;
-  const ver =
-    len <= MAX_POINTS
-      ? "1x"
-      : len <= MAX_POINTS * 2
-        ? "2x"
-        : len <= MAX_POINTS * 4
-          ? "4x"
-          : "8x";
-  const cd = cv[ver];
-  const val = (d) => d?.value ?? (typeof d === "number" ? d : null);
-
-  let commits, series;
-  if (ver === "1x") {
-    commits = full.commits.slice(si, ei + 1);
-    series = Object.fromEntries(
-      [...full.series].map(([n, d]) => [n, d.slice(si, ei + 1).map(val)]),
-    );
-  } else {
-    const s = +ver[0],
-      dsi = Math.floor(si / s),
-      dei = Math.min(Math.ceil(ei / s), cd.commits.length - 1);
-    commits = cd.commits.slice(dsi, dei + 1);
-    series = Object.fromEntries(
-      [...cd.series].map(([n, d]) => [n, d.slice(dsi, dei + 1).map(val)]),
-    );
-  }
-
-  json(res, 200, {
-    group,
-    chart,
-    unit: cd.unit,
-    downsampleLevel: ver,
-    originalLength: full.commits.length,
-    requestedRange: { startIndex: si, endIndex: ei, length: len },
-    commits: commits.map((c) => ({
-      id: c.id,
-      message: c.message?.split("\n")[0] || "",
-      timestamp: c.timestamp,
-      author: c.author?.name || "Unknown",
-      url: c.url,
-    })),
-    series,
-  });
 }
 
 const server = http.createServer((req, res) => {
-  const [path_, qs] = req.url.split("?");
-  const params = new URLSearchParams(qs || "");
+  const [pathName, rawQuery] = req.url.split("?");
+  const params = new URLSearchParams(rawQuery || "");
 
   if (req.method === "OPTIONS") {
     res.writeHead(204, {
@@ -735,41 +137,79 @@ const server = http.createServer((req, res) => {
       "Access-Control-Allow-Methods": "GET",
       "Access-Control-Allow-Headers": "Content-Type",
     });
-    return res.end();
+    res.end();
+    return;
   }
 
-  if (path_ === "/api/metadata")
-    return store.metadata
-      ? json(res, 200, store.metadata)
-      : json(res, 503, { error: "Loading" });
-
-  if (path_.startsWith("/api/data/")) {
-    const p = path_.slice(10).split("/");
-    return handleData(
+  if (pathName === "/api/metadata") {
+    const metadata = benchmarks.metadata;
+    const status = benchmarks.status;
+    json(
       res,
-      decodeURIComponent(p[0] || ""),
-      decodeURIComponent(p.slice(1).join("/") || ""),
-      params.get("start"),
-      params.get("end"),
-      params.get("last"),
-      params.has("startIdx") ? params.get("startIdx") : null,
-      params.has("endIdx") ? params.get("endIdx") : null,
+      metadata ? 200 : 503,
+      metadata || getLoadingPayload(),
+      metadata ? {} : loadingHeaders(status),
     );
+    return;
+  }
+
+  if (pathName === "/api/health") {
+    const status = benchmarks.status;
+    json(res, status.ready ? 200 : 503, status, loadingHeaders(status));
+    return;
+  }
+
+  if (pathName.startsWith("/api/data/")) {
+    const segments = pathName.slice(10).split("/");
+    handleData(
+      res,
+      decodeURIComponent(segments[0] || ""),
+      decodeURIComponent(segments.slice(1).join("/") || ""),
+      params,
+    ).catch((error) => {
+      console.error("Unhandled data handler error:", error);
+      json(res, 500, { error: "Internal server error" });
+    });
+    return;
   }
 
-  const fp = path.join(__dirname, "dist", path_ === "/" ? "index.html" : path_);
-  if (!fp.startsWith(__dirname) || fp.includes("/sample/")) {
+  const filePath = path.join(
+    __dirname,
+    "dist",
+    pathName === "/" ? "index.html" : pathName,
+  );
+  if (!filePath.startsWith(__dirname) || filePath.includes("/sample/")) {
     res.writeHead(403);
-    return res.end("Forbidden");
+    res.end("Forbidden");
+    return;
   }
-  serveFile(res, fp);
+  serveFile(res, filePath);
 });
 
-async function start() {
+function start() {
   console.log("Starting server...");
-  await refresh();
-  setInterval(refresh, REFRESH_INTERVAL);
-  server.listen(PORT, () => console.log(`Server at http://localhost:${PORT}`));
+  server.listen(PORT, () => {
+    console.log(`Server at http://localhost:${PORT}`);
+  });
+  triggerRefresh();
+  refreshIntervalId = setInterval(triggerRefresh, Number(REFRESH_INTERVAL));
+}
+
+async function shutdown() {
+  if (refreshIntervalId) {
+    clearInterval(refreshIntervalId);
+    refreshIntervalId = null;
+  }
+  await benchmarks.close();
+  server.close();
 }
 
-start().catch(console.error);
+process.on("SIGINT", () => {
+  shutdown().finally(() => process.exit(0));
+});
+
+process.on("SIGTERM", () => {
+  shutdown().finally(() => process.exit(0));
+});
+
+start();
diff --git a/benchmarks-website/src/App.jsx b/benchmarks-website/src/App.jsx
index 0df05bebf01..f5edf0e92e2 100644
--- a/benchmarks-website/src/App.jsx
+++ b/benchmarks-website/src/App.jsx
@@ -1,15 +1,13 @@
-import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
+import React, { useState, useEffect, useCallback, useMemo } from 'react';
 import Header from './components/Header';
 import Sidebar from './components/Sidebar';
 import BenchmarkSection from './components/BenchmarkSection';
 import Modal from './components/Modal';
-import { fetchMetadata } from './api';
 import { BENCHMARK_CONFIGS, CATEGORY_TAGS } from './config';
+import useBenchmarkMetadata from './hooks/useBenchmarkMetadata';
 
 export default function App() {
-  const [metadata, setMetadata] = useState(null);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(null);
+  const { metadata, loading, error } = useBenchmarkMetadata();
   const [expandedGroups, setExpandedGroups] = useState(new Set());
   const [sidebarOpen, setSidebarOpen] = useState(false);
   const [categoryFilter, setCategoryFilter] = useState('all');
@@ -17,28 +15,15 @@ export default function App() {
   const [viewMode, setViewMode] = useState('grid');
   const [modalChart, setModalChart] = useState(null);
   const [showBackToTop, setShowBackToTop] = useState(false);
-  const metadataFetched = useRef(false);
 
   useEffect(() => {
-    if (metadataFetched.current) return;
-    metadataFetched.current = true;
+    if (!metadata?.groups) return;
 
-    async function loadMetadata() {
-      try {
-        const data = await fetchMetadata();
-        setMetadata(data);
-        const params = new URLSearchParams(window.location.search);
-        if (params.get('expanded') === 'true' && data?.groups) {
-          setExpandedGroups(new Set(Object.keys(data.groups)));
-        }
-      } catch (err) {
-        setError(err.message);
-      } finally {
-        setLoading(false);
-      }
+    const params = new URLSearchParams(window.location.search);
+    if (params.get('expanded') === 'true') {
+      setExpandedGroups(new Set(Object.keys(metadata.groups)));
     }
-    loadMetadata();
-  }, []);
+  }, [metadata]);
 
   useEffect(() => {
     const handleScroll = () => {
@@ -55,7 +40,6 @@ export default function App() {
     const hash = window.location.hash;
     if (hash && hash.startsWith('#group-')) {
       const groupId = hash.slice(1); // Remove the '#'
-      const groupName = groupId.replace('group-', '').replace(/-/g, ' ');
 
       // Find the matching group (case-insensitive, handle hyphenated names)
       const matchingGroup = Object.keys(metadata.groups).find(name =>
diff --git a/benchmarks-website/src/api.js b/benchmarks-website/src/api.js
index 042ea7a6f0b..7fbcf781ce3 100644
--- a/benchmarks-website/src/api.js
+++ b/benchmarks-website/src/api.js
@@ -1,9 +1,53 @@
 const API_BASE = '';
 
-export async function fetchMetadata() {
-  const response = await fetch(`${API_BASE}/api/metadata`);
-  if (!response.ok) throw new Error(`Failed to fetch metadata: ${response.status}`);
-  return response.json();
+function parseRetryAfterMs(value) {
+  if (!value) return null;
+
+  const seconds = Number.parseInt(value, 10);
+  if (Number.isFinite(seconds) && seconds >= 0) {
+    return seconds * 1000;
+  }
+
+  const retryAt = Date.parse(value);
+  if (Number.isNaN(retryAt)) {
+    return null;
+  }
+
+  return Math.max(0, retryAt - Date.now());
+}
+
+async function readResponse(response) {
+  const contentType = response.headers.get('content-type') || '';
+
+  if (contentType.includes('application/json')) {
+    return response.json();
+  }
+
+  const text = await response.text();
+  return text ? { error: text } : null;
+}
+
+export async function fetchMetadata(options = {}) {
+  const response = await fetch(`${API_BASE}/api/metadata`, {
+    cache: 'no-store',
+    signal: options.signal,
+  });
+  const payload = await readResponse(response);
+
+  if (!response.ok) {
+    const message = payload?.lastRefreshError
+      ? `Failed to fetch metadata: ${payload.lastRefreshError}`
+      : payload?.error
+        ? `Failed to fetch metadata: ${payload.error}`
+        : `Failed to fetch metadata: ${response.status}`;
+    const error = new Error(message);
+    error.status = response.status;
+    error.payload = payload;
+    error.retryAfterMs = parseRetryAfterMs(response.headers.get('retry-after'));
+    throw error;
+  }
+
+  return payload;
 }
 
 export async function fetchChartData(groupName, chartName, options = {}) {
@@ -35,7 +79,19 @@ export async function fetchChartData(groupName, chartName, options = {}) {
 
   if (params.toString()) url += '?' + params.toString();
 
-  const response = await fetch(url);
-  if (!response.ok) throw new Error(`Failed to fetch chart data: ${response.status}`);
-  return response.json();
+  const response = await fetch(url, { cache: 'no-store' });
+  const payload = await readResponse(response);
+
+  if (!response.ok) {
+    const message = payload?.error
+      ? `Failed to fetch chart data: ${payload.error}`
+      : `Failed to fetch chart data: ${response.status}`;
+    const error = new Error(message);
+    error.status = response.status;
+    error.payload = payload;
+    error.retryAfterMs = parseRetryAfterMs(response.headers.get('retry-after'));
+    throw error;
+  }
+
+  return payload;
 }
diff --git a/benchmarks-website/src/components/ChartContainer.jsx b/benchmarks-website/src/components/ChartContainer.jsx
index 1dfb193e836..681192a09a4 100644
--- a/benchmarks-website/src/components/ChartContainer.jsx
+++ b/benchmarks-website/src/components/ChartContainer.jsx
@@ -277,7 +277,7 @@ export default function ChartContainer({
 
     const { series, commits } = chartData;
     const datasets = [];
-    const labels = commits.map(c => formatDate(c.timestamp));
+    const labels = commits.map(c => formatDate(c?.timestamp));
 
     Object.entries(series).forEach(([seriesName, points]) => {
       // Apply removed datasets filter
diff --git a/benchmarks-website/src/components/Header.jsx b/benchmarks-website/src/components/Header.jsx
index 95edda98560..952cfd80087 100644
--- a/benchmarks-website/src/components/Header.jsx
+++ b/benchmarks-website/src/components/Header.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
 
 export default function Header({
   sidebarOpen,
@@ -25,8 +24,8 @@ export default function Header({
           </button>
           <a href="/" className="logo-link">
             <picture>
-                <source srcset="/vortex_black_nobg.svg" media="(prefers-color-scheme: light)"/>
-                <source srcset="/vortex_white_nobg.svg" media="(prefers-color-scheme: dark)"/>
+                <source srcSet="/vortex_black_nobg.svg" media="(prefers-color-scheme: light)"/>
+                <source srcSet="/vortex_white_nobg.svg" media="(prefers-color-scheme: dark)"/>
                 <img src="/vortex_black_nobg.svg" alt="Vortex" className="site-logo"/>
             </picture>
           </a>
diff --git a/benchmarks-website/src/components/Modal.jsx b/benchmarks-website/src/components/Modal.jsx
index b9bb7d419b1..f118e18f795 100644
--- a/benchmarks-website/src/components/Modal.jsx
+++ b/benchmarks-website/src/components/Modal.jsx
@@ -95,7 +95,7 @@ export default function Modal({ chartData, onClose }) {
 
     const { series, commits } = data;
     const datasets = [];
-    const labels = commits.map(c => formatDate(c.timestamp));
+    const labels = commits.map(c => formatDate(c?.timestamp));
 
     Object.entries(series).forEach(([seriesName, points]) => {
       if (config?.removedDatasets?.has(seriesName)) return;
diff --git a/benchmarks-website/src/hooks/useBenchmarkMetadata.js b/benchmarks-website/src/hooks/useBenchmarkMetadata.js
new file mode 100644
index 00000000000..f287560e0c7
--- /dev/null
+++ b/benchmarks-website/src/hooks/useBenchmarkMetadata.js
@@ -0,0 +1,75 @@
+import { useEffect, useState } from 'react';
+import { fetchMetadata } from '../api';
+
+const METADATA_RETRY_DELAY_MS = 1000;
+
+const initialState = {
+  metadata: null,
+  loading: true,
+  error: null,
+};
+
+export default function useBenchmarkMetadata() {
+  const [state, setState] = useState(initialState);
+
+  useEffect(() => {
+    let active = true;
+    let retryTimer = null;
+    let activeController = null;
+
+    const clearRetry = () => {
+      if (retryTimer !== null) {
+        window.clearTimeout(retryTimer);
+        retryTimer = null;
+      }
+    };
+
+    const loadMetadata = async () => {
+      clearRetry();
+      activeController?.abort();
+      activeController = new AbortController();
+
+      try {
+        const metadata = await fetchMetadata({ signal: activeController.signal });
+        if (!active) return;
+
+        setState({
+          metadata,
+          loading: false,
+          error: null,
+        });
+      } catch (error) {
+        if (!active || error.name === 'AbortError') return;
+
+        if (error.status === 503 && error.payload?.status !== 'error') {
+          setState((current) => ({
+            metadata: current.metadata,
+            loading: true,
+            error: null,
+          }));
+          retryTimer = window.setTimeout(
+            loadMetadata,
+            error.retryAfterMs ?? METADATA_RETRY_DELAY_MS,
+          );
+          return;
+        }
+
+        setState({
+          metadata: null,
+          loading: false,
+          error: error.message,
+        });
+      }
+    };
+
+    loadMetadata();
+
+    return () => {
+      active = false;
+      clearRetry();
+      activeController?.abort();
+    };
+  }, []);
+
+  return state;
+}
diff --git a/benchmarks-website/store/benchmark-store.js b/benchmarks-website/store/benchmark-store.js
new file mode 100644
index 00000000000..8f112d94f4d
--- /dev/null
+++ b/benchmarks-website/store/benchmark-store.js
@@ -0,0 +1,354 @@
+import { DuckDBInstance } from "@duckdb/node-api";
+import { prepareInputFiles } from "./cache.js";
+import { queryRows, withConnection } from "./db.js";
+import { downsample, downsampleLevel } from "./downsample.js";
+import { buildMetadata, collectDiagnostics } from "./metadata.js";
+import { buildBootstrapSql } from "./sql.js";
+import { firstLine } from "./utils.js";
+
+const CHART_RANGE_QUERY = `
+  with requested_commits as (
+    select commit_idx
+    from active_commits
+    where commit_idx between $start_idx and $end_idx
+  ),
+  requested_series as (
+    select distinct series_name
+    from benchmark_points_active
+    where group_name = $group_name
+      and chart_name = $chart_name
+  ),
+  dense_points as (
+    select
+      rs.series_name,
+      rc.commit_idx,
+      bpa.value
+    from requested_series rs
+    cross join requested_commits rc
+    left join benchmark_points_active bpa
+      on bpa.group_name = $group_name
+     and bpa.chart_name = $chart_name
+     and bpa.series_name = rs.series_name
+     and bpa.commit_idx = rc.commit_idx
+  )
+  select
+    series_name,
+    list(value order by commit_idx) as values
+  from dense_points
+  group by 1
+  order by 1
+`;
+
+async function buildStoreState({ instance, inputs }) {
+  await withConnection(instance, async (connection) => {
+    await connection.run("begin transaction");
+    try {
+      await connection.run(buildBootstrapSql(inputs.dataPath, inputs.commitsPath));
+      await connection.run("commit");
+    } catch (error) {
+      try {
+        await connection.run("rollback");
+      } catch {
+        // Best effort: keep the previous committed state if the rebuild failed.
+      }
+      throw error;
+    }
+  });
+
+  const lastUpdated = new Date().toISOString();
+  const [{ commits, metadata, chartIndex }, diagnostics] = await Promise.all([
+    buildMetadata(instance, lastUpdated),
+    withConnection(instance, collectDiagnostics),
+  ]);
+
+  return {
+    commits,
+    metadata,
+    chartIndex,
+    lastUpdated,
+    diagnostics,
+  };
+}
+
+function commitTimestamp(commit) {
+  return typeof commit.timestamp === "number"
+    ? commit.timestamp
+    : new Date(commit.timestamp).getTime();
+}
+
+function resolveRequestedRange(commits, options) {
+  const totalCommits = commits.length;
+  let startIdx = 0;
+  let endIdx = totalCommits - 1;
+
+  if (
+    options.last &&
+    !options.start &&
+    !options.end &&
+    options.startIdx == null &&
+    options.endIdx == null
+  ) {
+    const count = Number.parseInt(options.last, 10);
+    if (count > 0 && count < totalCommits) {
+      startIdx = totalCommits - count;
+    }
+  } else if (options.startIdx != null || options.endIdx != null) {
+    if (options.startIdx != null) {
+      startIdx = Math.max(0, Number.parseInt(options.startIdx, 10));
+    }
+    if (options.endIdx != null) {
+      endIdx = Math.min(totalCommits - 1, Number.parseInt(options.endIdx, 10));
+    }
+  } else {
+    if (options.start) {
+      const startTs = Number(options.start);
+      const idx = commits.findIndex((commit) => commitTimestamp(commit) >= startTs);
+      if (idx !== -1) startIdx = idx;
+    }
+
+    if (options.end) {
+      const endTs = Number(options.end);
+      for (let i = endIdx; i >= 0; i--) {
+        if (commitTimestamp(commits[i]) <= endTs) {
+          endIdx = i;
+          break;
+        }
+      }
+    }
+  }
+
+  return {
+    startIdx: Math.max(0, Math.min(startIdx, totalCommits - 1)),
+    endIdx: Math.max(startIdx, Math.min(endIdx, totalCommits - 1)),
+  };
+}
+
+function serializeRequestedCommits(commits, startIdx, endIdx) {
+  return commits
+    .slice(startIdx, endIdx + 1)
+    .map(({ id, message, timestamp, author, url }) => ({
+      id,
+      message: firstLine(message),
+      timestamp,
+      author,
+      url,
+    }));
+}
+
+function buildSeriesMap(rows) {
+  return new Map(
+    rows.map((row) => [
+      row.series_name,
+      (row.values || []).map((value) => (value == null ? null : value)),
+    ]),
+  );
+}
+
+export class BenchmarkStore {
+  constructor(options) {
+    this.options = options;
+    this.state = null;
+    this.instance = null;
+    this.instancePromise = null;
+    this.refreshPromise = null;
+    this.remoteCheckTimer = null;
+    this.lastRefreshStartedAt = null;
+    this.lastRefreshCompletedAt = null;
+    this.lastRefreshError = null;
+  }
+
+  get metadata() {
+    return this.state?.metadata || null;
+  }
+
+  get status() {
+    let state = "idle";
+
+    if (this.state) {
+      if (this.refreshPromise) {
+        state = "refreshing";
+      } else if (this.lastRefreshError) {
+        state = "stale";
+      } else {
+        state = "ready";
+      }
+    } else if (this.lastRefreshError) {
+      state = "error";
+    } else if (this.refreshPromise) {
+      state = "loading";
+    }
+
+    return {
+      state,
+      ready: Boolean(this.state),
+      refreshing: Boolean(this.refreshPromise),
+      hasData: Boolean(this.state?.metadata),
+      lastUpdated: this.state?.lastUpdated || null,
+      lastRefreshStartedAt: this.lastRefreshStartedAt,
+      lastRefreshCompletedAt: this.lastRefreshCompletedAt,
+      lastRefreshError: this.lastRefreshError,
+    };
+  }
+
+  async getInstance() {
+    if (this.instance) return this.instance;
+
+    if (!this.instancePromise) {
+      this.instancePromise = DuckDBInstance.create(":memory:")
+        .then((instance) => {
+          this.instance = instance;
+          return instance;
+        })
+        .finally(() => {
+          this.instancePromise = null;
+        });
+    }
+
+    return this.instancePromise;
+  }
+
+  scheduleRemoteCheck() {
+    if (this.remoteCheckTimer) return;
+
+    this.remoteCheckTimer = setTimeout(() => {
+      this.remoteCheckTimer = null;
+      this.refresh({ forceRemoteCheck: true }).catch(() => {});
+    }, 0);
+  }
+
+  async refresh({ forceRemoteCheck = false } = {}) {
+    if (this.refreshPromise) return this.refreshPromise;
+
+    this.lastRefreshStartedAt = new Date().toISOString();
+    this.lastRefreshError = null;
+    this.refreshPromise = (async () => {
+      const startedAt = Date.now();
+      const hasState = Boolean(this.state);
+      const inputs = await prepareInputFiles({
+        ...this.options,
+        preferCached: !hasState && !forceRemoteCheck,
+        forceRemoteCheck,
+      });
+
+      if (hasState && !inputs.changed) {
+        this.lastRefreshCompletedAt = new Date().toISOString();
+        console.log(
+          `Refresh skipped in ${Date.now() - startedAt}ms (${inputs.source})`,
+        );
+        return;
+      }
+
+      const instance = await this.getInstance();
+      const nextState = await buildStoreState({ instance, inputs });
+
+      console.log(
+        `Processed ${nextState.diagnostics.benchmarkCount} benchmarks, ${nextState.commits.length} commits`,
+      );
+
+      if (nextState.diagnostics.uncategorized.length > 0) {
+        console.log(
+          `Uncategorized benchmark prefixes (${nextState.diagnostics.uncategorized.length}):`,
+          nextState.diagnostics.uncategorized.join(", "),
+        );
+      }
+
+      const chartCounts = nextState.diagnostics.groupCounts
+        .map((row) => `${row.group_name}: ${row.chart_count}`)
+        .filter((entry) => !entry.endsWith(": 0"));
+      console.log("Charts per group:", chartCounts.join(", "));
+
+      this.state = nextState;
+      this.lastRefreshCompletedAt = nextState.lastUpdated;
+
+      console.log(
+        `Refresh done in ${Date.now() - startedAt}ms (${nextState.diagnostics.missingCommits} missing commits, source: ${inputs.source})`,
+      );
+
+      if (inputs.deferRemoteCheck) {
+        console.log(
+          "Serving cached benchmark files for startup; scheduling remote revalidation",
+        );
+        this.scheduleRemoteCheck();
+      }
+    })()
+      .catch((error) => {
+        this.lastRefreshError = error?.message || String(error);
+        console.error("Refresh error:", error);
+        throw error;
+      })
+      .finally(() => {
+        this.refreshPromise = null;
+      });
+
+    return this.refreshPromise;
+  }
+
+  async close() {
+    if (this.remoteCheckTimer) {
+      clearTimeout(this.remoteCheckTimer);
+      this.remoteCheckTimer = null;
+    }
+
+    this.state = null;
+
+    if (this.instance) {
+      this.instance.closeSync();
+      this.instance = null;
+    }
+  }
+
+  async getChartData(groupName, chartName, options = {}) {
+    if (!this.state) {
+      throw new Error("Loading");
+    }
+
+    const chart = this.state.chartIndex.get(`${groupName}\u0000${chartName}`);
+    if (!chart) {
+      const error = new Error("Chart not found");
+      error.statusCode = 404;
+      throw error;
+    }
+
+    const { commits, metadata } = this.state;
+    const instance = this.instance;
+    if (!instance) {
+      throw new Error("Loading");
+    }
+
+    const { startIdx, endIdx } = resolveRequestedRange(commits, options);
+    const rows = await withConnection(instance, async (connection) =>
+      queryRows(connection, CHART_RANGE_QUERY, {
+        group_name: groupName,
+        chart_name: chartName,
+        start_idx: startIdx,
+        end_idx: endIdx,
+      }),
+    );
+
+    const requestedCommits = serializeRequestedCommits(commits, startIdx, endIdx);
+    const selected = {
+      commits: requestedCommits,
+      series: buildSeriesMap(rows),
+    };
+    const level = downsampleLevel(requestedCommits.length);
+    const sampled =
+      level === "1x"
+        ? selected
+        : downsample(selected, Number.parseInt(level, 10));
+
+    return {
+      group: groupName,
+      chart: chartName,
+      unit: chart.unit,
+      downsampleLevel: level,
+      originalLength: metadata.totalCommits,
+      requestedRange: {
+        startIndex: startIdx,
+        endIndex: endIdx,
+        length: endIdx - startIdx + 1,
+      },
+      commits: sampled.commits,
+      series: Object.fromEntries(sampled.series),
+    };
+  }
+}
diff --git a/benchmarks-website/store/cache.js b/benchmarks-website/store/cache.js
new file mode 100644
index 00000000000..7141b353b24
--- /dev/null
+++ b/benchmarks-website/store/cache.js
@@ -0,0 +1,174 @@
+import fs from "fs";
+import fsp from "fs/promises";
+import os from "os";
+import path from "path";
+import { Readable } from "stream";
+import { pipeline } from "stream/promises";
+import { fileURLToPath } from "url";
+import { DEFAULT_CACHE_DIR_NAME } from "./constants.js";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const WEBSITE_DIR = path.dirname(__dirname);
+
+function cachePaths(cacheDir) {
+  return {
+    dataPath: path.join(cacheDir, "data.json.gz"),
+    commitsPath: path.join(cacheDir, "commits.json"),
+    manifestPath: path.join(cacheDir, "manifest.json"),
+  };
+}
+
+async function pathExists(filePath) {
+  try {
+    await fsp.access(filePath);
+    return true;
+  } catch {
+    return false;
+  }
+}
+
+async function readJsonFile(filePath) {
+  try {
+    const raw = await fsp.readFile(filePath, "utf8");
+    return JSON.parse(raw);
+  } catch (error) {
+    if (error.code === "ENOENT") return null;
+    throw error;
+  }
+}
+
+async function writeJsonFileAtomic(filePath, value) {
+  const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
+  await fsp.writeFile(tempPath, JSON.stringify(value, null, 2));
+  await fsp.rename(tempPath, filePath);
+}
+
+async function downloadToFile(url, destination, metadata = {}) {
+  const headers = {};
+  if (metadata.etag) headers["If-None-Match"] = metadata.etag;
+  if (metadata.lastModified) headers["If-Modified-Since"] = metadata.lastModified;
+
+  const response = await fetch(url, { headers });
+  if (response.status === 304) {
+    return {
+      changed: false,
+      metadata: {
+        ...metadata,
+        checkedAt: new Date().toISOString(),
+      },
+    };
+  }
+
+  if (!response.ok) {
+    throw new Error(`Fetch failed: ${url} ${response.status}`);
+  }
+
+  if (!response.body) {
+    throw new Error(`Fetch failed: ${url} empty body`);
+  }
+
+  const tempPath = `${destination}.tmp-${process.pid}-${Date.now()}`;
+  try {
+    await pipeline(Readable.fromWeb(response.body), fs.createWriteStream(tempPath));
+    await fsp.rename(tempPath, destination);
+  } finally {
+    if (await pathExists(tempPath)) {
+      await fsp.unlink(tempPath);
+    }
+  }
+
+  return {
+    changed: true,
+    metadata: {
+      url,
+      etag: response.headers.get("etag"),
+      lastModified: response.headers.get("last-modified"),
+      fetchedAt: new Date().toISOString(),
+      checkedAt: new Date().toISOString(),
+    },
+  };
+}
+
+export async function prepareInputFiles({
+  dataUrl,
+  commitsUrl,
+  useLocalData,
+  cacheDir,
+  preferCached = false,
+  forceRemoteCheck = false,
+}) {
+  if (useLocalData) {
+    return {
+      dataPath: path.join(WEBSITE_DIR, "sample/data.json"),
+      commitsPath: path.join(WEBSITE_DIR, "sample/commits.json"),
+      changed: true,
+      source: "local",
+      deferRemoteCheck: false,
+    };
+  }
+
+  const resolvedCacheDir =
+    cacheDir || path.join(os.tmpdir(), DEFAULT_CACHE_DIR_NAME);
+  await fsp.mkdir(resolvedCacheDir, { recursive: true });
+
+  const { dataPath, commitsPath, manifestPath } = cachePaths(resolvedCacheDir);
+  const manifest = (await readJsonFile(manifestPath)) || {};
+  const [hasDataFile, hasCommitsFile] = await Promise.all([
+    pathExists(dataPath),
+    pathExists(commitsPath),
+  ]);
+  const hasCachedFiles = hasDataFile && hasCommitsFile;
+
+  if (preferCached && hasCachedFiles && !forceRemoteCheck) {
+    return {
+      dataPath,
+      commitsPath,
+      changed: true,
+      source: "cache",
+      deferRemoteCheck: true,
+    };
+  }
+
+  try {
+    const [dataResult, commitsResult] = await Promise.all([
+      downloadToFile(dataUrl, dataPath, manifest.data || {}),
+      downloadToFile(commitsUrl, commitsPath, manifest.commits || {}),
+    ]);
+
+    await writeJsonFileAtomic(manifestPath, {
+      version: 1,
+      dataUrl,
+      commitsUrl,
+      data: dataResult.metadata,
+      commits: commitsResult.metadata,
+      updatedAt: new Date().toISOString(),
+    });
+
+    return {
+      dataPath,
+      commitsPath,
+      changed: !hasCachedFiles || dataResult.changed || commitsResult.changed,
+      source:
+        !hasCachedFiles || dataResult.changed || commitsResult.changed
+          ? "remote"
+          : "cache",
+      deferRemoteCheck: false,
+    };
+  } catch (error) {
+    if (hasCachedFiles) {
+      console.warn(
+        `Falling back to cached benchmark files after refresh failed: ${error.message}`,
+      );
+      return {
+        dataPath,
+        commitsPath,
+        changed: false,
+        source: "stale-cache",
+        deferRemoteCheck: false,
+      };
+    }
+
+    throw error;
+  }
+}
diff --git a/benchmarks-website/store/constants.js b/benchmarks-website/store/constants.js
new file mode 100644
index 00000000000..fa5799bc3e5
--- /dev/null
+++ b/benchmarks-website/store/constants.js
@@ -0,0 +1,20 @@
+import { FAN_OUT_GROUPS, QUERY_SUITES } from "../src/config.js";
+
+export const MAX_POINTS = 200;
+export const DEFAULT_CACHE_DIR_NAME = "vortex-benchmarks-website-cache";
+
+export const GROUPS = [
+  "Random Access",
+  "Compression",
+  "Compression Size",
+  ...QUERY_SUITES.filter((suite) => !suite.skip && !suite.fanOut).map(
+    (suite) => suite.displayName,
+  ),
+  ...FAN_OUT_GROUPS,
+];
+
+export const QUERY_GROUP_EXCLUSIONS = new Set([
+  "Random Access",
+  "Compression",
+  "Compression Size",
+]);
diff --git a/benchmarks-website/store/db.js b/benchmarks-website/store/db.js
new file mode 100644
index 00000000000..543c33e2ab7
--- /dev/null
+++ b/benchmarks-website/store/db.js
@@ -0,0 +1,33 @@
+function normalizeValue(value) {
+  if (typeof value === "bigint") {
+    const asNumber = Number(value);
+    return Number.isSafeInteger(asNumber) ? asNumber : value.toString();
+  }
+
+  if (Array.isArray(value)) {
+    return value.map(normalizeValue);
+  }
+
+  if (value && typeof value === "object") {
+    return Object.fromEntries(
+      Object.entries(value).map(([key, child]) => [key, normalizeValue(child)]),
+    );
+  }
+
+  return value;
+}
+
+export async function queryRows(connection, sql, values) {
+  const result = await connection.run(sql, values);
+  const rows = await result.getRowObjectsJS();
+  return rows.map((row) => normalizeValue(row));
+}
+
+export async function withConnection(instance, fn) {
+  const connection = await instance.connect();
+  try {
+    return await fn(connection);
+  } finally {
+    connection.closeSync();
+  }
+}
diff --git a/benchmarks-website/store/downsample.js b/benchmarks-website/store/downsample.js
new file mode 100644
index 00000000000..47a774184cc
--- /dev/null
+++ b/benchmarks-website/store/downsample.js
@@ -0,0 +1,68 @@
+import { LTTB } from "downsample";
+import { MAX_POINTS } from "./constants.js";
+
+function lttbIndices(seriesMap, target) {
+  const keys = [...seriesMap.keys()];
+  if (!keys.length) return [];
+
+  const length = seriesMap.get(keys[0])?.length || 0;
+  if (length <= target) return [...Array(length).keys()];
+
+  const averages = Array(length);
+  for (let i = 0; i < length; i++) {
+    let sum = 0;
+    let count = 0;
+
+    for (const series of seriesMap.values()) {
+      const value = series[i]?.value ?? series[i];
+      if (value != null && !Number.isNaN(value)) {
+        sum += value;
+        count++;
+      }
+    }
+
+    averages[i] = [i, count ? sum / count : 0];
+  }
+
+  const indices = LTTB(averages, target).map((point) => Math.round(point[0]));
+  if (!indices.includes(0)) indices.unshift(0);
+  if (!indices.includes(length - 1)) indices.push(length - 1);
+  return indices.sort((a, b) => a - b);
+}
+
+export function downsample(data, factor) {
+  const target = Math.ceil(data.commits.length / factor);
+  if (target >= data.commits.length) return data;
+
+  const indices = [...new Set(
+    lttbIndices(data.series, target).filter(
+      (index) =>
+        Number.isInteger(index) &&
+        index >= 0 &&
+        index < data.commits.length,
+    ),
+  )].sort((a, b) => a - b);
+
+  if (!indices.length) return data;
+
+  const series = new Map();
+  for (const [seriesName, values] of data.series) {
+    series.set(
+      seriesName,
+      indices.map((index) => values[index]),
+    );
+  }
+
+  return {
+    ...data,
+    commits: indices.map((index) => data.commits[index]),
+    series,
+  };
+}
+
+export function downsampleLevel(length) {
+  if (length <= MAX_POINTS) return "1x";
+  if (length <= MAX_POINTS * 2) return "2x";
+  if (length <= MAX_POINTS * 4) return "4x";
+  return "8x";
+}
diff --git a/benchmarks-website/store/metadata.js b/benchmarks-website/store/metadata.js
new file mode 100644
index 00000000000..296e7df2c6a
--- /dev/null
+++ b/benchmarks-website/store/metadata.js
@@ -0,0 +1,401 @@
+import { GROUPS, QUERY_GROUP_EXCLUSIONS } from "./constants.js";
+import { queryRows, withConnection } from "./db.js";
+import { firstLine } from "./utils.js";
+
+const COMMITS_QUERY = `
+  select
+    commit_idx,
+    id,
+    message,
+    timestamp,
+    author,
+    url
+  from active_commits
+  order by commit_idx
+`;
+
+const CHART_ROWS_QUERY = `
+  select
+    cd.group_name,
+    cd.chart_name,
+    cd.unit,
+    cd.sort_position,
+    list(cslv.series_name order by cslv.series_name) as series_names,
+    json_group_object(cslv.series_name, cslv.latest_value) as latest_values
+  from chart_defs cd
+  join chart_series_latest_values cslv
+    on cslv.group_name = cd.group_name
+   and cslv.chart_name = cd.chart_name
+  group by 1, 2, 3, 4
+  order by 1, 4, 2
+`;
+
+const RANDOM_ACCESS_SUMMARY_QUERY = `
+  with selected_chart as (
+    select chart_name
+    from chart_defs
+    where group_name = 'Random Access'
+    order by sort_position, chart_name
+    limit 1
+  )
+  select
+    series_name as name,
+    value as time,
+    value / min(value) over () as ratio
+  from chart_latest_values
+  where group_name = 'Random Access'
+    and chart_name = (select chart_name from selected_chart)
+  order by value, series_name
+`;
+
+const COMPRESSION_SUMMARY_QUERY = `
+  with anchor_chart as (
+    select chart_name
+    from chart_defs
+    where group_name = 'Compression'
+      and chart_name in (
+        'VORTEX:PARQUET ZSTD RATIO COMPRESS TIME',
+        'VORTEX:PARQUET ZSTD RATIO DECOMPRESS TIME'
+      )
+    order by case
+      when chart_name = 'VORTEX:PARQUET ZSTD RATIO COMPRESS TIME' then 0
+      else 1
+    end
+    limit 1
+  ),
+  anchor_idx as (
+    select latest_commit_idx as commit_idx
+    from chart_latest_idx
+    where group_name = 'Compression'
+      and chart_name = (select chart_name from anchor_chart)
+  )
+  select
+    (
+      select geomean(1.0 / value)
+      from benchmark_points_active
+      where group_name = 'Compression'
+        and chart_name = 'VORTEX:PARQUET ZSTD RATIO COMPRESS TIME'
+        and commit_idx = (select commit_idx from anchor_idx)
+        and lower(series_name) not like '%wide table%'
+        and value > 0
+    ) as compress_ratio,
+    (
+      select geomean(1.0 / value)
+      from benchmark_points_active
+      where group_name = 'Compression'
+        and chart_name = 'VORTEX:PARQUET ZSTD RATIO DECOMPRESS TIME'
+        and commit_idx = (select commit_idx from anchor_idx)
+        and lower(series_name) not like '%wide table%'
+        and value > 0
+    ) as decompress_ratio,
+    (
+      select count(*)
+      from benchmark_points_active
+      where group_name = 'Compression'
+        and chart_name = 'VORTEX:PARQUET ZSTD RATIO COMPRESS TIME'
+        and commit_idx = (select commit_idx from anchor_idx)
+        and lower(series_name) not like '%wide table%'
+        and value > 0
+    ) as dataset_count
+`;
+
+const COMPRESSION_SIZE_SUMMARY_QUERY = `
+  with anchor_idx as (
+    select latest_commit_idx as commit_idx
+    from chart_latest_idx
+    where group_name = 'Compression Size'
+      and chart_name = 'VORTEX:PARQUET ZSTD SIZE'
+  )
+  select
+    min(value) as min_ratio,
+    geomean(value) as mean_ratio,
+    max(value) as max_ratio,
+    count(*) as dataset_count
+  from benchmark_points_active
+  where group_name = 'Compression Size'
+    and chart_name = 'VORTEX:PARQUET ZSTD SIZE'
+    and commit_idx = (select commit_idx from anchor_idx)
+    and lower(series_name) not like '%wide table%'
+    and value > 0
+`;
+
+const QUERY_SUMMARY_QUERY = `
+  with per_series as (
+    select *
+    from chart_series_latest_values
+    where group_name not in (
+      'Random Access',
+      'Compression',
+      'Compression Size'
+    )
+  ),
+  chart_bases as (
+    select
+      group_name,
+      chart_name,
+      min(latest_value) as base_value
+    from per_series
+    group by 1, 2
+  ),
+  series_totals as (
+    select
+      group_name,
+      series_name,
+      sum(latest_value) as total_runtime,
+      max(latest_value) as max_runtime
+    from per_series
+    group by 1, 2
+  ),
+  group_series as (
+    select distinct group_name, series_name
+    from per_series
+  ),
+  ratios as (
+    select
+      gs.group_name,
+      gs.series_name,
+      ((10.0 + coalesce(ps.latest_value, greatest(300000.0, st.max_runtime) * 2.0))
+        / (10.0 + cb.base_value)) as ratio
+    from group_series gs
+    join series_totals st
+      on st.group_name = gs.group_name
+     and st.series_name = gs.series_name
+    join chart_defs cd
+      on cd.group_name = gs.group_name
+    join chart_bases cb
+      on cb.group_name = cd.group_name
+     and cb.chart_name = cd.chart_name
+    left join per_series ps
+      on ps.group_name = cd.group_name
+     and ps.chart_name = cd.chart_name
+     and ps.series_name = gs.series_name
+  )
+  select
+    gs.group_name,
+    gs.series_name as name,
+    geomean(r.ratio) as score,
+    st.total_runtime
+  from group_series gs
+  join ratios r
+    on r.group_name = gs.group_name
+   and r.series_name = gs.series_name
+  join series_totals st
+    on st.group_name = gs.group_name
+   and st.series_name = gs.series_name
+  group by 1, 2, 4
+  order by 1, 3, 4, 2
+`;
+
+const DIAGNOSTICS_COUNTS_QUERY = `
+  select
+    (select count(*) from raw_benchmarks) as benchmark_count,
+    (
+      select count(*)
+      from classified_benchmarks cb
+      left join commit_dim cd
+        on cd.id = cb.resolved_commit_id
+      where cb.resolved_commit_id is null
+         or cd.id is null
+    ) as missing_commits
+`;
+
+const UNCATEGORIZED_QUERY = `
+  select distinct split_part(name, '/', 1) as prefix
+  from classified_benchmarks
+  where group_name is null
+  order by prefix
+  limit 20
+`;
+
+const GROUP_COUNTS_QUERY = `
+  select
+    group_name,
+    count(*) as chart_count
+  from chart_defs
+  group by 1
+  order by 1
+`;
+
+function buildEmptyGroups() {
+  return Object.fromEntries(
+    GROUPS.map((groupName) => [
+      groupName,
+      {
+        charts: [],
+        totalCharts: 0,
+        hasData: false,
+        summary: null,
+      },
+    ]),
+  );
+}
+
+function buildChartIndex(groupMap) {
+  const chartIndex = new Map();
+  for (const [groupName, groupData] of Object.entries(groupMap)) {
+    for (const chart of groupData.charts) {
+      chartIndex.set(`${groupName}\u0000${chart.name}`, chart);
+    }
+  }
+  return chartIndex;
+}
+
+function appendChartRows(groups, chartRows, totalCommits) {
+  for (const row of chartRows) {
+    const group = groups[row.group_name];
+    if (!group) continue;
+
+    group.charts.push({
+      name: row.chart_name,
+      unit: row.unit,
+      series: row.series_names || [],
+      sortPosition: row.sort_position,
+      totalPoints: totalCommits,
+      latestValues: row.latest_values || {},
+    });
+  }
+}
+
+function applyRandomAccessSummary(groups, rows) {
+  if (!rows.length) return;
+
+  groups["Random Access"].summary = {
+    type: "randomAccess",
+    title: "Random Access Performance",
+    rankings: rows.map((row) => ({
+      name: row.name,
+      time: row.time,
+      ratio: row.ratio,
+    })),
+    explanation: "Random access time | Ratio to fastest (lower is better)",
+  };
+}
+
+function applyCompressionSummary(groups, row) {
+  if (!row?.compress_ratio && !row?.decompress_ratio) return;
+
+  groups.Compression.summary = {
+    type: "compression",
+    title: "Compression Throughput vs Parquet",
+    compressRatio: row.compress_ratio,
+    decompressRatio: row.decompress_ratio,
+    datasetCount: row.dataset_count,
+    explanation: "Inverse geomean of Vortex/Parquet ratios (higher is better)",
+  };
+}
+
+function applyCompressionSizeSummary(groups, row) {
+  if (!row?.mean_ratio) return;
+
+  groups["Compression Size"].summary = {
+    type: "compressionSize",
+    title: "Compression Size Summary",
+    minRatio: row.min_ratio,
+    meanRatio: row.mean_ratio,
+    maxRatio: row.max_ratio,
+    datasetCount: row.dataset_count,
+    explanation: "Geomean of Vortex/Parquet size ratios (lower is better)",
+  };
+}
+
+function applyQuerySummary(groups, rows) {
+  for (const row of rows) {
+    if (QUERY_GROUP_EXCLUSIONS.has(row.group_name)) continue;
+
+    const group = groups[row.group_name];
+    if (!group) continue;
+
+    if (!group.summary) {
+      group.summary = {
+        type: "queryBenchmark",
+        title: "Performance Summary",
+        rankings: [],
+        explanation: "Geomean of query time ratio to fastest (lower is better)",
+      };
+    }
+
+    group.summary.rankings.push({
+      name: row.name,
+      score: row.score,
+      totalRuntime: row.total_runtime,
+    });
+  }
+}
+
+function finalizeGroups(groups) {
+  for (const group of Object.values(groups)) {
+    group.totalCharts = group.charts.length;
+    group.hasData = group.charts.length > 0;
+  }
+}
+
+async function fetchCoreMetadata(connection) {
+  const commits = await queryRows(connection, COMMITS_QUERY);
+  const chartRows = await queryRows(connection, CHART_ROWS_QUERY);
+
+  return { commits, chartRows };
+}
+
+async function fetchSummaryMetadata(connection) {
+  const randomAccessRows = await queryRows(connection, RANDOM_ACCESS_SUMMARY_QUERY);
+  const compressionRows = await queryRows(connection, COMPRESSION_SUMMARY_QUERY);
+  const compressionSizeRows = await queryRows(
+    connection,
+    COMPRESSION_SIZE_SUMMARY_QUERY,
+  );
+
+  return {
+    randomAccessRows,
+    compressionRows,
+    compressionSizeRows,
+  };
+}
+
+export async function buildMetadata(instance, lastUpdated) {
+  const [
+    { commits, chartRows },
+    { randomAccessRows, compressionRows, compressionSizeRows },
+    querySummaryRows,
+  ] = await Promise.all([
+    withConnection(instance, fetchCoreMetadata),
+    withConnection(instance, fetchSummaryMetadata),
+    withConnection(instance, (connection) => queryRows(connection, QUERY_SUMMARY_QUERY)),
+  ]);
+
+  const groups = buildEmptyGroups();
+  appendChartRows(groups, chartRows, commits.length);
+  applyRandomAccessSummary(groups, randomAccessRows);
+  applyCompressionSummary(groups, compressionRows[0]);
+  applyCompressionSizeSummary(groups, compressionSizeRows[0]);
+  applyQuerySummary(groups, querySummaryRows);
+  finalizeGroups(groups);
+
+  return {
+    commits,
+    metadata: {
+      groups,
+      totalCommits: commits.length,
+      commits: commits.map(({ id, message, timestamp, author }) => ({
+        id,
+        message: firstLine(message),
+        timestamp,
+        author,
+      })),
+      lastUpdated,
+    },
+    chartIndex: buildChartIndex(groups),
+  };
+}
+
+export async function collectDiagnostics(connection) {
+  const [counts] = await queryRows(connection, DIAGNOSTICS_COUNTS_QUERY);
+  const uncategorized = await queryRows(connection, UNCATEGORIZED_QUERY);
+  const groupCounts = await queryRows(connection, GROUP_COUNTS_QUERY);
+
+  return {
+    benchmarkCount: counts?.benchmark_count ?? 0,
+    missingCommits: counts?.missing_commits ?? 0,
+    uncategorized: uncategorized.map((row) => row.prefix),
+    groupCounts,
+  };
+}
diff --git a/benchmarks-website/store/sql.js b/benchmarks-website/store/sql.js
new file mode 100644
index 00000000000..907a5831267
--- /dev/null
+++ b/benchmarks-website/store/sql.js
@@ -0,0 +1,479 @@
+import { ENGINE_RENAMES, QUERY_SUITES } from "../src/config.js";
+import { GROUPS } from "./constants.js";
+
+function sqlStringLiteral(value) {
+  return `'${String(value).replace(/'/g, "''")}'`;
+}
+
+function sqlValue(value) {
+  if (value === null || value === undefined) return "NULL";
+  if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
+  if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
+  return sqlStringLiteral(value);
+}
+
+function sqlValues(rows) {
+  return rows
+    .map((row) => `(${row.map((value) => sqlValue(value)).join(", ")})`)
+    .join(",\n");
+}
+
+function joinStatements(statements) {
+  return statements
+    .map((statement) => statement.trim().replace(/;?$/, ";"))
+    .join("\n\n");
+}
+
+function randomAccessCondition(expression = "name_lower") {
+  return `(starts_with(${expression}, 'random-access/') or starts_with(${expression}, 'random access/'))`;
+}
+
+function renameSeriesInSql(sourceExpression) {
+  return `coalesce(
+    (select dst from engine_renames er where er.src = lower(${sourceExpression}) limit 1),
+    ${sourceExpression}
+  )`;
+}
+
+function buildQuerySuitesStatement() {
+  const rows = QUERY_SUITES.map((suite) => [
+    suite.prefix,
+    suite.displayName || null,
+    suite.queryPrefix || null,
+    suite.datasetKey || null,
+    Boolean(suite.fanOut),
+    Boolean(suite.skip),
+  ]);
+
+  return `
+create or replace table query_suites as
+select * from (
+  values
+  ${sqlValues(rows)}
+) as suites(prefix, display_name, query_prefix, dataset_key, fan_out, skip)
+`;
+}
+
+function buildValidGroupsStatement() {
+  const rows = GROUPS.map((groupName) => [groupName]);
+
+  return `
+create or replace table valid_groups as
+select * from (
+  values
+  ${sqlValues(rows)}
+) as groups(group_name)
+`;
+}
+
+function buildEngineRenamesStatement() {
+  const rows = Object.entries(ENGINE_RENAMES);
+
+  return `
+create or replace table engine_renames as
+select * from (
+  values
+  ${sqlValues(rows)}
+) as renames(src, dst)
+`;
+}
+
+function buildRawCommitsViewStatement(commitsPath) {
+  return `
+create or replace view raw_commits as
+select *
+from read_json(
+  ${sqlStringLiteral(commitsPath)},
+  format = 'newline_delimited',
+  compression = 'auto_detect',
+  columns = {
+    id: 'VARCHAR',
+    message: 'VARCHAR',
+    timestamp: 'VARCHAR',
+    author: 'JSON',
+    url: 'VARCHAR'
+  }
+)
+`;
+}
+
+function buildCommitDimStatement() {
+  return `
+create or replace table commit_dim as
+select
+  row_number() over (order by commit_ts nulls last, id) - 1 as commit_idx,
+  id,
+  message,
+  timestamp as timestamp_text,
+  commit_ts,
+  author,
+  url
+from (
+  select
+    id,
+    message,
+    timestamp,
+    try_cast(timestamp as timestamptz) as commit_ts,
+    coalesce(json_extract_string(author, '$.name'), 'Unknown') as author,
+    url
+  from raw_commits
+)
+`;
+}
+
+function buildRawBenchmarksViewStatement(dataPath) {
+  return `
+create or replace view raw_benchmarks as
+select
+  row_number() over () as benchmark_row,
+  *
+from read_json(
+  ${sqlStringLiteral(dataPath)},
+  format = 'newline_delimited',
+  compression = 'auto_detect',
+  columns = {
+    name: 'VARCHAR',
+    unit: 'VARCHAR',
+    value: 'DOUBLE',
+    storage: 'VARCHAR',
+    dataset: 'JSON',
+    commit: 'JSON',
+    commit_id: 'VARCHAR'
+  }
+)
+`;
+}
+
+function buildBenchmarksBaseStatement() {
+  return `
+create or replace table benchmarks_base as
+select
+  benchmark_row,
+  name,
+  lower(name) as name_lower,
+  split_part(name, '/', 1) as part1,
+  lower(split_part(name, '/', 1)) as part1_lower,
+  coalesce(nullif(split_part(name, '/', 2), ''), 'default') as part2,
+  coalesce(nullif(split_part(name, '/', 3), ''), 'default') as part3,
+  coalesce(nullif(split_part(name, '/', 4), ''), 'default') as part4,
+  array_length(string_split(name, '/')) as part_count,
+  unit,
+  value as raw_value,
+  storage,
+  dataset as dataset_json,
+  commit as commit_json,
+  commit_id
+from raw_benchmarks
+`;
+}
+
+function buildMatchedSuitesStatement() {
+  return `
+create or replace table matched_suites as
+select
+  b.benchmark_row,
+  s.prefix,
+  s.display_name,
+  s.query_prefix,
+  s.dataset_key,
+  s.fan_out
+from benchmarks_base b
+join query_suites s
+  on (b.name_lower like s.prefix || '_q%' or b.name_lower like s.prefix || '/%')
+where not s.skip
+qualify row_number() over (partition by b.benchmark_row order by length(s.prefix) desc) = 1
+`;
+}
+
+function buildGroupNameExpressionSql() {
+  return `
+case
+  when ${randomAccessCondition()} then 'Random Access'
+  when starts_with(name_lower, 'vortex size/')
+    or starts_with(name_lower, 'vortex-file-compressed size/')
+    or starts_with(name_lower, 'parquet size/')
+    or starts_with(name_lower, 'lance size/')
+    or contains(name_lower, ':raw size/')
+    or contains(name_lower, ':parquet-zstd size/')
+    or contains(name_lower, ':lance size/')
+    then 'Compression Size'
+  when starts_with(name_lower, 'compress time/')
+    or starts_with(name_lower, 'decompress time/')
+    or starts_with(name_lower, 'parquet_rs-zstd compress')
+    or starts_with(name_lower, 'parquet_rs-zstd decompress')
+    or starts_with(name_lower, 'lance compress')
+    or starts_with(name_lower, 'lance decompress')
+    or starts_with(name_lower, 'vortex:lance ratio')
+    or starts_with(name_lower, 'vortex:parquet-zstd ratio')
+    or starts_with(name_lower, 'vortex:raw ratio')
+    then 'Compression'
+  when suite_prefix is not null and not suite_fan_out then suite_display_name
+  when suite_prefix is not null and suite_fan_out then
+    suite_display_name
+    || ' ('
+    || case when upper(coalesce(storage, '')) = 'S3' then 'S3' else 'NVMe' end
+    || ') (SF='
+    || cast(cast(round(coalesce(try_cast(suite_scale_factor as double), 1.0)) as bigint) as varchar)
+    || ')'
+  else null
+end
+`;
+}
+
+function buildRawChartNameExpressionSql() {
+  return `
+case
+  when ${randomAccessCondition()} and part_count = 4
+    then upper(replace(replace(part2 || '/' || part3, '_', ' '), '-', ' '))
+  when ${randomAccessCondition()} and part_count = 2
+    then 'RANDOM ACCESS'
+  when suite_prefix is not null
+    and regexp_extract(part1_lower, '^' || suite_prefix || '[_ ]?q([0-9]+)', 1) <> ''
+    then suite_query_prefix || ' Q' || cast(cast(regexp_extract(part1_lower, '^' || suite_prefix || '[_ ]?q([0-9]+)', 1) as integer) as varchar)
+  else upper(replace(replace(part1, '_', ' '), '-', ' '))
+end
+`;
+}
+
+function buildRawSeriesNameExpressionSql() {
+  return `
+case
+  when ${randomAccessCondition()} and part_count = 4 then part4
+  else part2
+end
+`;
+}
+
+function buildRawUnitExpressionSql() {
+  return `
+case
+  when unit is not null then unit
+  when contains(name_lower, ' size/') then 'bytes'
+  when contains(name_lower, ' ratio ') then 'ratio'
+  else 'ns'
+end
+`;
+}
+
+function buildSortPositionExpressionSql() {
+  return `
+coalesce(
+  try_cast(nullif(regexp_extract(part1_lower, 'q([0-9]+)$', 1), '') as integer),
+  0
+)
+`;
+}
+
+function buildChartNameExpressionSql() {
+  return `
+case
+  when group_name = 'Compression Size' and raw_chart_name = 'VORTEX FILE COMPRESSED SIZE' then 'VORTEX SIZE'
+  else raw_chart_name
+end
+`;
+}
+
+function buildDisplayUnitExpressionSql() {
+  return `
+case
+  when raw_unit = 'ns' then 'ms/iter'
+  when raw_unit = 'bytes' then 'MiB'
+  else raw_unit
+end
+`;
+}
+
+function buildDisplayValueExpressionSql() {
+  return `
+case
+  when raw_unit = 'ns' then raw_value / 1000000.0
+  when raw_unit = 'bytes' then raw_value / 1048576.0
+  else raw_value
+end
+`;
+}
+
+function buildClassifiedBenchmarksStatement() {
+  return `
+create or replace table classified_benchmarks as
+with base as (
+  select
+    b.*,
+    ms.prefix as suite_prefix,
+    ms.display_name as suite_display_name,
+    ms.query_prefix as suite_query_prefix,
+    ms.dataset_key as suite_dataset_key,
+    coalesce(ms.fan_out, false) as suite_fan_out,
+    coalesce(json_extract_string(b.commit_json, '$.id'), b.commit_id) as resolved_commit_id,
+    case
+      when ms.dataset_key = 'tpch' then json_extract_string(b.dataset_json, '$.tpch.scale_factor')
+      when ms.dataset_key = 'tpcds' then json_extract_string(b.dataset_json, '$.tpcds.scale_factor')
+      else null
+    end as suite_scale_factor
+  from benchmarks_base b
+  left join matched_suites ms using (benchmark_row)
+),
+named as (
+  select
+    *,
+    ${buildGroupNameExpressionSql()} as group_name,
+    ${buildRawChartNameExpressionSql()} as raw_chart_name,
+    ${buildRawSeriesNameExpressionSql()} as raw_series_name,
+    ${buildRawUnitExpressionSql()} as raw_unit,
+    ${buildSortPositionExpressionSql()} as sort_position
+  from base
+),
+renamed as (
+  select
+    n.*,
+    ${renameSeriesInSql("n.raw_series_name")} as series_name
+  from named n
+)
+select
+  benchmark_row,
+  name,
+  resolved_commit_id,
+  group_name,
+  ${buildChartNameExpressionSql()} as chart_name,
+  series_name,
+  sort_position,
+  ${buildDisplayUnitExpressionSql()} as unit,
+  ${buildDisplayValueExpressionSql()} as value
+from renamed
+`;
+}
+
+function buildBenchmarkPointsStatement() {
+  return `
+create or replace table benchmark_points as
+select
+  cb.group_name,
+  cb.chart_name,
+  cb.series_name,
+  cb.sort_position,
+  cb.unit,
+  cb.value,
+  cd.commit_idx
+from classified_benchmarks cb
+join valid_groups vg on vg.group_name = cb.group_name
+join commit_dim cd on cd.id = cb.resolved_commit_id
+where cb.group_name is not null
+  and cb.chart_name not like '%PARQUET-UNC%'
+  and cb.name not like '% throughput%'
+  and cb.value is not null
+`;
+}
+
+function buildActiveCommitsStatement() {
+  return `
+create or replace table active_commits as
+with first_active as (
+  select coalesce(min(commit_idx), 0) as min_commit_idx
+  from benchmark_points
+)
+select
+  cd.commit_idx as original_commit_idx,
+  cd.commit_idx - fa.min_commit_idx as commit_idx,
+  cd.id,
+  cd.message,
+  cd.timestamp_text as timestamp,
+  cd.author,
+  cd.url
+from commit_dim cd
+cross join first_active fa
+where cd.commit_idx >= fa.min_commit_idx
+order by cd.commit_idx
+`;
+}
+
+function buildBenchmarkPointsActiveStatement() {
+  return `
+create or replace table benchmark_points_active as
+select
+  bp.group_name,
+  bp.chart_name,
+  bp.series_name,
+  bp.sort_position,
+  bp.unit,
+  bp.value,
+  ac.commit_idx
+from benchmark_points bp
+join active_commits ac
+  on ac.original_commit_idx = bp.commit_idx
+`;
+}
+
+function buildChartDefinitionsStatement() {
+  return `
+create or replace table chart_defs as
+select
+  group_name,
+  chart_name,
+  min(sort_position) as sort_position,
+  min(unit) as unit
+from benchmark_points_active
+group by 1, 2
+`;
+}
+
+function buildChartLatestIndexStatement() {
+  return `
+create or replace table chart_latest_idx as
+select
+  group_name,
+  chart_name,
+  max(commit_idx) as latest_commit_idx
+from benchmark_points_active
+group by 1, 2
+`;
+}
+
+function buildChartLatestValuesStatement() {
+  return `
+create or replace table chart_latest_values as
+select
+  bpa.group_name,
+  bpa.chart_name,
+  bpa.series_name,
+  bpa.value
+from benchmark_points_active bpa
+join chart_latest_idx cli
+  on cli.group_name = bpa.group_name
+ and cli.chart_name = bpa.chart_name
+ and cli.latest_commit_idx = bpa.commit_idx
+`;
+}
+
+function buildChartSeriesLatestValuesStatement() {
+  return `
+create or replace table chart_series_latest_values as
+select
+  group_name,
+  chart_name,
+  series_name,
+  arg_max(value, commit_idx) as latest_value
+from benchmark_points_active
+group by 1, 2, 3
+`;
+}
+
+export function buildBootstrapSql(dataPath, commitsPath) {
+  return joinStatements([
+    buildQuerySuitesStatement(),
+    buildValidGroupsStatement(),
+    buildEngineRenamesStatement(),
+    buildRawCommitsViewStatement(commitsPath),
+    buildCommitDimStatement(),
+    buildRawBenchmarksViewStatement(dataPath),
+    buildBenchmarksBaseStatement(),
+    buildMatchedSuitesStatement(),
+    buildClassifiedBenchmarksStatement(),
+    buildBenchmarkPointsStatement(),
+    buildActiveCommitsStatement(),
+    buildBenchmarkPointsActiveStatement(),
+    buildChartDefinitionsStatement(),
+    buildChartLatestIndexStatement(),
+    buildChartLatestValuesStatement(),
+    buildChartSeriesLatestValuesStatement(),
+  ]);
+}
diff --git a/benchmarks-website/store/utils.js b/benchmarks-website/store/utils.js
new file mode 100644
index 00000000000..462c974fbce
--- /dev/null
+++ b/benchmarks-website/store/utils.js
@@ -0,0 +1,3 @@
+export function firstLine(message) {
+  return String(message || "").split("\n")[0] || "";
+}
