Architecture
A real transport layer, not a shortcut
Agents never write storage directly. Every record passes through a Kafka-API transport, keeping storage credentials off the edge and letting ClickHouse and Tantivy fail independently of each other.
agent → ingest → Redpanda → {ClickHouse-writer, Tantivy-indexer} → api → web
Query engine
Structured filters and full-text search, compiled once
A query like service=api status>=500 | stats count by host and a query like message:"connection refused" aren't two different code paths — the compiler routes each clause to whichever backend actually answers it.
query bar → compiler → IR → {ClickHouse, Tantivy} → merged results
Multi-tenancy
Isolation at the connection layer, not a row filter
Tenant isolation lives where it can't be bypassed by a clever query: each tenant gets its own ClickHouse connection pool and its own Tantivy index, resolved from the authenticated request identity — never a parameter a client can override. The raw-SQL escape hatch runs through the same scoped connection as everything else, so there's no query shape that reaches another tenant's data.
- OIDC and SAML SSO, verified against real identity providers
- Per-resource dashboard grants, not just tenant-wide roles
- Append-only, hash-chained audit log for every query
Deployment
docker-compose to a Kubernetes fleet
Start with a single docker compose up for a homelab or small team.
Move to a Kubernetes Operator and Helm chart when you need it — the same
Tenant custom resource drives per-tenant provisioning either way,
so growing past one node doesn't mean a rewrite.
- A Go controller-runtime Operator managing one CRD
- Helm chart covering every service, gated behind one flag
- Real credentials synced onto the CR, not placeholders
See the dependencies behind it
Every choice on the stack page was picked for being proven, not novel.
View the stack →