The Ops Atlas xOps School

Security & Compliance Emerging practice Sources verified

SecDataOps

SecDataOps is the engineering and governance of security telemetry: onboarding log sources, parsing and normalising them to a schema, enriching, routing and tiering them, and retaining them so that detections, investigations and audits have complete, trustworthy and affordable evidence.

Key takeaways

  • The label is young and vendor-led; the practice is old and government-documented as log management, event logging and SIEM pre-processing.
  • Treat every log source as a product with a contract: owner, schema, required fields, freshness, retention tier and a canary detection.
  • Silence is the worst failure: build the monitor that notices a source stopped before an incident does.
  • Tier by use, not by habit: hot for detections, a lake for investigations and hunting, cold for obligations; 90 days is rarely enough for intrusions.
  • Normalise to one shared schema, such as the open-source OCSF, but keep the raw event; measure parse rate and field completeness, not just gigabytes.
  • Cost is a design input: pre-process at source, forwarder or ingestion, and justify each stream by the decisions it informs.
  • Parse strictly and keep the event's own time: an unrecognised line belongs in quarantine, not in the SIEM as a guessed 'deny'.

SecDataOps is the discipline of engineering security telemetry as a product. It decides which logs to collect, gets them out of the systems that produce them, parses and normalises them to a common schema, enriches them, routes them to the storage tier that fits their use, keeps them as long as investigations and obligations require, and measures all of this. The output is evidence that is complete, parsed, timely and affordable, ready for DetectionOps, SOC Ops, ThreatOps and ResponseOps.

The name is not standardised, so this page states its reading: SecDataOps is data engineering for defenders. It is an editorial distinction of this Atlas that SecDataOps builds data for security while DataSecOps secures business data; neither label has an industry definition.

Who it is for: security data engineers; engineers and architects of the SIEM (security information and event management platform) and the SOC (security operations centre); detection engineers whose fields are missing; and leaders paying an ingestion bill they cannot explain. Practitioners: start at the reference implementation; leaders: the decision framework and judgement call.

Item Summary
Goal Complete, parsed, timely, trustworthy and affordable security telemetry for every decision defenders make
Scope (this page) Source coverage, collection, parsing and normalisation to a shared schema, enrichment, redaction, routing, delivery guarantees, SIEM and lake tiers, retention, data-quality contracts and metrics, cost of ingest
Prerequisites What a SIEM does (SOC Ops); basic JSON and one query language
Key outputs Coverage register, a telemetry contract per source, versioned parsers, a tiering and retention policy, a source-health monitor, a cost model per stream
Term status Emerging. In public use since at least March 2023 by a few vendors and writers with differing definitions; no standards body or certification defines it. Governments document the practice as log management and event logging.[1][2]

Security teams rarely lack data; they lack usable data. The Australian Signals Directorate’s Australian Cyber Security Centre (ASD’s ACSC), writing with the US Cybersecurity and Infrastructure Security Agency (CISA) and others in 2025, saw teams “preferring to collect and centralise too much data, which makes it more difficult to develop effective rules and filters”, while incomplete data produces false negatives.[3] Query’s 2026 white paper describes retention “driven by licensing pressure rather than investigative needs”.[4] Three failures recur:

  • Silence. A source stops sending and nobody notices until an investigation needs it. ASD: “The absence of logs from systems should be a concern to organisations.”[3]
  • Too short a memory. Mandiant’s 2026 report on its 2025 investigations puts the global median dwell time at 14 days, but 122 days for espionage and North Korean IT-worker cases, and warns that with threats such as BRICKSTORM “achieving dwell times of nearly 400 days, standard 90-day log retention policies leave organizations completely blind to the initial access vector and the full scope of the intrusion”.[5] ASD agrees: “Default log retention periods are often insufficient.”[2]
  • The field is missing, or costs extra. The US State Department found the 2023 Exchange Online intrusion because it had bought the premium audit tier exposing the MailItemsAccessed log and had built a custom rule on it; the Cyber Safety Review Board (CSRB) adds that “Just purchasing the additional logging alone would not have been enough”, and recommended access logs “without additional charges, with a minimum default retention of six months”.[6] In July 2023, after the intrusion, Microsoft announced email-access logs and 180-day default retention for its standard audit tier.[7]

The cost is paid during an incident, when “what did the attacker touch?” meets a source never onboarded, a parser that dropped the field, or a retention window that ended last month.

  • Not DataSecOps. DataSecOps protects business data; the two meet where a data platform’s access history becomes a log source.
  • Not detection engineering or the SOC. Rules belong to DetectionOps, triage to SOC Ops; DetectionOps names a data gap, SecDataOps owns the fix.
  • Not application observability or generic DataOps. ObservabilityOps shares the collectors and DataOps teaches tests and contracts; security telemetry adds its own schemas, coverage against attacker techniques, forensic retention, and an adversary who benefits from gaps.
  • Not a product category. Forrester calls the tools “data pipeline management (DPM)”;[8] the practice exists without them.

Misconceptions: that a SIEM licence includes log management (it includes storage and search, not coverage or quality), and that “ingest everything” is safer than selection.[3]

Where the label comes from and where it sits

Link to section “Where the label comes from and where it sits”

The practice is older than the name. NIST’s 2023 draft log-management guide defines log management as “the process for generating, transmitting, storing, accessing, and disposing of log data”;[1] M-21-31 (2021) set US federal logging maturity tiers (EL0 “Not Effective” to EL3 “Advanced”) and retention minimums;[9] and a 2024 publication led by ASD’s ACSC with CISA, the FBI, the NSA and partners from seven other countries set a baseline for “event logging best practices”.[2]

The compound label is recent, and its users disagree on its scope:

  • In March 2023 Jonathan Rau described it as “the process of integrating data into the entire security life cycle”.[10]
  • Tenzir, a pipeline vendor, calls itself “the pioneer in SecDataOps”[11] and defines the term as applying “DataOps principles to security”:[12] a vendor’s argument, not an adopted standard.
  • Query, a federated-search vendor, has run a “SecDataOps Cast” podcast since 2024 whose description asks: “What is SecDataOps? Who should care about it? Wait, wasn’t it a throwaway joke?”[13] Its 2025 workshop posts[14] and its 2026 white paper use the long form “Security Data Operations (SDO)”, which the white paper defines as “the discipline of managing security data intentionally from creation through retirement, with the explicit goal of improving security outcomes”.[4]
  • Forrester (November 2024) calls the tool category “data pipeline management (DPM)” and notes such tools “are not new” to enterprise data teams.[8]

So the label is emerging: real and vendor-led, with definitions from whole-lifecycle data integration to pipeline engineering. This page takes the telemetry-engineering reading; the whole-lifecycle reading overlaps SecOps and SOC Ops.

Term Meaning Why it matters
SIEM The platform that stores, searches and alerts on security events The usual hot tier and largest bill
Coverage Share of expected producers actually reporting A rule that sees 300 of 1,200 laptops is a quarter of a rule
Schema Shared field names and types: OCSF (Open Cybersecurity Schema Framework, Linux Foundation); ECS (Elastic Common Schema), merging into OpenTelemetry (OTel) conventions; vendor models ASIM (Microsoft Sentinel), CIM (Splunk) and UDM (Google Security Operations) Pick what your consumers query in
Parse / normalise Turning a raw event into typed schema fields, e.g. OCSF Authentication (class 3002)[15] Rules work across vendors only on normalised fields
Pipeline Software that parses, enriches, redacts and routes between collection and storage, e.g. Vector, programmed in VRL (Vector Remap Language) Where cost is controlled and fields are lost
Tiers Hot: SIEM storage that alerts; warm: a lake of Parquet (open columnar) files queried in minutes; cold: an archive restored for audits Where most cost and much risk lives
Telemetry contract One source’s agreement: owner, schema, required fields, freshness, tier, canary Makes “the data is broken” measurable
Data component (MITRE ATT&CK) What a source must show to detect a technique in ATT&CK, a public knowledge base of adversary tactics and techniques: 120 across domains, 106 in Enterprise; “data sources” deprecated in v18 (October 2025)[16][17] Coverage against adversary behaviour
DeTT&CT scoring Detect Tactics, Techniques & Combat Threats, an open-source framework by Marcus Bakker and Ruben Bouman, sponsored by Rabobank:[18] 0–5 scoring on device completeness, field completeness, timeliness, consistency and retention[19] An open rubric (Lab 1)

The mental model is the evidence supply chain: producers → collection → pipeline (parse, normalise, enrich, redact, route) → tiers → consumers (detections, hunts, investigations, audits). Each link has an owner, a contract and a health signal; because an adversary benefits from any gap, monitoring the chain is a security control.

