Files
cairnobs/web
jcoffey-dev fb5049a747 Phase 2: unified query language spanning ClickHouse and Tantivy
Replaces the separate SQL-only /query and text-only /search endpoints
with one pipe-syntax query language (plus raw SQL escape hatch) that
compiles to a single IR and execution plan across both backends, so a
query like `message:"connection refused" | stats count by host` runs
as one request instead of two disjoint tools.

- api/internal/querylang: lexer -> ast -> parser -> ir -> planner ->
  executor, each layer independently tested.
- Execution generalizes Phase 1's proven Tantivy-prefilter pattern
  into a 4-way routing table (pure ClickHouse / text-only / text +
  aggregation / raw SQL passthrough).
- Unified web query page and `sentryctl query`, both hitting the same
  POST /query endpoint.
- Benchmarked against a real 1,022,000-row dataset
  (hack/benchmark-fixture); caught and fixed a real bug where the
  Tantivy prefilter cap (10,000) produced an IN-clause exceeding
  ClickHouse's default max_query_size -- lowered to 5,000, documented
  in docs/query-language-design.md and docs/phase-2-runbook.md.
- docs/query-language-reference.md: customer-facing syntax reference.
2026-08-13 12:21:42 -07:00
..

web

SvelteKit frontend. Phase 0: one page, one query box, one table. No auth, no styling polish, no routing beyond /.

What it does

Textarea for a raw SQL string → POST {VITE_API_BASE_URL}/query on /api → renders {columns, rows} as an HTML table, or shows {error} from a rejected/failed query. That's the whole app — see src/routes/+page.svelte.

Why a static build, not a Node server

Scaffolded with @sveltejs/adapter-static: this page has no server-side data loading (all data comes from a client-side fetch triggered by the submit button), so there's nothing here that needs a running SvelteKit server. A prerendered static site is simpler to build, deploy, and reason about than running Node in production for a page that's this thin.

Because it's static, VITE_API_BASE_URL is baked in at build time, not read at container start. Set it before npm run build (or pass --build-arg VITE_API_BASE_URL=... to docker build) — changing it later means rebuilding, not just restarting the container.

Building & running

npm install
cp .env.example .env   # adjust VITE_API_BASE_URL if /api isn't on localhost:8080
npm run dev             # local dev server with hot reload
npm run check            # svelte-check, type errors
npm run build             # static output to build/
npm run preview            # serve the static build locally to sanity-check it
docker build -f Dockerfile -t sentry-web .   # context is web/, not the repo root
docker run -p 3000:3000 sentry-web

Why nginx, not distroless

The repo convention prefers distroless/scratch base images. Serving a static SPA still needs some HTTP server, though, and nginx:alpine is the boring, standard choice for that job — writing a custom static-file binary just to stay distroless would be more engineering than a Phase 0 placeholder page justifies. nginx.conf here is minimal: serve build/, fall back to index.html for client-side routing (only one route exists today, but this is what you want the moment a second one is added).