# logviewer Web UI for the ClickHouse log store (`logs.raw`). Single Go binary with an embedded Bootstrap 3 + jQuery UI — no CDN assets, no build step. logviewer does **no authentication itself**. It runs behind oauth2-proxy at `logviewer.unkin.net`; the proxy is the auth boundary. Query safety relies on the readonly ClickHouse user (`logreader`) plus server-side guardrails: - **Time bounds are always enforced.** Missing `since`/`until` default to a 15m window ending now; windows wider than 72h (the table TTL) are rejected. No request can scan the store unbounded. - **Everything is parameterized** (ClickHouse `param_*` bound parameters) except the explicit raw `sql` fragment, which is parenthesised and ANDed into the outer query — the enforced time bounds and `LIMIT` still apply, and the readonly user can't write. - `max_execution_time=30` is set on every query. ## Configuration | Env | Default | | |---|---|---| | `CH_URL` | `http://clickhouse-logs.logging.svc.cluster.local:8123` | ClickHouse HTTP endpoint | | `CH_USER` | `logreader` | readonly ClickHouse user | | `CH_PASSWORD` | (empty) | password for `CH_USER` | | `LISTEN_ADDR` | `:8080` | HTTP listen address | ## API - `GET /api/query` — `since`/`until` (RFC3339, unix, or relative `15m`/`6h`/`1d`), filters `namespace`/`host`/`pod`/`container`/`app`/`severity`/`stream`/`source`, `q` (space-separated case-insensitive substring terms, all must match), `sql` (raw WHERE fragment), `limit` (max 1000) / `offset`. Newest first. - `GET /api/tail` — same filters plus `cursor` (exclusive lower bound, unix ms). Returns ascending rows and the next `cursor`. Stale cursors are clamped to the last 15m. - `GET /api/facets` — top 20 namespaces / apps (`labels['app']`) / hosts by row count in the window. - `GET /healthz` — ClickHouse ping. ## UI Time-range picker (15m/1h/6h/1d/custom), facet dropdowns, debounced fuzzy search, toggleable raw-SQL box, paged log table with expandable rows (labels/fields), and a Tail mode (1s polling, autoscroll, pauses when you scroll up). ## Development ```sh make build # dist/logviewer make test # go test -race ./... make patch # tag + push next vX.Y.Z → CI builds+pushes the image ``` Releases: pushing a `v*` tag builds the container and pushes it to `artifactapi.k8s.syd1.au.unkin.net/docker-internal/logviewer`.