Open-source project
tenzir/tenzir avatar
tenzir/tenzir

tenzir: a C++ pipeline engine for security telemetry

Tenzir is the data pipeline engine for security teams.

766 stars111 forksC++BSD-3-Clause

At a glance

What is it?
Tenzir is a data pipeline engine aimed at security teams, written in C++ and built to collect, parse, shape, normalize, aggregate, store, query and route telemetry in one place. It suits teams that want to run detections in stream and cut data volume before it reaches a cloud bill, but the published Docker images come from a Nix attribute rather than the Dockerfile in the repository, and the current 6.20 line is still in release candidate form.
Who is it for?
Adopt Tenzir if your security team already has several telemetry sources and wants one engine to parse, reduce and route them, with detections running in stream rather than in a batch query.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One engine for the whole telemetry path, not another log store

Tenzir is described as the data pipeline engine for security teams, and the README gives the full chain of verbs in one sentence: collect, parse, shape, normalize, aggregate, store, query, and route security telemetry data at scale.

That list is the product description. Most tools in this space cover one or two of those verbs, which is why security data stacks tend to grow a separate collector, a separate parser, a normalisation layer and a separate query engine. Tenzir's pitch is that a pipeline handles the whole path, and the topics list backs that up: netflow, pcap, sigma, suricata, zeek, siem, soc, threat hunting, incident response and dataops all appear as tags on the repository, so the formats and use cases it targets are the ones a security operations centre actually has to deal with.

The stated benefits are the two that matter for a budget. It makes it easy to quickly onboard and store data from numerous sources, and it reduces data volumes to optimise cloud and data costs. The third is that it executes detections and runs analytics in stream, which is the difference between a detection running when someone searches and a detection running as the data arrives.

The project is written in C++ and the documentation lives at docs.tenzir.com, which the README describes as holding tutorials, how-to guides, explanations of key concepts and an in-depth reference.

The licence is BSD-3-Clause, and the repository is not archived, with the last push on 2026-10-20.

Three processes behind one endpoint

The docker-compose file in the repository is aimed at developers, and it is the clearest picture of how Tenzir is put together. It defines three services, and the split matters.

The `tenzir-node` service is the worker. It is the only service started by default, and it runs the `tenzir-node` entrypoint with two volumes attached, `tenzir-lib` for state and `tenzir-log` for logs. It listens on `TENZIR_ENDPOINT=tenzir-node:5158`, and the port is internal to the compose network rather than published to the host.

The `tenzir-api` service runs `tenzir-ctl web server --mode=dev --bind=0.0.0.0` and publishes port 5160 to the host. Its mode is explicitly dev, and it sits in a profile named `donotstart`, so it does not come up with the default compose run and has to be asked for.

The third service, also in the `donotstart` profile, runs the `tenzir` entrypoint itself and sets `TENZIR_TQL2=true`.

yaml
services:
  tenzir-node:
    image: ${TENZIR_IMAGE:-ghcr.io/tenzir/tenzir:${TENZIR_VERSION:-main}}
    build:
      target: tenzir
    environment:
      - TENZIR_PLATFORM_CONTROL_ENDPOINT=${TENZIR_PLATFORM_CONTROL_ENDPOINT:-}
      - TENZIR_TOKEN=${TENZIR_TOKEN:-}
      - TENZIR_ENDPOINT=tenzir-node:5158
    entrypoint:
      - tenzir-node
    volumes:
      - tenzir-lib:/var/lib/tenzir/
      - tenzir-log:/var/log/tenzir/

Two environment variables point outwards. `TENZIR_PLATFORM_CONTROL_ENDPOINT` and `TENZIR_TOKEN` are empty by default and clearly exist for a hosted control plane, while the compose file leaves them as overridable parameters for the developer to set.

The image reference itself is worth noting, because it defaults to a moving target. The image is `ghcr.io/tenzir/tenzir:${TENZIR_VERSION:-main}`, so an unset TENZIR_VERSION means you run whatever the main branch built last, not a released version. For a developer environment that is convenient; for anything you intend to run for a while, TENZIR_VERSION has to be set explicitly.

