Command
your security data
The agentic telemetry pipeline for data-driven cyber defenders.
The pipeline
Turn intent into deployable pipelines
Describe the outcome: onboard your events, make them OCSF-compliant, and remove the noise. Your agent turns that intent into a deployable pipeline.
Building blocks
Compose your security data pipelines
Let your agents collect, transform, and route all data by assembling prebuilt operators into pipelines you can read at a glance.
Tenzir pipelines are assembled from TQL building blocks: 235 operators and 250 functions. Each stage of the data lifecycle below shows an example pipeline and the blocks it uses.
Collect
Fetch paginated API results over HTTP, follow the cursor links, unpack the response envelope, and publish what arrives for the next pipeline.
let $url = "https://api.example.com/security/events"
let $headers = {
"Authorization": f"Bearer {secret("EVENTS_TOKEN")}",
}
from_http f"{$url}?limit=500",
headers=$headers,
paginate=(page => {
url: f"{$url}?limit=500&cursor={page.next_cursor}",
} if page.next_cursor != null),
paginate_delay=250ms,
max_retry_count=5 {
read_json
}
unroll events
this = events
ingest = {source: "events-api", received_at: now()}
publish "events.raw"Parse
Receive Syslog over UDP and turn the message body into typed fields with key-value and request-line parsing.
accept_udp "0.0.0.0:514"
syslog = data.parse_syslog()
fields = syslog.message.trim().parse_kv(unflatten_separator=".")
fields.http.request = fields.http.request.parse_ssv(
header=["method", "url", "version"],
)
fields.src.ip = ip(fields.src.ip)
fields.dst.ip = ip(fields.dst.ip)
fields.src.port = int(fields.src.port)
fields.dst.port = int(fields.dst.port)
this = {
timestamp: syslog.timestamp,
hostname: syslog.hostname,
app: syslog.app_name,
...fields,
}Operators: accept_udp, set
Shape
Restructure events without changing what they mean. Spread records and lists, round timestamps, test subnet membership, derive totals, and sort tags.
let $corp = 10.0.0.0/8
timestamp = timestamp.round(1s)
duration = duration.round(1ms)
client = {
...client,
internal: client.ip in $corp,
}
server = {
...server,
internal: server.ip in $corp,
}
request.path = request.url.split("?")[0]
totals = {
bytes: bytes_in + bytes_out,
packets: packets_in + packets_out,
}
tags = [...tags?, request.method.to_lower()].sort()
drop bytes_in, bytes_out, packets_in, packets_outNormalize
Map vendor fields into OCSF, ASIM, ECS, CIM, UDM, or your own schema, and keep whatever has no home yet. Every downstream tool then sees the same semantics.
unmapped = this
@name = "ocsf.network_activity"
metadata = {
version: "1.9.0",
product: {name: "PAN-OS", vendor_name: "Palo Alto Networks"},
}
category_uid = 4
class_uid = 4001
activity_id = 6
severity_id = 1
type_uid = class_uid * 100 + activity_id
time = move timestamp
src_endpoint = {ip: move src.ip, port: move src.port}
dst_endpoint = {ip: move dst.ip, port: move dst.port}
connection_info.protocol_name = (move proto).to_lower()
ocsf_derive
ocsf_castOperators: set, ocsf_derive, ocsf_cast
Functions: to_lower
Optimize
Drop what nobody queries and collapse repeats while the data is still in flight, so storage cost and analyst noise stay down.
where class_uid == 4001 and activity_id == 6
severity_id = severity_id? else 1
deduplicate src_endpoint.ip, dst_endpoint.ip,
connection_info.protocol_name,
create_timeout=1h,
count_field=suppressed
where severity_id >= 4 or suppressed > 10Operators: where, set, deduplicate
Protect
Protect sensitive fields with encryption, pseudonymization, anonymization, masking, salted hashes, redaction, and drops. Keep useful correlations without exposing raw identities.
let $ip_seed = secret("cryptopan-key").decode_hex()
let $hash_salt = env("HASH_SALT")
src_endpoint.ip = encrypt_cryptopan(src_endpoint.ip, seed=$ip_seed)
user.email_addr = f"*****@{user.email_addr.split("@")[1]}"
user.uid = user.uid.hash_sha256(seed=$hash_salt)
if card_number?.is_luhn_valid() {
card_number = card_number.slice(begin=-4).pad_start(16, "*")
} else if card_number? != null {
card_number = "******"
}
unmapped.dob = unmapped.dob.format_time("%Y-01-01").time()
unmapped.api_key = "******"
drop unmapped.password, unmapped.tokenFunctions: secret, decode_hex, env, encrypt_cryptopan, split, hash_sha256, is_luhn_valid, slice, pad_start, format_time, time
Enrich
Add domain reputation and asset inventory from live lookup tables, then ask a local model to summarize what came back.
where class_uid == 4003
// Lookup tables add reputation and asset context.
context_enrich "domain_reputation",
key=query.hostname,
into=observables,
mode="append"
context_enrich "endpoint_assets",
key=src_endpoint.ip,
into=src_endpoint
// GLM5.2 runs on local Ollama.
ai_prompt model="GLM5.2",
endpoint="http://127.0.0.1:11434/v1",
system="Summarize this enriched DNS event.",
data={
query: query.hostname,
reputation: observables,
asset: src_endpoint,
},
into=ai.summaryOperators: where, context_enrich, ai_prompt
Analyze
Model coding-agent behavior as operation distributions, duration thresholds, and distinct-session estimates.
where "ai_operation" in metadata.profiles?
window size=15min, on=time {
summarize agent=ai_agent.name,
span_duration=tdigest(trace?.span?.duration?),
operations=frequency_table(api?.operation?),
sessions=hll(ai_agent.instance_uid)
p95_duration_ms = tdigest_quantile(span_duration, 0.95)
active_sessions = hll_cardinality(sessions)
start = $window.start
end = $window.end
}
publish "ocsf.agent-insights"Operators: where, window, summarize, set, publish
Functions: tdigest, frequency_table, hll, tdigest_quantile, hll_cardinality
Detect
Spot SMB traffic spikes with event-time windows and a statistical baseline, while the events are still moving.
where class_uid == 4006 and traffic.bytes? != null
window size=70min, every=10min, on=time {
summarize src=src_endpoint.hostname, samples=count(),
avg_bytes=mean(traffic.bytes),
stdev_bytes=stddev(traffic.bytes),
current_bytes=max(traffic.bytes)
upper_bound = avg_bytes + stdev_bytes * 2
where samples >= 5 and current_bytes > upper_bound
start = $window.start
end = $window.end
}Store
Persist compressed, encrypted Parquet in object storage. Rotation and partitioning keep the lake predictable and cheap to query.
let $key = decode_base64(secret("PARQUET_KEY"))
where class_uid == 4001
year = time.year()
month = time.month()
day = time.day()
to_s3 "s3://security-lake/ocsf/network/**/data_{uuid}.parquet",
partition_by=[class_uid, metadata.product.name,
year, month, day],
max_size=128M {
write_parquet compression_type="zstd", encryption_key=$key
}Operators: where, set, to_s3, write_parquet
Functions: decode_base64, secret, year, month, day
Search
Query events where they live with federated search in TQL. Tenzir pushes supported filters and field selections down to ClickHouse as SQL.
let $yesterday = now() - 24h
from_clickhouse host="clickhouse", table="security.network_activity"
// Tenzir transpiles these filters and field selections to SQL.
where time >= $yesterday and severity_id >= 4
select time, src_endpoint, dst_endpoint, traffic, severity_id
// Sort and limit the results in Tenzir.
sort -time
head 100
where src_endpoint.ip in 10.0.0.0/8Operators: from_clickhouse, where, select, sort, head
Functions: now
Route
Split, publish, fork, and load-balance streams to the right destinations. Send alerts, archives, and SIEM feeds without duplicating collection work.
let $splunk = [
{url: "https://splunk-a:8088", token: secret("HEC_A")},
{url: "https://splunk-b:8088", token: secret("HEC_B")},
]
subscribe "ocsf.normalized"
fork {
where severity_id >= 4
publish "ocsf.alerts.high"
}
load_balance $splunk {
to_splunk $splunk.url, hec_token=$splunk.token
}Operators: subscribe, fork, where, publish, load_balance, to_splunk
Functions: secret
Replay
Send stored events back to the start of the pipeline. Repair bounded timestamp disorder, shift the timeline, and recreate an incident at twenty times its original speed.
from_file "s3://security-lake/ocsf/network/**/day=17/*.parquet" {
read_parquet
}
where time >= 2024-01-17T09:00:00Z and time < 2024-01-17T10:00:00Z
reorder on=time, tolerance=2min
timeshift time, start=now()
delay time, speed=20.0
publish "ocsf.replay"Operators: from_file, read_parquet, where, reorder, timeshift, delay, publish
Functions: now
Integrations
Connect the world of security and data
All security data at your fingertips. Unlock its value with our expanding list of integrations.
Latest resources
Fresh insights for security data teams
Schema on write turns a swamp into a lake
Blog Post · Nov 9, 2026
Raw logs let each consumer count failed logins differently. A versioned OCSF mapping makes them agree, while Parquet makes scans cheaper. They solve different problems.
Catch the login before the breakout
Blog Post · Nov 7, 2026
Tenzir raises an alert seconds after the log line that reveals an attack. A rule on a 15-minute search schedule needs 11.5 minutes on average, while eCrime attackers move on in 29. This week's OpenSSH password bypass shows how.
Context decides which alerts matter
Blog Post · Nov 6, 2026
A firewall rates alerts by signature, not by target. Enriching alerts in OCSF with asset, vulnerability, and reputation context lets a pipeline set each alert's priority before anyone triages it, and shrinks the analyst queue by 85%.
Customer voices
Why security teams choose Tenzir
Tenzir gave us a security data lake that runs entirely inside our own AWS VPC. We normalize logs into open formats in S3 and query terabytes of history at low cost, with no SIEM lock-in. Privacy is the foundation of everything we build, and that principle doesn't stop at our own telemetry.
Ashley Blackmore
Security Engineer, Cape
Tenzir empowered us to create a federated security operations architecture that allows us to focus on content and people instead of technology. This has provided our organization with the necessary capability and flexibility to support new features, growth, and expansion.
Dr. Andreas Rohr
Managing Director, DCSO
At G+D, our telemetry was bound to the tools that consumed it, so every architectural decision was dictated by a vendor's ingestion model. Tenzir decoupled the two. We now govern every data flow across our infrastructure from one place, independent of any platform downstream. That organizational control is the real value, and cost discipline is its direct result: we decide which telemetry is worth keeping, so we pay for the volume we choose rather than the volume we inherit.
Thorsten Delbrouck
Chief Security Officer, Giesecke+Devrient
As an MDR provider defending European organizations against advanced adversaries, our assume-breach mindset requires broad and deep security telemetry—not just to detect threats, but to investigate incidents and understand root cause. The challenge is that ingesting everything into the detection tier quickly becomes economically unsustainable. Tenzir lets us keep the visibility we need while controlling cost, scaling efficiently, and keeping analysts focused on what matters.
Joost Bijl
Head of Product, Hunt & Hackett
Tenzir gives Swiss Post Cybersecurity a unified control layer for security data. We normalize and optimize security-relevant telemetry once using OCSF, which unlocks delivery to multiple SIEM platforms from the same foundation. This transforms recurring integration effort into a strategic advantage for our Cyber Defense Center.
Jürgen Anthamatten
CPO Chief Product & Portfolio Officer, Swiss Post Cybersecurity
Built for the agentic era
One data pipeline for humans and agents
Shape, enrich, and route any security data with one pipeline language that humans and agents share. Your engineers write it directly, and your AI agents generate it from natural language.
One data pipeline for humans and agents
Shape, enrich, and route any security data with one pipeline language that humans and agents share. Your engineers write it directly, and your AI agents generate it from natural language.















