{"id":49458271,"url":"https://github.com/tsouza/cerberus","last_synced_at":"2026-07-02T09:01:16.752Z","repository":{"id":357196195,"uuid":"1230293697","full_name":"tsouza/cerberus","owner":"tsouza","description":"Drop-in Prometheus / Loki / Tempo HTTP gateway for ClickHouse. Translate PromQL, LogQL, and TraceQL into optimized CH SQL — keep Grafana, swap the backend.","archived":false,"fork":false,"pushed_at":"2026-06-29T20:25:00.000Z","size":13428,"stargazers_count":21,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T22:25:22.864Z","etag":null,"topics":["clickhouse","golang","grafana","logql","loki","metrics-logs-traces","observability","open-source","opentelemetry","otel","prometheus","promql","query-engine","tempo","traceql"],"latest_commit_sha":null,"homepage":"https://cerberus.foo","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsouza.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-05T21:29:01.000Z","updated_at":"2026-06-29T22:07:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tsouza/cerberus","commit_stats":null,"previous_names":["tsouza/cerberus"],"tags_count":74,"template":false,"template_full_name":null,"purl":"pkg:github/tsouza/cerberus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsouza%2Fcerberus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsouza%2Fcerberus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsouza%2Fcerberus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsouza%2Fcerberus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsouza","download_url":"https://codeload.github.com/tsouza/cerberus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsouza%2Fcerberus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35040024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clickhouse","golang","grafana","logql","loki","metrics-logs-traces","observability","open-source","opentelemetry","otel","prometheus","promql","query-engine","tempo","traceql"],"created_at":"2026-04-30T08:03:59.488Z","updated_at":"2026-07-02T09:01:16.484Z","avatar_url":"https://github.com/tsouza.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://cerberus.foo/assets/brand/readme-banner-light-1280x640.png\"\u003e\n  \u003cimg src=\"https://cerberus.foo/assets/brand/readme-banner-1280x640.png\" alt=\"cerberus — three query languages, one backend\" width=\"100%\"\u003e\n\u003c/picture\u003e\n\n\u003cbr\u003e\n\n### Drop-in Prometheus / Loki / Tempo HTTP gateway for ClickHouse\n\n_Keep Grafana, alerting, and your CLI tooling. Swap the backend._\n\n\u003csub\u003e\n\u003ca href=\"#why-cerberus\"\u003eWhy cerberus\u003c/a\u003e \u0026nbsp;·\u0026nbsp;\n\u003ca href=\"#quick-start\"\u003eQuick start\u003c/a\u003e \u0026nbsp;·\u0026nbsp;\n\u003ca href=\"#architecture\"\u003eArchitecture\u003c/a\u003e \u0026nbsp;·\u0026nbsp;\n\u003ca href=\"#compatibility\"\u003eCompatibility\u003c/a\u003e \u0026nbsp;·\u0026nbsp;\n\u003ca href=\"#documentation\"\u003eDocs\u003c/a\u003e\n\u003c/sub\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\n[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n[![Go Reference](https://pkg.go.dev/badge/github.com/tsouza/cerberus.svg)](https://pkg.go.dev/github.com/tsouza/cerberus)\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/tsouza/cerberus)\n[![Go Report Card](https://goreportcard.com/badge/github.com/tsouza/cerberus)](https://goreportcard.com/report/github.com/tsouza/cerberus)\n\n[![PromQL compat](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftsouza%2Fcerberus%2Fcompat-scores%2Fbadges%2Fprometheus.json)](https://github.com/tsouza/cerberus/actions/workflows/compatibility.yml)\n[![LogQL compat](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftsouza%2Fcerberus%2Fcompat-scores%2Fbadges%2Floki.json)](https://github.com/tsouza/cerberus/actions/workflows/compatibility.yml)\n[![TraceQL compat](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftsouza%2Fcerberus%2Fcompat-scores%2Fbadges%2Ftempo.json)](https://github.com/tsouza/cerberus/actions/workflows/compatibility.yml)\n\n\u003c/div\u003e\n\n\u003e [!NOTE]\n\u003e **1.0.0 — stable wire API, young project.** The Prometheus / Loki /\n\u003e Tempo HTTP surfaces cerberus serves are its 1.0 compatibility contract\n\u003e and follow semantic versioning from here. Behaviour is held to reference\n\u003e Prometheus / Loki / Tempo by differential harnesses on every merge\n\u003e (PromQL at **574/574** on the CNCF compliance tester). It's a confident\n\u003e 1.0 on behaviour — but a young, actively-developed project, so evaluate\n\u003e it against your own corpus before production, and expect TraceQL to carry\n\u003e the lightest conformance confidence of the three heads\n\u003e ([details](#compatibility)). See [`CHANGELOG.md`](CHANGELOG.md) for what\n\u003e has landed.\n\nThe three `*QL compat` badges are **differential parity scores** —\n`passed / total` cases where cerberus matched a reference Prometheus /\nLoki / Tempo on the same seeded corpus ([details](#compatibility)). The\nPromQL leg runs the third-party\n[PromLabs / CNCF **PromQL Compliance Tester**](https://github.com/prometheus/compliance)\n(`prometheus/compliance`) — the same tool the CNCF Prometheus Conformance\nProgram uses — at **574/574 cases passing, no allow-list**, against a real\n`prom/prometheus`. The scores are tracked, not gated: see\n[Compatibility](#compatibility) for exactly what the CI checks enforce.\n\n---\n\n## Why cerberus?\n\nMetrics, logs, and traces rarely share a store — the usual answer is\nPrometheus + Loki + Tempo, three retention policies and storage bills for\nwhat is largely the same OTLP data sliced three ways. ClickHouse is a\ngreat single store for all three signals; cerberus supplies the missing\n**query side**. Point Grafana at it as three datasources and your\nexisting PromQL / LogQL / TraceQL keeps working, translated to ClickHouse\nSQL underneath.\n\n- **No Grafana plugin.** Cerberus speaks each upstream HTTP API verbatim\n  (`/api/v1/query_range`, `/loki/api/v1/query_range`, `/api/search`, …).\n  Grafana sees three normal datasources.\n- **No custom QL.** PromQL, LogQL, TraceQL — exactly as your dashboards\n  and alerts already use them.\n- **Grammar-faithful parsers.** PromQL is parsed by the upstream Apache\n  `prometheus/promql/parser` directly. LogQL and TraceQL are parsed by\n  cerberus's own clean-room Apache reimplementations of the published\n  grammars (`internal/logql/lsyntax`, `internal/traceql/ast`), kept honest\n  by differential tests against the upstream parsers. If upstream parses\n  it, cerberus parses it — without linking Grafana's AGPL code.\n\n## Version requirements\n\nTwo axes decide whether a deployment is compatible: the **ClickHouse server\nversion** cerberus queries, and the **OTel schema shape** the data was written\nin.\n\n| Component            | Minimum                        | Notes                                                                                                                               |\n| -------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |\n| ClickHouse           | **24.8**                       | The supported floor — the SQL cerberus emits is correct down to it. Enabling the experimental native rate requires 25.9 (below).    |\n| OTel exporter schema | **clickhouseexporter 0.152.0** | A **schema shape**, not a binary version — see below.                                                                               |\n\n**ClickHouse.** 24.8 is the lowest version cerberus's emitted SQL is correct on:\nthe 24.8 empty-input / parse-unit / filter-path quirks are all worked around\nunconditionally, so a query that runs on 24.8 runs on every newer server too.\nThe differential compatibility harnesses — the source of truth for all three\nheads — execute on ClickHouse 25.8, so the validated SQL is exercised forward of\nthe floor as well. The ClickHouse-optimization auto-picker\n(`CERBERUS_CH_OPTIMIZATIONS=auto`, the default) probes the connected server's\nversion once at startup and enables the result-equivalent optimizations it\nsupports — `aggregation_in_order` (24.8+) and `condition_cache` (25.3+), plus the\nnative `timeSeries*ToGrid` aggregates on capable servers (the whole family at\n25.9+ — the rate/resample aggregates first shipped at 25.6 but used a closed\nmembership window that diverged from PromQL until the 25.9 left-open fix, so\n25.9 is the auto floor for all four). Those native aggregates keep an\n\"experimental\" maturity label but are auto-selected by version, because they are\nvalidated result-correct at flat memory. So on modern ClickHouse the auto-picker\n**raises** the effective floor to **25.9** for eligible `rate(\u003ccounter\u003e[range])`\nrange queries, lowering them to the compiled `timeSeriesRateToGrid` aggregate. On a 24.8 server none of the\nnative aggregates engage and the 24.8-safe SQL is emitted unchanged. The lone\nopt-in-only feature is `columnar_result_decode` (a perf tradeoff `auto` never\nselects). See\n[`docs/clickhouse-optimizations.md`](docs/clickhouse-optimizations.md) for the\nauto-picker and\n[`docs/operations.md`](docs/operations.md#native-rate-timeseriesratetogrid--auto-enabled-on-259)\nfor the runtime contract and the experimental-setting details.\n\n**OTel schema — the shape, not the exporter.** Cerberus reads the\n**OpenTelemetry ClickHouse schema shape** pinned to `clickhouseexporter`\n**v0.152.0** (via the `tsouza/…:cerberus-ddl` fork in\n[`go.mod`](go.mod)). What matters is the **table layout** — column names,\ntypes, and `Map` shapes — not which binary produced it. Any exporter, collector\npipeline, or ingestion path that writes tables in that shape works; the exporter\nbinary version itself is irrelevant. If your layout deviates from the exporter\ndefaults, point cerberus at it with the `CERBERUS_SCHEMA_*` overrides — see\n[`docs/configuration.md`](docs/configuration.md#schema-overrides-and-prometheus-resource-labels).\n\n## Quick start\n\n```sh\ngit clone https://github.com/tsouza/cerberus.git \u0026\u0026 cd cerberus\ndocker compose up --wait\nopen http://localhost:3000   # Grafana (auto-login as admin); cerberus on :8080\n```\n\nThat builds cerberus, boots single-node ClickHouse, loads a deterministic\nOTel fixture (logs / traces / metrics), and brings up Grafana\npre-provisioned with cerberus as three datasources. A fresh dashboard\npopulates in ~30s; `docker compose down -v` wipes the volume.\n\n### From a published release\n\nCerberus is one stateless binary configured via environment variables.\nPin an explicit tag — `:latest` only moves with stable releases:\n\n```sh\ndocker pull ghcr.io/tsouza/cerberus:\u003ctag\u003e\ndocker run --rm -p 8080:8080 -e CERBERUS_CH_ADDR=clickhouse:9000 \\\n  ghcr.io/tsouza/cerberus:\u003ctag\u003e\n```\n\nPrebuilt binaries (linux / darwin × amd64 / arm64) are on the\n[release page](https://github.com/tsouza/cerberus/releases); each release\nships a [SLSA build provenance](https://slsa.dev) attestation:\n\n```sh\ngh attestation verify cerberus_*_linux_amd64.tar.gz --owner tsouza --repo cerberus\n```\n\nCerberus is configured **entirely** through `CERBERUS_*` environment\nvariables — see the full [configuration reference](docs/configuration.md).\nThe surrounding runtime contract (lifecycle, scaling, the solver and\nexperimental knobs in context) lives in\n[`docs/operations.md`](docs/operations.md).\n\n### Helm (Kubernetes)\n\nA production Helm chart lives in\n[`deploy/helm/cerberus`](deploy/helm/cerberus) and is published as an OCI\nartifact (cosign-signed, with SLSA provenance):\n\n```sh\nhelm install cerberus oci://ghcr.io/tsouza/cerberus/charts/cerberus --version \u003cx.y.z\u003e \\\n  --set clickhouse.addr='{clickhouse:9000}' \\\n  --set clickhouse.existingSecret=ch-creds\n```\n\nThe chart is stateless and secure-by-default, with typed\nClickHouse / OTLP / schema / admit blocks plus full escape hatches\n(`extraEnv`, sidecars, affinity, ingress, HPA, PDB, NetworkPolicy). See\nthe [chart README](deploy/helm/cerberus/README.md) for the complete values\nreference and a production HA example.\n\n## Architecture\n\nCerberus has **one** query pipeline, not three. Each head parses its\nquery language — PromQL with the upstream Apache prometheus parser, LogQL\nand TraceQL with cerberus's own in-house Apache reimplementations — and\nlowers to a shared plan IR\n([`internal/chplan`](internal/chplan)); a rule-based optimiser rewrites\nit; the closed typed-Frag [`internal/chsql`](internal/chsql) emitter\nproduces parameterised, escape-free ClickHouse SQL; and the engine\nstreams results. The three HTTP heads plug in as thin `Lang` adapters\nover [`internal/engine`](internal/engine), so the optimiser and emitter\nnever know which head produced a plan — **new optimisations cost one\nimplementation, not three**.\n\nSee [`docs/engine.md`](docs/engine.md) for the `Lang` contract, the\nrequest lifecycle, and the per-stage breakdown (IR algebra, optimiser\nrules, the typed-SQL emitter, the OTel schema). For how cerberus keeps\nqueries fast — the compute-fan-out strategy and per-layer optimisations —\nsee [`docs/performance.md`](docs/performance.md).\n\n\u003e **Rate-over-range is exact by default.** `rate(…)` range queries match\n\u003e reference Prometheus bit-for-bit and stay sub-second at realistic scale.\n\u003e For million-row queries an experimental native ClickHouse path\n\u003e (`timeSeriesRateToGrid`) trades a sub-observable last-bit rounding\n\u003e difference for flat memory and an order-of-magnitude speed-up — see the\n\u003e [exactness-vs-scale tradeoff guide](docs/performance.md#native-rate-exactness-vs-scale-should-i-enable-it).\n\n## Compatibility\n\nEach query language has a **differential harness**: cerberus and a\nreference engine answer the same corpus against the same seeded data, and\nthe responses are diffed case-for-case — pinning _observed semantics on\nreal ClickHouse_ against an upstream oracle, not just emitted SQL.\n\nThe strongest leg is **PromQL**, which runs the third-party **PromQL\nCompliance Tester** (`prometheus/compliance`, the PromLabs / CNCF\nPrometheus Conformance Program tooling) against a real `prom/prometheus`,\nseeded identically on both sides via remote-write. **574/574 cases pass,\nno allow-list.** LogQL diffs against a real Loki on Grafana's own\n`pkg/logql/bench` corpus — solid, but a Grafana bench corpus rather than a\nstandardised conformance suite. TraceQL is the lighter leg: there is **no\nthird-party TraceQL conformance suite**, so its corpus is cerberus-owned\n(author-written TXTAR), and its numerical confidence is correspondingly\nlower than PromQL's.\n\n| Head    | Reference + corpus                                                  | Required check             | Conformance leg                           |\n| ------- | ------------------------------------------------------------------- | -------------------------- | ----------------------------------------- |\n| PromQL  | real `prom/prometheus` vs `prometheus/compliance` (PromLabs / CNCF) | `compatibility/prometheus` | third-party conformance suite (strongest) |\n| LogQL   | real Loki vs `grafana/loki:pkg/logql/bench` corpus                  | `compatibility/loki`       | real-backend diff, Grafana bench corpus   |\n| TraceQL | real Tempo vs cerberus-owned TXTAR corpus                           | `compatibility/tempo`      | author-written corpus (lightest)          |\n\n```sh\njust compat-all          # or compat-promql / compat-logql / compat-traceql\n```\n\n**What the required checks enforce.** The three `compatibility/\u003chead\u003e`\nchecks run on every PR and **fail on infrastructure breakage** (stack\nwon't boot, seed fails, report unparseable). Per-case **parity drift is\nreport-only** by design ([#503](https://github.com/tsouza/cerberus/pull/503)):\nit is recorded in `report.json` and rendered into the live `compat-score.json`\nbadge, but does not turn the required check red. The one lane that\n_hard-fails on any parity diff_ is `compatibility/prometheus-forced-route`\n(`FAIL_ON_DIFF=1`, proving the sharded solver route is byte-identical to\nreference Prometheus over the whole corpus) — that lane is informational,\nnot a required check. The honest reading: the badges are a continuously\nre-measured conformance score, not a merge gate on numeric correctness.\n\n**No allow-lists** — every diff against the reference is a real bug to\nfix at the source, not an exception to suppress. The full playbook\n(per-head drivers, local reproduction, rejection parity, the sole pinned\n`upstream-skip-baseline` contract) is in\n[`docs/compatibility.md`](docs/compatibility.md).\n\n## Testing\n\nCerberus is tested in a 13-layer map spanning AST-shape pinning, plan-IR\ninvariants, optimiser properties, emitted-SQL goldens, chDB roundtrips,\nfunction-surface parity, HTTP wire conformance, differential harnesses,\nPlaywright UX flows, deterministic chaos / goleak, perf benchmarks +\ncompute-fan-out guards, live-stack chaos against the k3d deployment, and\nan oracle-based property framework.\n`just test` runs the core lanes; see\n[`docs/test-strategy.md`](docs/test-strategy.md) for the canonical layer\nmap, the CI-gate inventory, and the gremlins rollout.\n\n## Documentation\n\n| Doc                                                                      | What's in it                                                                                                                  |\n| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |\n| [`docs/engine.md`](docs/engine.md)                                       | The shared query pipeline, the `Lang` contract, and the per-stage breakdown.                                                  |\n| [`docs/coverage.md`](docs/coverage.md)                                   | Per-function / per-construct support status across PromQL / LogQL / TraceQL.                                                  |\n| [`docs/configuration.md`](docs/configuration.md)                         | The full `CERBERUS_*` environment-variable reference, grouped by area, with types and defaults.                               |\n| [`docs/operations.md`](docs/operations.md)                               | Runtime contract: lifecycle, scaling, the solver and experimental knobs in context.                                           |\n| [`docs/performance.md`](docs/performance.md)                             | The compute-fan-out strategy, per-layer optimisations, and how they're held against regression.                               |\n| [`docs/optimization-rules.md`](docs/optimization-rules.md)               | The standing optimizer-design rules (feature-registry single-source-of-truth, clone-less-not-faster).                         |\n| [`docs/clickhouse-optimizations.md`](docs/clickhouse-optimizations.md)   | The ClickHouse-optimization suite: feature registry, version gating, the runtime probe, and the query_log corpus reconciler.  |\n| [`docs/solver.md`](docs/solver.md)                                       | The sharded-pushdown solver: eligibility, slicing, execution, and the cancellation contract.                                  |\n| [`docs/router-rules.md`](docs/router-rules.md)                           | The offline router-rules catalog: generic drivers in the repo, per-deployment thresholds resolved from the corpus at runtime. |\n| [`docs/native-clickhouse.md`](docs/native-clickhouse.md)                 | What native ClickHouse capability cerberus uses today, and why we don't upstream aggregates (the upstream positioning).       |\n| [`docs/benchmarks.md`](docs/benchmarks.md)                               | Benchmark methodology and the recorded numbers (regenerable).                                                                 |\n| [`docs/compatibility.md`](docs/compatibility.md)                         | The differential-harness playbook for all three heads.                                                                        |\n| [`docs/test-strategy.md`](docs/test-strategy.md)                         | The 13-layer test map and CI-gate inventory.                                                                                  |\n| [`docs/observability.md`](docs/observability.md)                         | Self-observability across logs / metrics / traces (OTLP export).                                                              |\n| [`docs/health.md`](docs/health.md)                                       | `/readyz` / `/healthz` probe semantics.                                                                                       |\n| [`docs/upstream-forks.md`](docs/upstream-forks.md)                       | The `tsouza/*` parser-fork + Dependabot-watch flow.                                                                           |\n| [`docs/forbid-skip.md`](docs/forbid-skip.md)                             | The forbidden-pattern reference for the `forbid-skip` gate.                                                                   |\n\n## Contributing\n\nSmaller PRs (a new optimizer rule, a TXTAR fixture, a parser-dep bump)\nare welcome any time; open an issue or discussion before a large one. The\nlocal-dev and end-to-end commands live in\n[`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## License\n\n[Apache 2.0](LICENSE) © Thiago Souza.\n\nCerberus's LogQL and TraceQL parsers are clean-room reimplementations of the\npublished language grammars, API-compatible with Grafana Loki / Tempo but not\nderived from their AGPLv3 source. Third-party attributions and the clean-room\nstatement are in [`NOTICE`](NOTICE). Cerberus is not affiliated with or endorsed\nby Grafana Labs, Prometheus, or the OpenTelemetry project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsouza%2Fcerberus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsouza%2Fcerberus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsouza%2Fcerberus/lists"}