The healthcheck pings the API through the pipeline language

One line in the compose file tells you more about the system's design than any amount of prose would: the healthcheck is not a TCP connect.

yaml
    healthcheck:
      test: tenzir run --connection-timeout=30s --connection-retry-delay=1s 'api "/ping"'
      interval: 30s
      retries: 1

The check runs a query through the engine itself. The `api` operator is used as a pipeline element to call an endpoint, and `/ping` is the endpoint, so the healthcheck exercises the same path a real query would take: the command starts, connects with a 30 second connection timeout and a one second retry delay, and the result tells you the node is serving.

That is a different kind of healthcheck from the usual HTTP status probe. A liveness check that only opens a socket tells you the process is up; this one tells you the engine is answering queries. The trade-off is that it is slower and it depends on the engine's own machinery working, which is exactly what you want to know but also means a slow start can look like a failure. The interval is 30 seconds and the retry count is 1, so a single failure is enough to mark the container unhealthy, which is tight for a service that may be busy.

The two profile-gated services are the ones that need this to be reliable, since the API and the Tenzir client both attach to the node over the internal endpoint. A node that is up but not answering queries would leave those two hanging rather than failing.

The published images are built by Nix, not by the repository Dockerfile

The Dockerfile at the repository root starts with a comment that changes how you should read it. It says it is a local, non-Nix development and test environment, and that CI neither builds nor publishes it any more. The `tenzir/tenzir`, `tenzir/tenzir-node` and `tenzir/tenzir-demo` images all come from the Nix attribute `tenzir`, via `engine/nix/tenzir/image.nix`, and the comment instructs you to change that file when you change what the published images contain.

So the Dockerfile in the tree and the images you pull are two different things, and that is a genuine trap for anyone building their own. If you copy the compose file and point it at a local build, you are not reproducing the published image.

The Dockerfile itself is a substantial multi-stage build. It starts from `public.ecr.aws/docker/library/debian:trixie-slim` for the runtime base and `gcc:15-trixie` for the build base, sets ccache as the CMake compiler launcher for both C and C++, and then builds a series of dependency packages as separate stages: aws-sdk-cpp, google-cloud-cpp and azure-sdk-cpp, each with its own script under `scripts/debian/`, and then an arrow-package stage that collects the resulting debs and applies patches from `nix/overrides/`.

dockerfile
FROM public.ecr.aws/docker/library/debian:trixie-slim AS runtime-base

FROM gcc:15-trixie AS build-base

ENV CC="gcc" \
    CXX="g++" \
    CMAKE_C_COMPILER_LAUNCHER=ccache \
    CCACHE_DIR=/ccache \
    CMAKE_CXX_COMPILER_LAUNCHER=ccache \
    CMAKE_INSTALL_PREFIX=/usr/local

The dependency list explains the scope of the project. AWS, Google Cloud and Azure SDKs, plus Arrow, mean Tenzir reads and writes to object stores and columnar data, which is what a pipeline that moves data into a lake or out of a cloud bucket needs.

The repository also carries `flake.nix` and `flake.lock`, `shell.nix` and a `.envrc`, so the intended development environment is a Nix shell rather than a container.

A C++ core with plugins, a schema directory and a Python surface

The top level layout of the repository shows the shape of the project before you read a line of its documentation.

`libtenzir/` and `libtenzir_test/` are the core library and its tests. `tenzir/` is the main binary, and `plugins/` holds the pieces that teach the engine to read and write particular formats, which is where the netflow, pcap, zeek, suricata and sigma tags get their substance. `schema/` is a directory of its own, which suggests the data model is described somewhere declarative rather than only in C++ headers, and `python/` is a separate binding surface.

That combination is what makes the release notes meaningful. The three most recent releases are v6.20.0-rc.1, rc.2 and rc.3, published on 2026-10-09, 2026-10-12 and 2026-10-15, and all three carry the same summary: a new data model for faster pipelines. A change to the data model is not a small change, and shipping it across three release candidates in a week is how you stage a migration of that size.