Figure 1. Where SecDataOps sits among its neighbours
data gapssame collectorsaccess logs are a sourcetests, contractsLog sources: endpoints,identity,cloud, network, SaaS, dataplatformsSecDataOpsonboard · parse · normalise ·enrichroute · tier · retain · measureDetectionOpstested detectionsSOC OpstriageResponseOpsinvestigation, forensicsThreatOpshuntingObservabilityOpsapplication telemetryDataSecOpsprotecting business dataDataOpspipeline methods
Diagram source (Mermaid)
flowchart TB
  SRC["`Log sources: endpoints, identity,
  cloud, network, SaaS, data platforms`"]:::neutral
  SD["`SecDataOps
  onboard · parse · normalise · enrich
  route · tier · retain · measure`"]:::core
  DET["`DetectionOps
  tested detections`"]:::neutral
  SOC["`SOC Ops
  triage`"]:::neutral
  RESP["`ResponseOps
  investigation, forensics`"]:::neutral
  THR["`ThreatOps
  hunting`"]:::neutral
  OBS["`ObservabilityOps
  application telemetry`"]:::decision
  DSO["`DataSecOps
  protecting business data`"]:::decision
  DO["`DataOps
  pipeline methods`"]:::decision
  SRC --> SD
  SD --> DET --> SOC
  SD --> RESP
  SD --> THR
  DET -. "data gaps" .-> SD
  OBS -. "same collectors" .- SD
  DSO -. "access logs are a source" .-> SD
  DO -. "tests, contracts" .-> SD
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029

Text equivalent: Log sources feed SecDataOps, which supplies DetectionOps (whose detections feed SOC Ops), ResponseOps and ThreatOps; DetectionOps returns data-gap requests. ObservabilityOps shares collectors, DataSecOps hands over access logs, and DataOps contributes tests and contracts.

Neighbouring Ops Shared scope Unique focus there
DataSecOps Data-platform audit logs The data is the protected object: policies, masking, access audit
DetectionOps Field requirements; Sigma logsource; fixtures Detection as code, tests, tuning
SOC Ops SIEM in daily use; source-health tickets Tiers, triage, escalation, handover
ResponseOps and ThreatOps Retention and clock sync; enrichment and hunt data needs Containment and evidence; intelligence and hunting
DataOps and ObservabilityOps Pipelines, tests, contracts; collectors and redaction Business data pipelines; reliability telemetry
ComplianceOps and CloudSecOps Retention obligations; cloud audit logs as sources Control evidence; cloud posture

This is the Atlas’s proposed charter, not a published standard. The nearest official baseline is the ASD-led guidance’s “four key factors”: a logging policy, centralised access and correlation, secure storage and integrity, and a detection strategy.[2]

Mission. Give defenders complete, parsed, timely, trustworthy and affordable evidence, and know which of those five words is false for which source.

  1. Every source is a product: owner, contract, cost, consumer. No consumer, no ingest.
  2. Silence is an incident, with a detection-time target and a runbook.
  3. Keep the raw, publish the normalised. Query-time parsing preserves the source format; ingest-time normalisation queries faster; Microsoft documents both for Sentinel.[20]
  4. One schema, versioned, with room for the unmapped. OCSF reserves unmapped for fields with no place in the class.[21]
  5. Tier by use. ASD recommends “a centralised event logging facility such as a secured data lake” feeding select, processed logs to the SIEM.[2]
  6. Time is a field you must trust. UTC, ISO 8601 with milliseconds (ASD’s model: 2024-07-25T20:54:59.649Z),[2] synchronised clocks, a zone designator;[9] the event’s own time kept apart from the pipeline’s processing time.
  7. Cost is a design input. Pre-process at source, forwarder or ingestion; name the decision each stream informs.[3]
  8. Measure the data, not only the bill. Coverage, parse rate, completeness, freshness and retention compliance are the service levels.

Non-goals. Writing detections, running triage, protecting business data, replacing the observability stack.

Layer Representative verified options Note
Producers Windows audit policy and Sysmon;[22] CloudTrail management and data events;[23] Snowflake ACCESS_HISTORY[24] The field exists only if the producer emits it
Collection Windows Event Forwarding;[25] Fluent Bit;[26] OpenTelemetry Collector; vendor connectors WEF’s buffer is the local event log, overwritten silently[25]
Pipeline Vector remap (VRL) and route;[27][28] Fluent Bit filters; OTel processors incl. redaction;[29] DPM products[8] Where cost is controlled and fields are lost
Schema OCSF 1.9.0;[30] ECS 9.5.0 converging with OTel;[31][32] ASIM;[20] Splunk CIM;[33] UDM[34] Pick what your consumers query in
Tiers SIEM tables; Amazon Security Lake (Parquet, OCSF);[35] Sentinel data lake (Parquet, 12 years);[36] Azure Monitor Analytics/Basic/Auxiliary plans[37] Retention and query capability differ by tier
Control plane Register, contracts, parser tests, health monitor, cost model The part most teams lack

SecDataOps rarely starts as a team: one SIEM engineer with a spreadsheet in small organisations, a security data group between the SOC and platform teams in large ones. The 2025 ASD guidance names a SIEM System Owner who “should be a standing member of the organisation’s enterprise architecture group and/or change control board”.[3]

Activity Security data engineer Source owner Detection engineer SOC lead CISO
Decide what to onboard R C C (states fields) C A
Enable audit policy at the producer C R/A I I I
Write and test parser and mapping R/A C (samples) C (fixtures) I I
Set tier and retention per stream R C (volume) C C A
Monitor source health R/A I I C I
Approve the ingest budget C I I C A

Skills: one query language, one pipeline language, the chosen schema, and enough forensics to know which fields an investigator will ask for.

Day 0 (design): inventory sources and consumers; choose the schema; design tiers and retention; decide where pre-processing happens. Day 1 (onboard): per source, enable at the producer, collect, parse, map, test against fixtures, route, publish a canary, record in the register. Day 2 (operate): watch health, parse rate and completeness; handle producer changes; re-tier streams nobody queries; age data off. Most cost and nearly all failures live in Day 2, which SIEM projects usually leave unstaffed.

An enterprise-approved logging policy names what is collected, for how long, and when collection is reassessed.[2] New-source requests arrive through one intake form (template). Producers’ changes pass the same change board as the pipeline’s.[3] Reviews: monthly source health and cost per stream; quarterly coverage against ATT&CK data components; annually retention against obligations. Escalation: a silent high-criticality source pages the engineer and informs the SOC lead; a parse-rate breach opens a data incident; a retention breach goes to the compliance owner.

Figure 2. Reference architecture: the evidence supply chain with a control plane
ProducersmeasuresmeasuresEndpoints: audit policy,SysmonIdentity, cloud, SaaS APIsNetwork, data platformsCollection: agents,forwarders, API pullsParse and normalise(OCSF / ECS / ASIM / CIM /UDM)Enrich, redact, filter, routeHot tier: SIEM analyticsdays to monthsWarm tier: lake (Parquet)months to yearsCold tier: archiveobligationsDetections and alertsInvestigations, huntsAudits, reportsControl plane: register,contracts, parser tests,health monitor, cost model
Diagram source (Mermaid)
flowchart TB
  subgraph P["Producers"]
    P1["Endpoints: audit policy, Sysmon"]
    P2["Identity, cloud, SaaS APIs"]
    P3["Network, data platforms"]
  end
  C1["Collection: agents, forwarders, API pulls"]
  L1["`Parse and normalise
  (OCSF / ECS / ASIM / CIM / UDM)`"]
  L2["Enrich, redact, filter, route"]
  T1["`Hot tier: SIEM analytics
  days to months`"]:::core
  T2["`Warm tier: lake (Parquet)
  months to years`"]:::core
  T3["`Cold tier: archive
  obligations`"]:::neutral
  K1["Detections and alerts"]
  K2["Investigations, hunts"]
  K3["Audits, reports"]
  CP["`Control plane: register,
  contracts, parser tests,
  health monitor, cost model`"]:::decision
  CP -. "measures" .-> L2
  CP -. "measures" .-> T1
  P1 --> C1
  P2 --> C1
  P3 --> C1
  C1 --> L1 --> L2
  L2 --> T1 --> K1
  L2 --> T2 --> K2
  L2 --> T3 --> K3
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029

Text equivalent: Producers (endpoints; identity, cloud and SaaS APIs; network and data platforms) feed collection, then a pipeline that parses, normalises, enriches, redacts, filters and routes to a hot SIEM tier for detections, a warm Parquet lake for investigations and hunts, and a cold archive for audits. A control plane (register, contracts, parser tests, health monitor, cost model) measures the pipeline and the hot tier.

Trust boundaries. Producers are inside the adversary’s reach: attackers “are known to modify or delete local system event logs”, so ASD recommends aggregation, Transport Layer Security (TLS) 1.3 in transit, least-privilege access and a “separate or segmented network” for log storage.[2] The pipeline drops fields, so its configuration is versioned and tested like detection code. Tiers differ in capability, not only cost: Azure Monitor’s Auxiliary plan has minimal ingestion cost but slower queries, no alerts and no restore (as of October 2026).[37]

Pattern Strengths Limits Fits when
SIEM-only, everything hot One tool, alerts on everything Ingest-priced; short retention; over-collection trap[3] Small estate
SIEM with native tiers Sentinel mirrors analytics data to a Parquet lake tier with up to 12 years’ retention (as of October 2026)[36] Tier limits must be learnt;[37] one vendor Already on that SIEM
Pipeline tool in front Routing, reduction, redaction outside the SIEM licence[8][38] One more system; dropped fields invisible downstream High volume, several destinations
Security data lake with OCSF Open format, cheap storage, many subscribers, lifecycle per Region[35][39] Custom sources need OCSF 1.3 or earlier, in Parquet, strictly partitioned;[40] no detection engine alone Multi-account cloud, long retention
Federated query No bulk data movement; the SIEM keeps real-time alerting[14] More engineering effort;[14] speed and cost set by each store, and some tiers bill per query[37] Many existing stores

End-to-end workflow: onboarding a log source

Link to section “End-to-end workflow: onboarding a log source”
Figure 3. Onboarding a source, with the exception path
yesyesnonoyesnoyesnoRequest: consumer namesthe decision, fieldsand freshnessAlready in thecoverage register?Contract covers thefields and freshness?Close: point the consumerto the existing tableWrite or amend the contract:owner, class, required fields,freshness, tier, retentionEnable at the producer;collect samples; writeparser, mapping, fixturesContract tests pass?parse rate, UTC,completenessFix parser or renegotiatefields with the ownerRoute and tier; set retention;estimate cost per dayCanary detection fireson live events?Publish in register; enablesource-health monitor
Diagram source (Mermaid)
%%{init: {"flowchart": {"rankSpacing": 24}}}%%
flowchart TB
  A["`Request: consumer names
  the decision, fields
  and freshness`"]:::neutral
  B{"`Already in the
  coverage register?`"}:::decision
  K{"`Contract covers the
  fields and freshness?`"}:::decision
  L["`Close: point the consumer
  to the existing table`"]:::neutral
  C["`Write or amend the contract:
  owner, class, required fields,
  freshness, tier, retention`"]:::core
  E["`Enable at the producer;
  collect samples; write
  parser, mapping, fixtures`"]:::core
  F{"`Contract tests pass?
  parse rate, UTC,
  completeness`"}:::decision
  G["`Fix parser or renegotiate
  fields with the owner`"]:::risk
  H["`Route and tier; set retention;
  estimate cost per day`"]:::core
  I{"`Canary detection fires
  on live events?`"}:::decision
  J["`Publish in register; enable
  source-health monitor`"]:::core
  A --> B
  B -- "yes" --> K
  K -- "yes" --> L
  K -- "no" --> C
  B -- "no" --> C
  C --> E --> F
  F -- "yes" --> H --> I
  F -- "no" --> G --> E
  I -- "yes" --> J
  I -- "no" --> G
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029

Text equivalent: A request names the decision, fields and freshness. If the source is registered and its contract covers them, the consumer is pointed to the existing table. Otherwise the engineer writes or amends the contract, enables the producer, writes parser, mapping and fixtures, and runs contract tests (parse rate, completeness, UTC); failures loop back to fix the parser or renegotiate fields. Passing data is routed, tiered and costed, and a canary must fire on live events before the source is published with its health monitor.

Happy path. A detection engineer asks for Entra ID sign-ins with user, src_endpoint.ip, is_mfa and status within five minutes. The contract names OCSF Authentication (3002), whose required attributes include time, user and type_uid, with is_mfa, src_endpoint and status recommended.[15] Exception path. The sample shows failed sign-ins without a source IP for one client type: fix the mapping if the field is elsewhere in the raw event, or record the exception in the contract so the rule’s blind spot is named.

Practice Goal Output Verification
Coverage register Know every source, owner, state, score Register with DeTT&CT-style scores[19] Review against ATT&CK data components[16]
Telemetry contract Make quality measurable per source Contract file in Git Contract tests in CI on fixtures
Parser as code Reproducible normalisation Versioned config, fixtures CI: parse rate, completeness
Source-health monitor Detect silence and degradation Alert with runbook Monthly: stop a lab source, time the alert
Pre-processing policy Cut cost without cutting evidence Per-stream rule: keep, trim, summarise, route, drop Replay check that detections still fire
Redaction at pipeline Keep secrets and personal data out of hot tiers Allow-listed keys, blocked patterns[29] Planted-secret fixture never reaches the sink

Playbook: cutting a high-volume stream without losing evidence. (1) List the detections and investigation questions that use it, from rule definitions and 90 days of analyst queries. (2) Classify fields as needed-hot, needed-cold or unused. (3) Choose the lever: drop unused fields; summarise per minute for the hot tier; route the full stream to the lake; sample only if no detection depends on individual events. Cribl documents the same levers: “Always consider which data is mission-critical for security analysis in your SIEM.”[38] (4) Replay a week of raw data; every dependent detection must still fire. (5) Record the decision in the contract. ASD places pre-processing “at source/host, at forwarder/replication, or at SIEM ingestion”; the further upstream, the cheaper and the less reversible.[3]

Enrichment: what to add, and when. Context turns an IP address into a host, an owner and a verdict.

Context Typical source Add at If the source fails
Host and owner for an IP DHCP leases, CMDB (configuration database), EDR (endpoint detection) inventory Ingest: leases move within hours An IP nobody can name; mark it unenriched, never guess
User department and privilege Directory or HR export Ingest; query time for “who is it now” Stale fields read as “unprivileged”
Geolocation and network owner (ASN) GeoIP database Ingest, with the database version Locations shift when the database updates
Threat-intelligence match Intelligence feeds Query time, or re-run over old data Ingest-only matching misses indicators published later

Ingest-time enrichment records what was true when the event happened, which an investigator needs months later; query-time enrichment stays current and cheap to change, which suits intelligence that improves later. That split is this Atlas’s reasoning, consistent with M-21-31 requiring DHCP lease information[9] and ASD advising that a SIEM “should ingest threat intelligence information”.[3] OCSF carries results in an enrichments array.[41] Give each enrichment source a health check: when the CMDB export stops, events still arrive but lose their meaning.

Delivery guarantees. Pipelines lose evidence in buffers. In Vector, in-memory buffers “are not durable” and “push-based sources may not be able to retransmit their messages”; for durability the documentation recommends disk buffers.[42] End-to-end acknowledgements make a source wait until sinks confirm delivery,[43] but only sources that can acknowledge benefit, and Vector’s syslog source cannot.[44] Retries and replays can deliver an event twice, so give each event a stable metadata.uid and deduplicate on it before counting, or a replayed day doubles the coverage numbers.

Item Type What it gives SecDataOps
NIST SP 800-53 Rev. 5 AU-11 Control catalogue “Retain audit records for [Assignment: organization-defined time period]”[45]
OMB M-21-31 (2021) Binding on US federal agencies (not national security systems) Tiers EL0–EL3; criticality 0–3 sets which logs each tier must cover; minimum 12 months active plus 18 months cold for most log categories at every criticality; 72 hours for full packet capture; firewall permit and deny events; clock rules; minimum fields per event[9]
ASD-led event logging (2024) and SIEM/SOAR (2025) guidance Multinational guidance Four key factors; JSON with consistent schemas; UTC/ISO 8601; tiering; integrity;[2] for organisations new to detection, a retention starting point of one year for administrative and security logs and 90 days for informational logs, tailored to risk; pre-processing points[3]
CERT-In directions (India, 2022) Binding directions 180-day rolling retention within India; clocks synced to NTP (Network Time Protocol) servers of the National Informatics Centre or National Physical Laboratory; 6-hour reporting[46]
OCSF (Linux Foundation, Apache 2.0) Open schema Categories, classes, objects, profiles, extensions; type_uid = class_uid × 100 + activity_id; SemVer;[47][21] 1.9.0 released 2026-08-03[30]
ATT&CK data components (v18+) Open knowledge base 106 Enterprise components (120 across all domains) tied to detection strategies and analytics[16][17]
Sigma logsource Open rule format category/product/service; “any mismatches in logsource can render the detection ineffective”[48]

Regulations and directions set minimum retention and sometimes clock and reporting rules; everything else above is a recommendation or a vocabulary. Take legal advice on which regimes apply.

Representative tools per layer are in the building blocks; the forwarding-format trap is under cost. Selection risks: pipeline products (Cribl, Tenzir and others Forrester lists[8]) hide dropped fields from consumers; ingest pricing shapes SIEM behaviour; a lake alone does not alert; and CISA’s Logging Made Easy (Wazuh, Elastic, ElastAlert) lost support on 2026-05-22.[49] Choose on openness of format, the tests you can run in CI, and the cost of leaving.

Automate CI for parsers and contracts, the health monitor, and lifecycle enforcement.[39] Keep humans on dropping a field or stream, shortening retention, and changing a schema version a consumer depends on. AI is a consumer of this work before it is a tool for it: Query argues that AI systems “depend on clean, well-structured, and accessible data”;[4] evaluation is in AIOps for Security. Machine-written parsers pass the same fixture-and-canary gate as human ones.

An illustrative design for about 1,200 managed Windows endpoints, Entra ID, 42 AWS accounts, six firewalls, four DNS resolvers and a Snowflake warehouse: OCSF-normalised events, 90 days hot, a Parquet lake for 400 days, a cold archive for an 18-month obligation, and a source-health monitor. All values are placeholders.

Artefact 1: the coverage register, scored on DeTT&CT’s five dimensions,[19] is the input to Lab 1.

Example status: JSON syntax-checked; this exact file was used in the executed run in Lab 1.

{
  "assessed": "2026-10-17",
  "desired_retention_days": 365,
  "sources": [
    {"id": "win-security", "name": "Windows Security log (4624/4625/4688/4697)",
     "ocsf_class": "authentication, process_activity", "devices_expected": 1200,
     "devices_reporting": 1104, "required_fields": 9, "fields_populated": 9,
     "median_lag_seconds": 45, "schema_mapped_fields_pct": 100, "retention_days": 365,
     "owner": "endpoint-platform"},
    {"id": "sysmon", "name": "Sysmon (1, 3, 11, 22)",
     "ocsf_class": "process_activity, network_activity, dns_activity",
     "devices_expected": 1200, "devices_reporting": 310, "required_fields": 7,
     "fields_populated": 6, "median_lag_seconds": 60, "schema_mapped_fields_pct": 85,
     "retention_days": 90, "owner": "endpoint-platform"},
    {"id": "entra-signin", "name": "Entra ID sign-in logs",
     "ocsf_class": "authentication", "devices_expected": 1, "devices_reporting": 1,
     "required_fields": 8, "fields_populated": 8, "median_lag_seconds": 150,
     "schema_mapped_fields_pct": 100, "retention_days": 400, "owner": "identity"},
    {"id": "cloudtrail-mgmt", "name": "CloudTrail management events (42 accounts)",
     "ocsf_class": "api_activity", "devices_expected": 42, "devices_reporting": 42,
     "required_fields": 6, "fields_populated": 6, "median_lag_seconds": 900,
     "schema_mapped_fields_pct": 100, "retention_days": 400, "owner": "cloud-platform"},
    {"id": "cloudtrail-s3-data",
     "name": "CloudTrail S3 data events (crown-jewel buckets)",
     "ocsf_class": "api_activity", "devices_expected": 12, "devices_reporting": 5,
     "required_fields": 6, "fields_populated": 6, "median_lag_seconds": 900,
     "schema_mapped_fields_pct": 100, "retention_days": 400, "owner": "cloud-platform"},
    {"id": "fw-allow", "name": "Perimeter firewall allowed flows",
     "ocsf_class": "network_activity", "devices_expected": 6, "devices_reporting": 6,
     "required_fields": 7, "fields_populated": 5, "median_lag_seconds": 20,
     "schema_mapped_fields_pct": 60, "retention_days": 30, "owner": "network"},
    {"id": "dns-resolver", "name": "Internal DNS resolver query logs",
     "ocsf_class": "dns_activity", "devices_expected": 4, "devices_reporting": 0,
     "required_fields": 5, "fields_populated": 0, "median_lag_seconds": 0,
     "schema_mapped_fields_pct": 0, "retention_days": 0, "owner": "network"},
    {"id": "snowflake-access", "name": "Snowflake ACCESS_HISTORY",
     "ocsf_class": "api_activity (custom mapping)", "devices_expected": 1,
     "devices_reporting": 1, "required_fields": 6, "fields_populated": 5,
     "median_lag_seconds": 10800, "schema_mapped_fields_pct": 70, "retention_days": 365,
     "owner": "data-platform", "licence_gate": "Snowflake Enterprise Edition or higher"}
  ]
}

cloudtrail-s3-data reports 5 of 12 crown-jewel buckets because data events are opt-in per resource and billed separately;[23] snowflake-access needs Enterprise Edition or higher, lags three hours because ACCESS_HISTORY “may be up to 180 minutes” behind, and excludes failed queries,[24] so a detection that needs failed attempts uses QUERY_HISTORY, whose latency is up to 45 minutes (both latencies as of October 2026).[50]

Artefact 2: the telemetry contract, one file per source, versioned next to the parser.

Example status: YAML syntax-checked; a proposal of this Atlas, not a standard format.

# contracts/entra-signin.yaml
source_id: entra-signin
owner: identity-team@example.com
producer: "Microsoft Entra ID sign-in logs (interactive and non-interactive)"
collection: "vendor connector, API pull every minute"
schema:
  standard: OCSF
  class_uid: 3002            # Authentication
  versions:                  # pin per destination; re-test on upgrade
    siem: "1.9.0"            # the SIEM's OCSF mapping
    lake: "1.3.0"            # Security Lake custom sources: 1.3 or earlier (October 2026)
required_fields:             # this organisation's detection contract, beyond OCSF's own required list
  - time                     # UTC, ISO 8601, millisecond precision
  - user.name
  - src_endpoint.ip
  - status_id
  - is_mfa
  - auth_protocol_id
service_levels:
  freshness_p95_seconds: 300
  parse_success_rate_min: 0.995
  field_completeness_min: 0.98
  max_silence_seconds: 900   # page after 15 minutes without events in business hours
tiering:                     # days count from ingestion; tiers overlap, they do not add up
  hot_days: 90               # SIEM analytics tier
  lake_days: 400             # Parquet lake, queryable
  cold_days: 548             # archive for the 18-month obligation
enrichment:                  # added at ingest, so values are those true at event time
  - "src_endpoint.location: GeoIP database, version recorded"
  - "user department and manager: HR directory export, refreshed daily"
delivery: "at least once; duplicates removed on metadata.uid"
canary:
  description: "A test-account sign-in from a lab IP must appear normalised within freshness_p95_seconds"
  schedule: "hourly"
known_gaps:
  - "Legacy-protocol failures carry no src_endpoint.ip for one client type; completeness measured excluding them"
cost:
  estimated_gb_per_day: 2.5
  justification: "Password spraying, impossible travel, MFA-fatigue detections; identity investigations"
changelog:
  - "2026-10-17 first version"

Artefact 3: the pipeline. A Vector configuration that receives firewall syslog over TLS, maps each line to OCSF Network Activity (4001), routes by use, and sends anything it cannot parse to a quarantine file. In Vector, remap runs a VRL program per event; with drop_on_error, drop_on_abort and reroute_dropped, a failed or aborted event leaves through a dropped output in its original form.[27] route sends unmatched events to <name>._unmatched.[28] Vector 0.59.0 was current on 2026-10-06.[51]

Example status: YAML syntax-checked by the validator; not executed (Vector is not installed on the authoring host). The VRL was written from the documentation and needs a run against your samples (Lab 2).

# vector.yaml: firewall flows over TLS syslog -> OCSF Network Activity 1.3.0 -> hot, lake, quarantine
secret:
  local:
    type: directory
    path: /etc/vector/secrets        # one file per secret, readable only by the vector user
    remove_trailing_whitespace: true # a final newline would otherwise become part of the token

sources:
  fw_syslog:
    type: syslog
    mode: tcp
    address: "10.0.5.10:6514"        # the collector's logging interface, not 0.0.0.0
    permit_origin: ["10.0.9.0/28"]   # the six firewalls only
    tls:
      enabled: true
      crt_file: /etc/vector/tls/collector.crt
      key_file: /etc/vector/tls/collector.key
      ca_file: /etc/vector/tls/firewall-ca.crt
      verify_certificate: true       # firewalls must present client certificates from this CA

transforms:
  fw_parse:
    type: remap
    inputs: ["fw_syslog"]
    drop_on_error: true
    drop_on_abort: true
    reroute_dropped: true            # failures go to fw_parse.dropped, not to /dev/null
    source: |
      # Accept exactly: "allow|deny src=<ip> dst=<ip> dport=<n> proto=<name> bytes=<n>"
      p, err = parse_regex(.message, r'^(?P<act>allow|deny) src=(?P<src>[0-9a-fA-F:.]+) dst=(?P<dst>[0-9a-fA-F:.]+) dport=(?P<dport>\d{1,5}) proto=(?P<proto>[a-z]+) bytes=(?P<bytes>\d+)$')
      if err != null { abort }       # unknown shape or action: quarantine, never "deny"
      event_time = timestamp!(.timestamp)            # from the syslog header
      uid = sha2(encode_json([.host, .timestamp, .message]))  # stable id for deduplication
      raw = .message
      . = {}
      .metadata = {
        "version": "1.3.0",          # the lake's limit for custom sources; see the contract
        "uid": uid,
        "processed_time": to_unix_timestamp(now(), unit: "milliseconds"),
        "product": { "name": "example-fw", "vendor_name": "Example" }
      }
      .time = to_unix_timestamp(event_time, unit: "milliseconds")
      .class_uid = 4001              # Network Activity
      .category_uid = 4
      .activity_id = 6               # Traffic
      .type_uid = 400106
      .severity_id = 1
      .action_id = if p.act == "allow" { 1 } else { 2 }   # 1 Allowed, 2 Denied
      .disposition_id = .action_id   # 1 Allowed, 2 Blocked
      .src_endpoint = { "ip": p.src }
      .dst_endpoint = { "ip": p.dst, "port": to_int!(p.dport) }
      .connection_info = { "protocol_name": p.proto }
      .traffic = { "bytes": to_int!(p.bytes) }
      .raw_data = raw                # keep the original line
      .timestamp = event_time        # for the sinks' date templates; not encoded

  fw_route:
    type: route
    inputs: ["fw_parse"]
    route:
      hot: '.action_id == 2 || .dst_endpoint.port == 3389 || .dst_endpoint.port == 22'
      lake: '.action_id == 1'

sinks:
  siem_hot:
    type: http
    inputs: ["fw_route.hot"]
    uri: "https://siem.example.com/ingest/ocsf"
    encoding:
      codec: json
      except_fields: ["timestamp"]
    auth:
      strategy: bearer
      token: "SECRET[local.siem_token]"
    buffer:
      type: disk
      max_size: 10737418240          # 10 GiB rides out a SIEM outage; then back-pressure
      when_full: block
  lake_staging:
    type: aws_s3
    inputs: ["fw_route.lake"]
    region: "eu-west-1"
    bucket: "example-security-lake-staging"
    key_prefix: "staging/example-fw/eventDay=%Y%m%d/"   # UTC day of the event time
    compression: gzip
    encoding:
      codec: json
      except_fields: ["timestamp"]
    buffer:
      type: disk
      max_size: 10737418240
      when_full: block
    # IAM: s3:PutObject on this prefix (plus s3:ListBucket for the healthcheck); no delete
  quarantine:
    type: file
    inputs: ["fw_parse.dropped", "fw_route._unmatched"]
    path: "/var/lib/vector/quarantine/%Y-%m-%d.log"
    encoding:
      codec: json

Four choices make this safe to copy. Strict parsing: VRL’s parse_key_value accepts a bare word as a key by default and to_int(null) returns 0,[52] so a lenient key-value parse would turn the line garbage into a “deny” to port 0; the anchored pattern aborts on anything unexpected, including an unknown action such as drop. Event time: time comes from the syslog header, which Vector replaces with arrival time only when the header does not parse;[44] a device timestamp in the payload, where one exists, should take precedence. The pipeline’s clock goes to metadata.processed_time, and action_id and disposition_id carry the verdict as OCSF enums.[53] An authenticated listener: TLS with client certificates and an origin allow-list, which the firewalls must support; a plaintext port lets anyone who reaches it inject “events”. The token comes from Vector’s secrets backend, which its documentation recommends over environment variables.[54] Durable buffers: the syslog source cannot acknowledge, so disk buffers carry outages; when full, block pushes back on the firewalls, which then buffer or drop by their own rules.[42]

The lake path is staging: Security Lake requires Parquet, one OCSF event class per source and, for custom sources, OCSF 1.3 or earlier when checked,[40] so this stream emits 1.3.0 and a separate, tested job converts each day to Parquet under the eventDay partition. The quarantine file is the point of the design: a parser error becomes something you can count, replay and fix.

Artefact 4: producer configuration. A minimal Sysmon configuration: process creation and DNS queries for all processes, network connections only for the ports investigations ask about, and no image loads, which Sysmon says “should be configured carefully”.[22] Every DNS query is the volume risk: measure it on a pilot group and exclude known-benign processes first.

Example status: structure follows the Sysmon documentation’s examples; not executed.

<!-- schemaversion: use the value that "sysmon -? config" prints for your binary -->
<Sysmon schemaversion="4.82">
  <HashAlgorithms>sha256</HashAlgorithms>
  <EventFiltering>
    <ProcessCreate onmatch="exclude" />
    <DnsQuery onmatch="exclude" />
    <NetworkConnect onmatch="include">
      <DestinationPort>22</DestinationPort>
      <DestinationPort>445</DestinationPort>
      <DestinationPort>3389</DestinationPort>
    </NetworkConnect>
    <ImageLoad onmatch="include" />  <!-- an empty include logs no image loads -->
  </EventFiltering>
</Sysmon>

Artefact 5: lifecycle as policy. Security Lake stores data “indefinitely using the S3 Standard storage class” unless retention is configured per Region.[39] AWS’s example uses ONEZONE_IA, a single-Availability-Zone class AWS recommends for data you can re-create; evidence cannot be, so this policy uses STANDARD_IA, which AWS recommends for an only copy.[55]

Example status: bash syntax-checked; not executed (no AWS account here); placeholders throughout.

# Move lake objects to Standard-IA after 60 days; expire them after 400 days (one Region).
# Not ONEZONE_IA: a single Availability Zone is no home for evidence you cannot re-create.
aws securitylake update-data-lake \
  --configurations '[{"region": "eu-west-1",
    "lifecycleConfiguration": {"expiration": {"days": 400},
      "transitions": [{"days": 60, "storageClass": "STANDARD_IA"}]}}]' \
  --meta-store-manager-role-arn "arn:aws:iam::123456789012:role/AmazonSecurityLakeMetaStoreManagerV2"

Lab 1 (beginner): score a coverage register and find the silent source

Link to section “Lab 1 (beginner): score a coverage register and find the silent source”
  • Objective: turn a source inventory into DeTT&CT-style quality scores[19] and a fix-first list, then check a sample of normalised events for parse rate, field completeness and silence.
  • Prerequisites: Python 3.8 or later, standard library only; 45 minutes. Cost and risk: none (illustrative data).

Steps

  1. Save the register above as coverage.json and the script below as coverage_score.py; run python3 coverage_score.py coverage.json.

Example status: executed on 2026-10-17 with the system Python 3; output reproduced below.

"""Score a log-source coverage register with DeTT&CT-style data quality dimensions (0-5).

Usage: python3 coverage_score.py coverage.json
Dimensions follow the DeTT&CT wiki (device completeness, data field completeness, timeliness,
consistency, retention); the bands below are this Atlas's reading of those 0-5 scales, not DeTT&CT's code.
Standard library only.
"""
import json
import sys


def band(pct):
    """Map a percentage to the DeTT&CT 1-5 completeness bands (0 = unknown / not applicable)."""
    if pct <= 0:
        return 0
    if pct < 26:
        return 1
    if pct < 51:
        return 2
    if pct < 76:
        return 3
    if pct < 100:
        return 4
    return 5


def timeliness(lag_seconds, reporting):
    """5 = available right away, 3 = acceptable, 1 = long delay (DeTT&CT uses 1/3/5)."""
    if not reporting:
        return 0
    if lag_seconds <= 120:
        return 5
    if lag_seconds <= 3600:
        return 3
    return 1


def consistency(mapped_pct, reporting):
    if not reporting:
        return 0
    if mapped_pct >= 100:
        return 5
    if mapped_pct > 50:
        return 3
    return 1


def score(register):
    desired = register["desired_retention_days"]
    rows = []
    for s in register["sources"]:
        reporting = s["devices_reporting"] > 0
        dev = band(100.0 * s["devices_reporting"] / s["devices_expected"]) if s["devices_expected"] else 0
        fld = band(100.0 * s["fields_populated"] / s["required_fields"]) if reporting else 0
        tim = timeliness(s["median_lag_seconds"], reporting)
        con = consistency(s["schema_mapped_fields_pct"], reporting)
        ret = band(100.0 * min(s["retention_days"], desired) / desired) if reporting else 0
        total = dev + fld + tim + con + ret
        rows.append((s["id"], dev, fld, tim, con, ret, total, s["owner"]))
    return rows


def main(path):
    with open(path, encoding="utf-8") as f:
        register = json.load(f)
    rows = score(register)
    print(f"{'source':20} {'dev':>3} {'fld':>3} {'tim':>3} {'con':>3} {'ret':>3} {'sum':>4}  owner")
    for r in rows:
        print(f"{r[0]:20} {r[1]:>3} {r[2]:>3} {r[3]:>3} {r[4]:>3} {r[5]:>3} {r[6]:>4}/25  {r[7]}")
    silent = [r[0] for r in rows if r[1] == 0]
    weakest = sorted(rows, key=lambda r: r[6])[:3]
    print("\nsilent sources:", ", ".join(silent) or "none")
    print("fix first:", ", ".join(f"{r[0]} ({r[6]}/25)" for r in weakest))


if __name__ == "__main__":
    main(sys.argv[1] if len(sys.argv) > 1 else "coverage.json")

Actual output:

source               dev fld tim con ret  sum  owner
win-security           4   5   5   5   5   24/25  endpoint-platform
sysmon                 1   4   5   3   1   14/25  endpoint-platform
entra-signin           5   5   3   5   5   23/25  identity
cloudtrail-mgmt        5   5   3   5   5   23/25  cloud-platform
cloudtrail-s3-data     2   5   3   5   5   20/25  cloud-platform
fw-allow               5   3   5   3   1   17/25  network
dns-resolver           0   0   0   0   0    0/25  network
snowflake-access       5   4   1   3   5   18/25  data-platform

silent sources: dns-resolver
fix first: dns-resolver (0/25), sysmon (14/25), fw-allow (17/25)
  1. Save the script below as silent_source.py. Run python3 silent_source.py --demo to write the twelve sample records to events.jsonl (six Windows authentication events, one of them a parser failure and one without a source IP; five firewall flows, two of them parser failures and one without a destination IP; one DNS query at 07:58), then run python3 silent_source.py events.jsonl. Three of the records look like this:
{"source": "win-security", "time": "2026-10-16T09:00:12Z", "class_uid": 3002, "user": ...}
{"source": "win-security", "time": "2026-10-16T09:02:55Z", ..., "parse_status": "error"}
{"source": "dns-resolver", "time": "2026-10-16T07:58:00Z", "class_uid": 4003, ...}

Example status: executed on 2026-10-17 (--demo, then the check); output reproduced below.

"""Detection-readiness check over a sample of normalised events.

Usage: python3 silent_source.py --demo    (writes the twelve sample records to events.jsonl)
       python3 silent_source.py events.jsonl [--now 2026-10-16T09:05:00Z]
For each source: event count, parse success rate, required-field completeness for the class, and
the silence gap against the source's expected cadence. A record that reached the sample from the
quarantine carries "parse_status": "error", which is this page's convention, not an OCSF attribute
(OCSF's `unmapped` is for source fields that have no place in the class).
Standard library only; illustrative data.
"""
import json
import sys
from datetime import datetime, timezone

# Required fields per OCSF class for THIS organisation's detection contract (not OCSF's own list).
CONTRACT = {
    3002: ["time", "user.name", "src_endpoint.ip", "status_id"],                # Authentication
    4001: ["time", "src_endpoint.ip", "dst_endpoint.ip", "dst_endpoint.port"],  # Network Activity
    4003: ["time", "query.hostname", "src_endpoint.ip"],                        # DNS Activity
}
# Expected maximum silence per source in seconds (from the coverage register).
MAX_SILENCE = {"win-security": 300, "fw-allow": 60, "dns-resolver": 120}


def demo_records():
    """Twelve illustrative records: two Windows and three firewall records are deliberately bad."""
    def auth(t, user, ip, status=1):
        rec = {"class_uid": 3002, "user": {"name": user}, "status_id": status}
        if ip:
            rec["src_endpoint"] = {"ip": ip}
        return ("win-security", t, rec)

    def flow(t, src, dst, port):
        rec = {"class_uid": 4001, "src_endpoint": {"ip": src}, "dst_endpoint": {"port": port}}
        if dst:  # a record without dst_endpoint.ip is an intended completeness failure
            rec["dst_endpoint"]["ip"] = dst
        return ("fw-allow", t, rec)

    def failed(source, t, class_uid):
        rec = {"class_uid": class_uid, "raw_data": "... unparsed", "parse_status": "error"}
        return (source, t, rec)

    rows = [
        auth("09:00:12", "a.khan", "10.1.4.22"),
        auth("09:00:40", "b.lee", "10.1.4.31", status=2),
        auth("09:01:05", "c.ortiz", "10.1.4.40"),
        auth("09:01:30", "d.wu", None),  # legacy-protocol failure without a source IP
        auth("09:02:10", "a.khan", "10.1.4.22"),
        failed("win-security", "09:02:55", 3002),
        flow("08:59:30", "10.1.9.7", "142.250.74.78", 443),
        failed("fw-allow", "08:59:45", 4001),
        flow("09:00:02", "10.1.9.7", None, 53),
        flow("09:00:03", "10.1.9.8", "151.101.1.69", 443),
        failed("fw-allow", "09:00:04", 4001),
        ("dns-resolver", "07:58:00", {"class_uid": 4003,
                                      "query": {"hostname": "updates.example.com"},
                                      "src_endpoint": {"ip": "10.1.4.22"}}),
    ]
    for source, t, rec in rows:
        yield {"source": source, "time": f"2026-10-16T{t}Z", **rec}


def get(ev, dotted):
    cur = ev
    for part in dotted.split("."):
        if not isinstance(cur, dict) or part not in cur:
            return None
        cur = cur[part]
    return cur


def parse_time(s):
    return datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)


def main(path, now):
    per = {}
    with open(path, encoding="utf-8") as f:
        for line in f:
            ev = json.loads(line)
            s = per.setdefault(ev["source"], {"n": 0, "parsed": 0, "complete": 0, "last": None})
            s["n"] += 1
            if ev.get("parse_status") != "error":
                s["parsed"] += 1
                req = CONTRACT.get(ev.get("class_uid"), ["time"])
                if all(get(ev, r) is not None for r in req):
                    s["complete"] += 1
            t = parse_time(ev["time"])
            s["last"] = t if s["last"] is None or t > s["last"] else s["last"]
    print(f"{'source':14} {'events':>6} {'parse%':>7} {'complete%':>9} {'silence_s':>9}  state")
    for name, s in per.items():
        parse_rate = 100.0 * s["parsed"] / s["n"]
        complete = 100.0 * s["complete"] / s["parsed"] if s["parsed"] else 0.0
        silence = int((now - s["last"]).total_seconds())
        state = "SILENT" if silence > MAX_SILENCE.get(name, 3600) else "ok"
        print(f"{name:14} {s['n']:>6} {parse_rate:>7.1f} {complete:>9.1f} {silence:>9}  {state}")


if __name__ == "__main__":
    args = sys.argv[1:]
    if "--demo" in args:
        with open("events.jsonl", "w", encoding="utf-8") as out:
            for rec in demo_records():
                out.write(json.dumps(rec) + "\n")
        print("wrote 12 records to events.jsonl")
        sys.exit(0)
    now = datetime(2026, 10, 16, 9, 5, 0, tzinfo=timezone.utc)
    if "--now" in args:
        now = parse_time(args[args.index("--now") + 1])
    files = [a for a in args if a.endswith(".jsonl")]
    main(files[0] if files else "events.jsonl", now)

Actual output:

source         events  parse% complete% silence_s  state
win-security        6    83.3      80.0       125  ok
fw-allow            5    60.0      66.7       296  SILENT
dns-resolver        1   100.0     100.0      4020  SILENT
  • Expected output: dns-resolver at 0/25; the DNS source silent for over an hour; the firewall’s parse rate at 60%.
  • Validation: set Sysmon’s devices_reporting to 1,200 and re-run step 1: its device score becomes 5. Delete the two firewall failure records from events.jsonl and re-run step 2: the firewall’s parse rate becomes 100.0.
  • Troubleshooting: a KeyError means a register row lacks a field; a ValueError on time means a timestamp is not YYYY-MM-DDTHH:MM:SSZ, which is itself the lesson.
  • Teardown: delete the four files.

Lab 2 (intermediate): run the pipeline and prove nothing is lost

Link to section “Lab 2 (intermediate): run the pipeline and prove nothing is lost”
  • Objective: run the reference Vector configuration on synthetic firewall lines, confirm hot/lake routing and event time, and prove that malformed lines and unknown actions land in quarantine.
  • Prerequisites: a Linux VM where you may install Vector 0.59.0 or later;[51] bash; 90 minutes. Cost and risk: none with file sinks; never point the http sink at a production SIEM.

Steps

  1. Copy vector.yaml to lab.yaml. For the lab only: remove the secret and tls blocks; set address to 127.0.0.1:6514 and permit_origin to ["127.0.0.1/32"]; replace the three sinks with file sinks writing /tmp/lab/hot.log, /tmp/lab/lake.log and /tmp/lab/quarantine.log. Run vector validate lab.yaml. Expected: validation succeeds.
  2. Start vector --config lab.yaml in one terminal. Expected: the syslog source starts listening on 127.0.0.1:6514.
  3. From a second terminal, send four RFC 5424 lines with a fixed header time:
for msg in "allow src=10.1.4.22 dst=142.250.74.78 dport=443 proto=tcp bytes=8812" \
           "deny src=203.0.113.9 dst=10.1.4.22 dport=3389 proto=tcp bytes=0" \
           "garbage" \
           "drop src=203.0.113.9 dst=10.1.4.22 dport=22 proto=tcp bytes=0"; do
  printf '<134>1 2026-10-16T09:00:00.000Z fw1 fw - - - %s\n' "$msg" > /dev/tcp/127.0.0.1/6514
done

Expected: no errors in Vector’s log. 4. Run wc -l /tmp/lab/*.log. Expected: hot.log 1 (the deny), lake.log 1 (the allow), quarantine.log 2 (garbage and the drop line). 5. Run head -1 /tmp/lab/lake.log. Expected: class_uid 4001, action_id 1, raw_data holding the original line, and time equal to 2026-10-16T09:00:00Z in epoch milliseconds (1792141200000), not the time you ran the lab.

  • Validation: the line counts are 1, 1 and 2, and time is earlier than metadata.processed_time.
  • Troubleshooting: a VRL compile error names the line; nothing arriving means permit_origin does not include 127.0.0.1/32; an empty quarantine file means reroute_dropped is not true.
  • Teardown: stop Vector; remove /tmp/lab and lab.yaml.
  • Status: not executed by the author (Vector not installed on the authoring host); YAML and bash syntax-checked.

Lab 3 (advanced): register a custom source in Amazon Security Lake and query coverage

Link to section “Lab 3 (advanced): register a custom source in Amazon Security Lake and query coverage”
  • Objective: convert one day of events to Parquet in Security Lake’s layout, register a custom source, query coverage.
  • Prerequisites: an AWS sandbox with Security Lake enabled and the permissions the custom-source guide lists;[40] a Parquet library; Athena; half a day. Cost and risk: Security Lake, S3, Glue and Athena charges.

Steps

  1. Write one day of OCSF 1.3.0 events as zstd-compressed Parquet to ext/<source>/region=<region>/accountId=external/eventDay=<YYYYMMDD>/, one event class per source, ordered by time.[40] Expected: objects whose eventDay matches each record’s UTC event time.
  2. Check them with AWS’s OCSF validation tool. Expected: no schema errors.
  3. Register the custom source and wait for the Glue crawler. Expected: a new table in the Security Lake Glue database.
  4. Run the coverage query below in Athena. Expected: one row per day under account external, with the record count you wrote.

Example status: SQL not executed (no AWS account here); illustrative names.

-- Events per account and day, last 7 days. Rows under accountId=external share one
-- account, so a gap shows as a missing day; external_<id> names show per-account gaps.
SELECT accountid, eventday, COUNT(*) AS events
FROM amazon_security_lake_glue_db_eu_west_1.amazon_security_lake_table_eu_west_1_ext_example_fw
WHERE eventday >= date_format(current_date - interval '7' day, '%Y%m%d')
GROUP BY accountid, eventday
ORDER BY accountid, eventday;
  • Validation: COUNT(*) equals the records converted.
  • Troubleshooting: no table → check the prefix and the source role’s read access; validation fails → attributes newer than the OCSF version Security Lake accepts for custom sources.[40]
  • Teardown: delete the custom source, the S3 objects and the Glue table.
  • Status: not executed by the author; documented from the AWS user guide.

Three illustrative scenarios built on sourced facts.

1. The mailbox logs you did not pay for. Context: a hosted mail platform; sign-ins collected, mailbox item access not. Method: check the provider’s current defaults first: Microsoft changed them in 2023,[7] and its mailbox-auditing page now marks MailItemsAccessed as audited by default, with new default actions “subject to the user having the appropriate license”.[56] Record the stream as collected, licence-gated or not emitted, with the date checked; price any licence against the detection it enables, remembering that State also needed a custom rule;[6] onboard with a canary that reads a test mailbox daily. Pitfall: treating “we have sign-in logs” as “we have mailbox logs”.

2. Snowflake access history as a security source. Context: UNC5537 stole data from Snowflake customers with infostealer credentials, no MFA and no allow lists, leaving a recognisable SQL sequence (CREATE TEMPORARY STAGE, COPY INTO, GET).[57] Method: onboard ACCESS_HISTORY and QUERY_HISTORY with a custom OCSF mapping and the contract terms noted under the reference implementation; the detection runs on QUERY_HISTORY, whose latency of up to 45 minutes[50] suits exfiltration detection, not session blocking. DataSecOps owns the preventive controls. Success: replaying the sequence in a test warehouse alerts in time.

3. Cutting the firewall bill without blinding the hunt. Context: allowed-flow logs are the largest, least-queried stream. Method: summarise allowed flows per minute for the hot tier, route full flows to the lake for 400 days, replay a week; ASD supports hot and cold tiering[2] and pre-processing at the forwarder.[3] Trade-off: hunts move to slower lake queries. Pitfall: dropping instead of routing; see the judgement call.

Gigabytes are a cost, not a service level; the service levels are the five words of the mission.

KPI Definition / formula Data source Pitfall
Device coverage devices reporting ÷ devices expected, per source Register vs asset inventory Counting agents installed, not events received
Parse success rate events without a parser error ÷ events received Quarantine counts Measuring after the drop
Field completeness events with all contract-required fields ÷ parsed events Contract tests on live samples Using OCSF’s required list instead of your rule’s needs
Freshness (p95 lag) 95th percentile of observed time − event time Pipeline, both timestamps kept Clock skew read as lag
Silent-source detection time last event → alert, per source Health monitor Thresholds guessed, not measured
Cost per stream per day GB/day × tier unit cost (+ per-query cost on tiers that charge it[37]) Billing, pipeline volume Averages hide the stream that matters

Worked calculation (illustrative). The firewall allowed-flow stream is 400 GB/day. Illustrative unit costs: hot tier 1.00 per GB ingested with 90 days included; lake 0.025 per GB-month. Everything hot: 400 × 1.00 = 400 per day, retained 90 days. Proposed: per-minute summaries to hot (about 8 GB/day), 8 per day; raw flows in the lake for 400 days, 400 GB/day × 400 days = 160,000 GB × 0.025 ≈ 4,000 per month ≈ 133 per day. Managed lakes and low-cost tiers often add two lines (Azure Monitor’s Basic and Auxiliary plans bill queries separately[37]): say 0.05 per GB processed at ingestion, 20 per day, and ten 2 TB hunts a month at 0.005 per GB scanned, about 3 per day. Total ≈ 164 per day against 400, with raw retention up from 90 to 400 days. The numbers are placeholders; the method is to price each stream per tier at steady state, ingestion and query charges included. Azure Monitor’s summary rules, which aggregate data as it arrives, are one native form of the per-minute summary (as of October 2026).[37]

  • Integrity. Apply ASD’s transport and storage advice from trust boundaries, including cryptographic verification;[2] a forged event is an attack on the evidence, hence the authenticated listener in the reference pipeline. Security Lake does not support S3 Object Lock (as of October 2026),[39] so an immutable copy needs another mechanism: signed daily checksums kept in another account (RFC 3227 suggests checksums and signatures for evidence[58]), or a copy in a separate bucket under S3 Object Lock compliance mode, which no user can overwrite or delete while it is locked.[59]
  • Time. M-21-31 requires a GPS or NIST-authenticated time baseline, and public NTP pools “shall only be used as an option of last resort”;[9] RFC 3227 asks collectors to “Note the difference between the system clock and UTC”.[58] Keep event time and observed time apart, as the OpenTelemetry log model does.[60]
  • Privacy. Redact in the pipeline with allow-listed keys and blocked patterns. In the OTel Collector’s redaction processor (alpha for logs, October 2026) an empty allowed_keys list allows no attributes, and matches are masked or hashed; the README recommends HMAC over MD5, SHA-1 or SHA-3 for low-entropy values such as IP addresses.[29] Use hmac-sha256 with a managed key: an unkeyed hash of a username is reversed by hashing guesses. Lawful basis belongs to PrivacyOps.
  • Compliance and least privilege. Map each obligation (M-21-31 for US federal agencies,[9] CERT-In’s 180 days within India,[46] sector regimes this page does not enumerate) to a tier setting, and let ComplianceOps collect the evidence. The pipeline’s identities read every log: give them write-only access to sinks and no delete on archives, and audit their activity, as Sentinel’s data lake does by default.[36]

Cost levers in order of leverage: what the producer emits (CloudTrail data events are opt-in and billed[23]); the forwarding format (WEF’s default “Rendered Text” doubles or triples event size, so switch subscriptions to “Events”[25]); where you pre-process;[3] the tier;[37] and retention per tier (Azure Monitor includes 31 days in the ingestion price, so shortening below that saves nothing[61]). Capacity: Microsoft plans roughly 3,000 events per second per WEF collector and points estates above 50,000 events per second at a data lake.[25] Sustainability is mostly storage: Parquet with zstd, which Security Lake prefers for custom sources,[40] stores and scans less than JSON, and expiring what no obligation needs is the greenest option.

Monthly, stop a lab source and time the alert. Quarterly, pick five detections at random and prove their data path end to end. Annually, audit tier settings against the policy and replay a past incident’s timeline to confirm the data would still answer it. Risk indicators: rising quarantine counts, a register unchanged for a quarter, a contract without a canary.

Symptom Likely cause Diagnosis Remediation Prevention
A source goes quiet; nothing alerts Agent update, certificate expiry, audit policy reset, token rotation Last-event time vs cadence; collector’s own log Restore; backfill from the producer’s buffer Health monitor per source[3]
Parse rate drops after a vendor release Log format changed Diff a quarantined event against a fixture Update mapping; re-parse from raw Keep raw; fixtures per format version
Rule fires on test data, not in production Field renamed or empty; Sigma logsource mismatch[48] Contract test on live samples Fix mapping or producer setting Canary before publishing
Investigator hits a 90-day cliff Retention left at the tool default Tier settings vs policy Extend tier or add a lake; the past is gone A retention policy by log class (template)

Worked runbook: the DNS resolver logs went silent

Link to section “Worked runbook: the DNS resolver logs went silent”

Engineering root cause and prevention only: the shift response is SOC Ops’ silent-source runbook, and a rule that stops firing while its data still arrives is DetectionOps’ silent-rule runbook.

Figure 4. Runbook: silent source
noyesnoyesnoyesnoyesAlert: dns-resolver silentfor 12 minutes (threshold 2)Other sources stillarriving?Platform incident: monitor,SIEM or collector downCollector receivinganything from the hosts?Producer side: service, logsetting, disk, agent versionPipeline acceptingand parsing?Pipeline side: quarantinecount, sink errors, certsEvents reach the indexthe monitor reads?Sink, index or monitorquery: fix and re-checkFalse alarm: quiet, notsilent; tune the thresholdRestore and backfillfrom producer buffer if anyDeclare the gap window;hunt it with firewall DNS, EDRPost-incident: threshold,monitor, change-board review
Diagram source (Mermaid)
%%{init: {"flowchart": {"rankSpacing": 24}}}%%
flowchart TB
  A["`Alert: dns-resolver silent
  for 12 minutes (threshold 2)`"]:::risk
  Z{"`Other sources still
  arriving?`"}:::decision
  Y["`Platform incident: monitor,
  SIEM or collector down`"]:::risk
  B{"`Collector receiving
  anything from the hosts?`"}:::decision
  C["`Producer side: service, log
  setting, disk, agent version`"]:::core
  D{"`Pipeline accepting
  and parsing?`"}:::decision
  E["`Pipeline side: quarantine
  count, sink errors, certs`"]:::core
  K{"`Events reach the index
  the monitor reads?`"}:::decision
  L["`Sink, index or monitor
  query: fix and re-check`"]:::core
  M["`False alarm: quiet, not
  silent; tune the threshold`"]:::neutral
  F["`Restore and backfill
  from producer buffer if any`"]:::core
  G["`Declare the gap window;
  hunt it with firewall DNS, EDR`"]:::core
  I["`Post-incident: threshold,
  monitor, change-board review`"]:::neutral
  A --> Z
  Z -- "no" --> Y
  Z -- "yes" --> B
  B -- "no" --> C --> F
  B -- "yes" --> D
  D -- "no" --> E --> F
  D -- "yes" --> K
  K -- "no" --> L --> F
  K -- "yes" --> M --> I
  F --> G --> I
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029

Text equivalent: The alert reports the DNS resolver source silent for 12 minutes against a 2-minute threshold. If no other source arrives either, it is a platform incident. Otherwise ask in turn whether the collector receives events from the resolver hosts (if not, the producer side), whether the pipeline parses them (if not, quarantine, sink errors, certificates) and whether they reach the index the monitor reads (if not, the sink, index or monitor query); if they do, it is a false alarm and the threshold needs tuning. After a real gap: restore and backfill, declare the gap window, hunt it with firewall DNS and EDR data, and review threshold, monitor and change board.

  1. Confirm the silence is real: other sources still arrive, so the monitor itself is healthy.
  2. Split producer from pipeline. Here an operating-system upgrade reset query logging on two hosts and rotated a client certificate on the other two: producer changes that bypassed the change board.[3]
  3. Restore and backfill. Re-enable logging; replay local query logs with their original timestamps.
  4. Declare the gap in the register and to the SOC lead; DetectionOps treats it as a known blind spot.
  5. Hunt the gap with firewall DNS flows and Sysmon DNS events (ID 22[22]) for the highest-risk hosts.
  6. Prevent. Put the logging setting in the configuration baseline and the upgrade on the change calendar; review the threshold; re-run the monthly silence test.
Anti-pattern or myth Why it fails Do instead
“Ingest everything, decide later” Cost scales with volume; over-collection makes effective rules harder[3] A named decision per stream
“Normalised, so drop the raw” Parsers are wrong; OCSF deprecates attributes between releases[30] Raw in the cheapest tier; normalise forward
“The SIEM vendor handles log management” SIEMs search what arrives; coverage and quality are yours Name an owner; run the control plane
“90 days is standard” Long-dwell intrusions outlive it[5] Retention set from dwell-time evidence and obligations
“One schema fits every consumer” Vendor models are not portable; OTel and ECS are still converging[32] One canonical schema, pinned; map others from it

Adopt deliberate SecDataOps when any of these holds: more than a handful of sources; a bill growing faster than the estate; detections blocked on data; an incident where evidence was missing; a retention obligation.

Do not create a function or buy a pipeline product when one SIEM with vendor connectors covers the estate and the bill is not a problem (keep the register and a health query anyway); when the SOC cannot consume better data (fix SOC Ops first); when an MDR (managed detection and response) provider runs detection, whose contract should then carry this page’s coverage, retention and health terms; or when producers do not emit the events at all, which is hardening work for CloudSecOps and EndpointOps. Hidden responsibilities: one more system to run, schema versions forever, an on-call for silent sources.

Figure 5. Tiering decision for one stream
yesnoyesnoyesnoyesnoStream: who uses it,for which decision?Does a detection fireon individual events?Volume too highfor the budget?Trim unused fields, filterknown-benign; copy raw tolakeHot tier, individual events(SIEM analytics)Needed for investigationsor hunts within months?Lake tier (Parquet),summary to hot if usefulRetention obligationor forensic value?Cold archive,integrity-protectedDo not collect; recordthe decision in the register
Diagram source (Mermaid)
flowchart TB
  A["`Stream: who uses it,
  for which decision?`"]:::neutral
  B{"`Does a detection fire
  on individual events?`"}:::decision
  I{"`Volume too high
  for the budget?`"}:::decision
  J["`Trim unused fields, filter
  known-benign; copy raw to lake`"]:::core
  C["`Hot tier, individual events
  (SIEM analytics)`"]:::core
  D{"`Needed for investigations
  or hunts within months?`"}:::decision
  E["`Lake tier (Parquet),
  summary to hot if useful`"]:::core
  F{"`Retention obligation
  or forensic value?`"}:::decision
  G["`Cold archive,
  integrity-protected`"]:::core
  H["`Do not collect; record
  the decision in the register`"]:::risk
  A --> B
  B -- "yes" --> I
  I -- "no" --> C
  I -- "yes" --> J --> C
  B -- "no" --> D
  D -- "yes" --> E
  D -- "no" --> F
  F -- "yes" --> G
  F -- "no" --> H
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029

Text equivalent: Start from who uses the stream and for which decision. If a detection fires on individual events, keep them hot, first trimming unused fields and filtering known-benign events (raw copy to the lake) when volume exceeds the budget. Otherwise use the lake if investigations or hunts need it within months, a cold integrity-protected archive if only an obligation or forensic value remains, and if nothing does, do not collect, and record the decision.

For the platform choice, weigh five criteria with your own weights: steady-state cost including query charges; openness of format and schema; operational burden; detection latency (a lake alone does not alert); and migration risk. The architecture table summarises the trade-offs.

This is the Atlas’s model, in the spirit of M-21-31’s maturity tiers[9] but not a reproduction and not an official standard.

Level Observable behaviour Exit criteria
1 Reactive Sources added after incidents; nobody owns the list; retention is the tool default A register with an owner per source
2 Inventoried Register exists; onboarding ad hoc; silence noticed by analysts Health monitor on critical sources; retention policy written
3 Contracted Contracts, fixtures and canaries for critical sources; tiering policy applied Coverage scored against ATT&CK data components; cost per stream known
4 Measured Quality and cost metrics reviewed on cadence; pre-processing policy in force Replay testing after pipeline changes; schema versions pinned
5 Optimising Utility drives tiering; producer changes flow through the change board A practice to keep, not a destination
  • Days 1–30 (see). Build the register from the SIEM’s source list, the asset inventory and the cloud accounts; score every source with the Lab 1 method; draft the retention policy; agree one canonical schema. Quick win: a health monitor on the ten most critical sources.
  • Days 31–60 (contract). Contracts, fixtures, CI and canaries for those ten; price each stream per tier.
  • Days 61–90 (tier). Re-tier the three largest streams with replay checks; set lake lifecycle rules; run the first monthly silence test; report coverage, completeness, freshness and cost per stream as the baseline.

Dependencies: a detection lead who states field needs, platform owners who will change audit settings, a budget owner.

Quick start

  • Every source in a register with an owner, a criticality and a tier
  • One canonical schema chosen; version pinned per source
  • Health monitor with a silence threshold on every high-criticality source
  • Raw events kept in the cheapest tier for every normalised stream

Onboarding preflight

  • Consumer named the decision, fields and freshness target
  • Producer emits the fields (audit policy, data events, licence tier checked)
  • Contract written; fixtures from real samples; parse-rate and completeness tests pass
  • Timestamps in UTC with zone designator; event time and observed time both kept
  • Tier, retention and cost per day recorded; redaction applied where needed
  • Canary fires on live events; register updated

Routine review (monthly) and governance audit (quarterly)

  • Silence test on a lab source; detection time recorded against the runbook
  • Quarantine counts and cost per stream compared with last month
  • Tier retention matches policy; log-store access reviewed; archive deletes audited
  • Coverage scored against ATT&CK data components; gaps prioritised with the detection lead
  • Chain-of-custody steps for exporting evidence documented[58]

Source onboarding request (intake form)

Source name:              (system, vendor, version)
Requested by / consumer:  (team; the decision this data informs)
Detections or questions:  (rule names, investigation questions, obligations)
Required fields:          (schema field names; which are mandatory for the use)
Freshness needed:         (p95 seconds; business hours only?)
Expected volume:          (events/s, GB/day; growth)
Producer owner:           (team; change process for audit settings)
Sensitive content:        (personal data, secrets; redaction needs)
Retention need:           (days; obligation or investigative; jurisdiction)
Proposed tier:            (hot / lake / cold; summary to hot?)
Cost estimate per day:    (by tier; who pays)
Canary:                   (how we will prove it works on live events)

Retention policy, one page. Periods count from ingestion and run at the same time: a 90-day hot copy mirrored into a 400-day lake keeps data for 400 days, not 490.

Log class Examples Hot (days) Lake (days) Cold (days) Basis
Identity, privileged access, cloud control plane Sign-ins, role changes, management events 90 400 548 Investigation need; dwell-time evidence;[5] obligation
Endpoint process and DNS 4688, Sysmon 1/22 30 400 0 Investigation need
Network allowed flows Firewall allow, VPC flow logs summary only 400 0 Hunting; volume

ADR stub for a tiering change

ADR-00N: Route <stream> raw events to the lake; keep per-minute summaries hot
Status: proposed | accepted | superseded
Context: volume, cost per day today, detections and hunts that use the stream (list)
Decision: tiers, retention per tier, pre-processing rule, schema version
Consequences: query latency for hunts; summary job to maintain; expected cost
Validation: replay of <N> days through the new pipeline; detections that must still fire
Rollback: restore the previous route; raw data still in the lake, so no evidence is lost
Owner / date / review date

Source-health runbook card

Source: <id>        Owner: <team>        Criticality: <0-3>
Expected cadence: <events per minute; business hours / always>
Silence threshold: <seconds>   Page: <who>   Inform: SOC lead
First checks: collector connection; producer service; certificate expiry; quarantine count
Backfill: <where the producer buffers; how long; replay command>
Alternative sources for the gap: <list>
Option Trade-off summary
Ingest-time vs query-time normalisation Ingest-time queries faster; query-time preserves the source format and fixes apply retroactively[20]
Pipeline product vs collector-native Products add routing, reduction, redaction and a UI;[8] collector-native is free and testable as code but costs engineering time
OCSF vs ECS/OTel vs vendor models OCSF is Linux Foundation-governed with a Security Lake ecosystem;[47] ECS/OTel suit observability-first estates;[32] vendor models are least portable

Is SecDataOps a real discipline or a marketing term? Both. A few vendors and writers use the name, with differing definitions (origins). The work it names is documented by NIST, OMB, ASD and CISA as log management and event logging.[1][9][2] Use the practice; be careful with the label.

Which schema should we pick? The one your main consumers query in: ASIM on Sentinel, CIM on Splunk, UDM on Google SecOps; OCSF for an open lake; ECS/OTel if the estate is observability-first. One canonical schema, pinned per source.

How long should we keep logs? Long enough for your investigations and obligations. Start from the obligations in the methods table and ASD’s starting point there, then test the result against the dwell times under the problem.

Our SIEM bill is the problem; is a pipeline product the answer? Often part of it: Forrester notes the immediate value is reducing log sizes and ingest costs.[8] Do the register and contracts first; buy if engineering time is the constraint.

How do we know a source is silent before an analyst does? A health monitor comparing last-event time with a per-source cadence threshold, tested monthly by stopping a lab source; ASD explicitly asks for such processes.[3]

  • Schema convergence. ECS is merging into OTel semantic conventions[32] while OCSF, a Linux Foundation project,[47] releases several times a year;[30] mapping between them will be a recurring task.
  • Detection strategies as data requirements. ATT&CK v18’s detection strategies, analytics and data components give DetectionOps and SecDataOps one vocabulary for “what data would this detection need”.[17]
  • Default audit logging from cloud providers. The CSRB recommended free access logs with six months’ retention,[6] and Microsoft extended email-access logging to its standard tier in 2023.[7] Providers that have not moved still gate sources by licence, so registers need a licence-gated state that is re-checked.
  • AI on security data. Vendors position clean data as the prerequisite;[4] evaluating the AI belongs to AIOps for Security.
Stage Do Outcome
Prerequisites SOC Ops and the DataOps glossary; one query language; JSON You know what consumers need
1-day primer The ASD-led event logging guidance[2] and “Understanding OCSF”;[21] Lab 1 You can score a register and explain a schema
1-week practice Lab 2 on your samples; a contract for one real source; a silence monitor A contracted, monitored source
30-day progression Three sources with fixtures and canaries; a tiering proposal with a replay check A control plane in miniature
Advanced Lab 3; DeTT&CT coverage against your threat model;[18] a schema upgrade with regression tests You can run Day 2
Portfolio ideas A public OCSF mapping with fixtures; a silence-monitor tool Evidence of the craft
  • Decide: one canonical schema; retention by log class; the first three streams to re-tier; the register’s owner.
  • Measure: coverage, parse rate, completeness, freshness p95, silent-source detection time, retention compliance, cost per stream.
  • Next actions: run Lab 1 on your real estate this week; stop a lab source and time the alert.

A judgement call: drop the firewall allow logs, or tier them?

Link to section “A judgement call: drop the firewall allow logs, or tier them?”

Assumptions. Ingest-priced SIEM; a lake can be enabled within the quarter; the analysts’ “sometimes” cannot be quantified because the SIEM’s query audit log was never retained (a finding in itself). For a US federal agency the choice is already made: M-21-31 lists firewall permit events among required logs, so Option A is not available.[9]

Decision process. Utility first: no rule fires on individual allowed flows, so the hot tier does not need them. But an exfiltration path is by definition an allowed path (reasoning, not a measured statistic), so the decision framework sends the stream to the lake. Cost second: the worked calculation prices the lake path at well under half the hot path while retaining more. Reversibility third: Option A destroys evidence that cannot be recreated; Option B is undone by promoting from the lake.

Why the attractive choice is wrong. Option A delivers the exact number asked for, this quarter, with no new system. Its cost stays invisible until an investigation needs flows that were never stored; ASD’s framing, that retention must accommodate the organisation’s mean time to detect threats,[3] applies.

Recommendation. Option B, if a one-week replay shows no detection regresses and a hunt over the lake answers an exfiltration question within an agreed time. If the lake cannot be enabled this quarter, summarise now and route raw flows to cold object storage rather than dropping them. Uncertainty: real usage is unknown, so turn on SIEM query auditing first.

After this page you should be able to explain why the label is emerging and which guidance the practice rests on; build and score a coverage register; design onboarding with contracts, fixtures, canaries and a health monitor; decide a stream’s tier and retention; and diagnose a silent source or a parse-rate drop and prevent its return.

1. A detection engineer says a brute-force rule “works in testing but never fires in production”. Which SecDataOps checks come first?

Contract completeness on live samples (is src_endpoint.ip or status_id empty in production?), the Sigma logsource mapping (a mismatch “can render the detection ineffective”[48]), device coverage, and the canary. Only then is it a rule problem.

2. Your register shows Sysmon on 310 of 1,200 endpoints with 90 days’ retention. What is its device completeness score, and what is the first fix?

25.8%, below the Lab 1 script’s 26% boundary, so band 1 (rounding first would give band 2: pick a rule and keep it). The first fix is deployment coverage, not retention: a rule that sees a quarter of the fleet is a quarter of a rule.

3. A platform team proposes to sample 1 in 10 Windows process-creation events to cut cost. What do you say?

No: rules and investigations depend on individual process-creation events. Offer the other levers (trim unused fields, route full events to the lake, filter known-benign processes in the Sysmon configuration) and replay a week to prove detections still fire.

4. The CSRB recommended free access logs with six months’ retention, and one provider changed its defaults in 2023. How should your register treat licence-gated sources?

Give them an explicit state (collected, licence-gated or not emitted), the licence that unlocks them and the date checked, and re-check when a provider announces new defaults, as Microsoft did for email-access logs in 2023.[7] Collecting is not detecting: State also needed a custom rule on the log.[6]

5. You normalise at ingest into OCSF tables and delete the raw. Six months later OCSF deprecates an attribute your mapping used. What went wrong?

Without raw events you cannot re-parse; old records keep a deprecated shape forever. Keep raw in the cheapest tier, pin the schema version in the contract, and re-parse forward on upgrade.[20]

Before anyone can detect, hunt, investigate or prove, someone must have made sure the evidence exists, is parsed, arrives on time, can be trusted and can be paid for. Governments have written that duty down for years as log management; vendors have lately called it SecDataOps. The name may not last. The work will, because every other security function inherits its failures.

The craft reduces to habits that are easy to state and hard to keep: treat each source as a product with a contract; parse strictly and keep the raw; tier by use, with retention set by how long intrusions actually last; notice silence before an adversary benefits from it; and measure the data alongside the bill. Do those, and the cost of the SIEM becomes a set of decisions rather than a surprise.

All sources were opened on 2026-10-17; the evidence ledger (research/secdataops-ledger.md) maps claims to sources, records the archive copies used and lists what could not be fetched.

Further reading: Query’s Phase 1 discovery guide (Neal Bridges, 2025-07-01), an interview-led assessment method.

Changelog.

  • 2026-10-17: first edition. Term status confirmed as emerging after opening the vendor and author sources; Lab 1 scripts executed locally; Vector, Sysmon, AWS CLI and Athena examples syntax-checked or documented but not executed.
  • 2026-10-17 (r1 revision): after an independent review, the reference pipeline parses strictly, keeps event time, listens on authenticated TLS and emits OCSF 1.3.0 for the lake; M-21-31 retention scope, the CSRB case and Microsoft’s 2023 logging change corrected; enrichment and delivery guarantees added; acronyms defined; Lab 1 fixture made reproducible; three diagrams fixed; restatements cut to keep the body within the length band.
  • 2026-10-17, post-score polish (reviews/secdataops-polish.md): AU-11 and M-21-31 quotations made verbatim; zstd wording, Entra lag, Lab 3 query and lifecycle storage class corrected; integrity options, “as of” dates and acronyms added; FAQ repeats trimmed; figures 2 and 3 uncrossed, 3 and 4 shortened; Lab 1 re-executed with unchanged output.
  • 2026-10-17, polish check (reviews/secdataops-polish-check.md): DeTT&CT credited to its authors and sponsor.

  1. Cybersecurity Log Management Planning Guide, SP 800-92 Rev. 1 (initial public draft) — NIST (K. Scarfone, M. Souppaya), 2023-10-11. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  2. Best practices for event logging and threat detection (PDF) — ASD’s ACSC with CISA, FBI, NSA and partners, 2024-08. Checked 2026-10-17 (archive copy). ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  3. Implementing SIEM and SOAR platforms: practitioner guidance (PDF) — ASD’s ACSC with international partners, 2025-05. Checked 2026-10-17 (archive copy). ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  4. Security Data Operations Assessment & Strategy Guide (PDF) — Query, 2026 (undated document; URL path February 2026). Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  5. M-Trends 2026: Data, Insights, and Strategies From the Frontlines — Google Cloud blog (Mandiant, J. Kutscher), 2026-03-24. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  6. Review of the Summer 2023 Microsoft Exchange Online Intrusion (PDF) — Cyber Safety Review Board (CISA/DHS), 2024-03-20. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  7. Expanding cloud logging to give customers deeper security visibility — Microsoft Security blog (Vasu Jakkal), 2023-07-19. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  8. If You’re Not Using Data Pipeline Management For Security And IT, You Need To — Forrester (A. Mellen et al.), 2024-11-12. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  9. M-21-31: Improving the Federal Government’s Investigative and Remediation Capabilities Related to Cybersecurity Incidents (PDF) — US Office of Management and Budget, 2021-08-27. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  10. Introducing The Complete Guide to SecDataOps and Vulnerability Management on AWS — Security Boulevard (Jonathan Rau), 2023-03-07. Checked 2026-10-17. ↩︎

  11. Tenzir Platform Goes GA at Black Hat 2024 — Tenzir press release, 2024-08-06. Checked 2026-10-17. ↩︎

  12. The SecDataOps evolution: why security data needs its own pipeline — Tenzir (Matthias Vallentin), 2025-02-04. Checked 2026-10-17. ↩︎

  13. SecDataOps Cast — About — Query.AI Inc., podcast since 2024. Checked 2026-10-17. ↩︎

  14. The Security Data Operations Workshop: A Roadmap for Phase 2 – Analysis & Strategy — Query (Neal Bridges), 2025-07-10. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  15. Authentication (3002) event class — OCSF schema browser, schema 1.9.0. Checked 2026-10-17. ↩︎ ↩︎

  16. Data Components — MITRE ATT&CK, content v20.0. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  17. Updates – October 2025 (ATT&CK v18) — MITRE ATT&CK, 2025-10-28. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  18. DeTT&CT: Detect Tactics, Techniques & Combat Threats — project README, rabobank-cdc on GitHub, v2.2.0, GPL-3.0. Checked 2026-10-17. ↩︎ ↩︎

  19. Data quality scoring — DeTT&CT wiki, undated. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  20. Normalization and the Advanced Security Information Model (ASIM) — Microsoft Learn, updated 2026-09-10. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  21. Understanding the Open Cybersecurity Schema Framework — OCSF (P. Agbabian), document v1.17, April 2026. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  22. Sysmon — Microsoft Learn, Sysinternals (M. Russinovich, T. Garnier), 2026-09-10. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  23. Logging data events — AWS CloudTrail User Guide, undated. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  24. ACCESS_HISTORY view — Snowflake documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  25. Use Windows Event Forwarding to help with intrusion detection — Microsoft Learn, updated 2025-08-18. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  26. Data pipeline — Fluent Bit documentation, undated. Checked 2026-10-17. ↩︎

  27. Remap with VRL — Vector documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  28. Route transform — Vector documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  29. Redaction processor README — OpenTelemetry Collector Contrib on GitHub, main branch. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  30. Releases, ocsf/ocsf-schema — OCSF on GitHub, latest 1.9.0 of 2026-08-03. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  31. Elastic Common Schema (ECS) reference — Elastic, version 9.5.0. Checked 2026-10-17. ↩︎

  32. Announcing the Elastic Common Schema (ECS) and OpenTelemetry Semantic Convention Convergence — OpenTelemetry blog (Reiley Yang), 2023-04-17. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  33. Overview of the Splunk Common Information Model — Splunk Help, CIM 5.2. Checked 2026-10-17. ↩︎

  34. UDM overview — Google Security Operations documentation, undated. Checked 2026-10-17. ↩︎

  35. What is Amazon Security Lake? — AWS documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  36. Microsoft Sentinel data lake overview — Microsoft Learn, updated 2025-08-11. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  37. Azure Monitor Logs overview (table plans) — Microsoft Learn, updated 2026-09-02. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  38. Reduce Costs (use case) — Cribl documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  39. Lifecycle management in Security Lake — AWS documentation, undated. Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  40. Collecting data from custom sources in Security Lake — AWS documentation, undated (OCSF 1.3 limit as of the check). Checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  41. Enrichment object, schema 1.3.0 — OCSF schema browser. Checked 2026-10-17. ↩︎

  42. Buffering model — Vector documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  43. End-to-end acknowledgements — Vector documentation, undated. Checked 2026-10-17. ↩︎

  44. Syslog source — Vector documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  45. NIST SP 800-53 Rev. 5, AU-11 Audit Record Retention — Rev. 5.2.0 control text as rendered by csf.tools, undated. Checked 2026-10-17. ↩︎

  46. Directions under section 70B(6) of the Information Technology Act, 2000 (PDF) — CERT-In, 2022-04-28. Checked 2026-10-17. ↩︎ ↩︎

  47. Open Cybersecurity Schema Framework — OCSF (Linux Foundation project since 2024-11-19), undated. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  48. Log Sources — SigmaHQ documentation, undated. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  49. Logging Made Easy (LME) — CISA on GitHub; support retired and repository archived 2026-05-22. Checked 2026-10-17. ↩︎

  50. QUERY_HISTORY view — Snowflake documentation, undated. Checked 2026-10-17. ↩︎ ↩︎

  51. Vector releases — Vector, latest 0.59.0 of 2026-10-06. Checked 2026-10-17. ↩︎ ↩︎

  52. VRL function reference — Vector documentation, undated. Checked 2026-10-17. ↩︎

  53. Network Activity (4001) event class, schema 1.3.0 — OCSF schema browser. Checked 2026-10-17. ↩︎

  54. Secrets management — Vector documentation, undated. Checked 2026-10-17. ↩︎

  55. Understanding and managing Amazon S3 storage classes — Amazon S3 User Guide, undated. Checked 2026-10-17. ↩︎

  56. Manage mailbox auditing — Microsoft Learn, updated 2026-06-19. Checked 2026-10-17. ↩︎

  57. UNC5537 Targets Snowflake Customer Instances for Data Theft and Extortion — Google Cloud blog (Mandiant), 2024-06-10, updated 2024-06-17. Checked 2026-10-17. ↩︎

  58. RFC 3227: Guidelines for Evidence Collection and Archiving (BCP 55) — IETF, 2002-02. Checked 2026-10-17. ↩︎ ↩︎ ↩︎

  59. Locking objects with Object Lock — Amazon S3 User Guide, undated. Checked 2026-10-17. ↩︎

  60. Logs Data Model — OpenTelemetry specification, status Stable. Checked 2026-10-17. ↩︎

  61. Manage data retention in a Log Analytics workspace — Microsoft Learn, updated 2026-09-02. Checked 2026-10-17. ↩︎

Related Ops

DataSecOps

DataSecOps is a vendor-promoted label for building security into data platform operations: classifying data, enforcing who may see which rows and columns, masking or tokenising sensitive fields, and proving it from access logs.

DetectionOps

DetectionOps is a rare label for detection engineering run like software delivery: detections are written as code from threat-informed requirements, tested, peer-reviewed, deployed through a pipeline, measured in production, tuned and retired.

SOC Ops

SOC Ops is running a security operations centre as a dependable service: turning alerts into timely, recorded decisions through tiers, playbooks, SIEM and SOAR, shift handover, metrics and sourcing choices that people can sustain around the clock.

DataOps

DataOps is the practice of delivering and running data pipelines with software discipline: changes are versioned and tested in CI, each load is checked before consumers see it, and data is monitored after, so correct data arrives on time. The label is real but not standardised.

ObservabilityOps

ObservabilityOps is this Atlas's name for running observability as an internal product: the telemetry pipeline, standards, budgets and alert hygiene that let engineers answer new questions about production at a predictable cost. The label itself is not standardised.

ResponseOps

In this Atlas, ResponseOps is security incident response run as an operational capability: contain an active adversary, preserve evidence, evict and recover cleanly, and meet notification duties. The label itself is not a standard term.

ThreatOps

ThreatOps is an unstandardised label. This page uses it for running threat intelligence and threat hunting as one operation: priority intelligence requirements steer what is collected and analysed, and testable hypotheses steer hunts whose findings change defences.

AIOps for Security

AIOps for Security is the use of statistics, machine learning and language models inside the detect-triage-respond loop of a security operation, evaluated and governed against an adversary: base rates, missed detections, prompt injection and limits on autonomy.

Practise and get certified

From xOps School

Practise SecDataOps and get certified

The free SecDataOps track on xOps School has study notes, a 25-question practice quiz with explanations, and a verifiable certificate when you score 70% or more. Sign in with Google to start.

For teams

Instructor-led training

Live batches for your team, with completion certificates and reports for L&D/HR.

Source: master.md (the editorial source of this page) · built 2026-10-17 · diagrams rendered with @mermaid-js/mermaid-cli 11.17.0 (mermaid 11.17.2)