The rest of the tree tells you about the surrounding practice. There is a `bench/` directory for benchmarks, a `changelog/` directory, a `VERSIONING.md` describing how versions are decided, a `container-signing.pub` for signing published images, a `vendored.spdx` file for tracking vendored dependency licences, and an `ansible/` directory for deployment. Configuration is templated rather than hardcoded, with `env.example`, `tenzir.yaml.example` and `nova.md` at the root.

The `.clang-format`, `.clang-tidy`, `.clangd`, `.cmake-format`, `.editorconfig`, `.yamllint.yaml` and `.markdownlint.json` files cover every language and file type in the tree, and `AGENTS.md` plus a `.agents/` directory sit alongside them. The project has clearly invested in making automated contributions to its own repository workable.

Against a general stream processor and against a SIEM built on a search engine

The comparison worth making is not with another security vendor but with the two shapes of tool a team usually ends up choosing between.

A general stream processor is the first option. Such a system takes events from any source, applies transformations and routes them onward, and it is a good general answer. The difference in approach is that its transformations are written against a general event model, while Tenzir's are written against a security telemetry model, with format-specific plugins for the protocols a security team actually receives and a query language aimed at that data. The Tenzir documentation hosts a reference for the query language, and the compose file shows a `TENZIR_TQL2` flag, so the language is versioned in step with the engine. If you already run a general stream processor and your data is already normalised, adding Tenzir is a second pipeline rather than a replacement.

The second option is a SIEM built on a search index. That approach keeps the raw events and writes queries against them, which is excellent for retrospective hunting and awkward for the cost profile: you store everything, and the bill follows the volume. Tenzir's stated design point is the opposite, reducing data volumes in the pipeline to optimise cloud and data costs, with aggregations happening before storage. The cost of that choice is that you cannot always ask a question of the raw event later, because the raw event was shaped on the way through.

The third option is doing nothing new, running the collectors and the query engine you have. That is a reasonable choice if the telemetry sources are few and the detections are simple. The case for a pipeline engine appears when the sources multiply, when the same parsing work is being done twice in two tools, and when the storage cost of keeping everything is the thing you most want to change.

Editorial conclusion

Adopt Tenzir if your security team already has several telemetry sources and wants one engine to parse, reduce and route them, with detections running in stream rather than in a batch query. Do not adopt it if you need a stable release to pin, because the 6.20 line is currently published as release candidates with a new data model, and check first that the published container images match what you expect, since the repository Dockerfile is now a local development environment while the shipped images are built from the Nix attribute.

Frequently asked questions

What is Tenzir and who is it for?

Tenzir is a data pipeline engine for security teams, written in C++. It collects, parses, shapes, normalizes, aggregates, stores, queries and routes security telemetry at scale, and it executes detections and analytics in stream rather than only in batch queries.

How do I run Tenzir with Docker Compose?

The repository ships a docker-compose.yaml aimed at developers, with a bundled env.example file for setup instructions. The default service is tenzir-node, and the tenzir-api and tenzir services sit in a profile named donotstart so they are not started by default.

How does Tenzir check that its node is healthy?

The healthcheck runs `tenzir run --connection-timeout=30s --connection-retry-delay=1s 'api "/ping"'` with an interval of 30 seconds and one retry. It queries the API through the engine rather than only opening a socket, so it verifies that the node is answering queries.

Where do the published Tenzir Docker images come from?

The Dockerfile in the repository is a local, non-Nix development and test environment and is no longer built or published by CI. The tenzir/tenzir, tenzir/tenzir-node and tenzir/tenzir-demo images are built from the Nix attribute tenzir via engine/nix/tenzir/image.nix.

Is Tenzir still being developed?

The repository is not archived and the last push was on 2026-10-20. The latest releases are v6.20.0-rc.3, v6.20.0-rc.2 and v6.20.0-rc.1, all released in October 2026 and all described as introducing a new data model for faster pipelines, under a BSD-3-Clause licence.

Official sources

  1. License: BSD-3-Clause
  2. Project website
  3. README
  4. Releases
  5. tenzir/tenzir on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tenzir-tenzir.svg)](https://hysenlabs.com/projects/tenzir-tenzir)

Was this useful?