The Ops Atlas xOps School

Security & Compliance Established term Sources verified

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.

Key takeaways

  • A SOC is a team that turns security alerts into decisions. SOC Ops is running that team as a service: who looks at what, how fast, with what authority, and how the next shift knows what happened.
  • The alert queue behaves like any queue: arrival rate, handling time and staff set the wait. MITRE estimates one 24x7 seat at roughly five full-time people and advises two analysts on shift at all times.
  • Never hand analysts an unfiltered feed. Record a disposition for every alert (true, benign, false or undetermined) and send the noise back to detection engineering as data.
  • Report medians and percentiles of time to triage by shift and severity, alert quality per rule and escalation quality, and pair every speed target with a quality check.
  • Automate enrichment and routine closure with an owner and an expiry date; automate containment only for proven rules on the least critical assets (asset tier 3), with a rate limit, and keep critical assets behind a human decision.
  • Outsourcing moves execution, not accountability. An MDR contract must say what the provider may do on its own, what it escalates, and how fast.
  • At Target, alerts reportedly fired but nobody acted on them; at the State Department, a SOC that had paid for the right audit log built a custom rule and investigated it despite past false positives. Data made detection possible; operations made it happen.

SOC Ops is the work of running a security operations centre (SOC) as a dependable service. A SOC receives a stream of security alerts from tools such as a security information and event management (SIEM) platform and endpoint detection and response (EDR) agents. Analysts decide, alert by alert, whether something bad is happening, and either close the alert with a recorded reason or escalate it so that responders can act. Running that well means deciding who looks at which alerts, how quickly, with what authority to act, what happens at 03:00 and at shift change, how performance is measured, and which parts are bought from a provider.

MITRE’s handbook on the subject defines the SOC as “a team, primarily composed of cybersecurity specialists, organized to prevent, detect, analyze, respond to, and report on cybersecurity incidents”[1]. This page is about the operating side of that team: the queue, the people, the shifts, the numbers and the contracts.

It is written for SOC analysts and shift leads, SOC managers, CISOs who fund or oversee a SOC, and anyone buying a managed detection and response (MDR) service. Beginners can read the first two parts and the cheat sheet; practitioners will find playbook, staffing and metrics material in the middle parts; leaders should go to Deciding and adopting.

Question Answer
Goal Every alert that reaches analysts is triaged within an agreed time, decided correctly, recorded, and escalated with enough context to act on
Who it is for SOC analysts, shift leads, SOC managers, CISOs, MDR buyers and providers
In scope SOC mission and authority, tiers and roles, triage and escalation, SIEM and SOAR (security orchestration, automation and response) in daily use, playbooks, shift handover, SOC metrics, staffing and 24x7 coverage, build versus buy (managed security services provider, MSSP; managed detection and response, MDR), analyst workload and burnout, maturity assessment
Out of scope Writing detections (DetectionOps), the incident response lifecycle (ResponseOps), intelligence and hunting (ThreatOps), telemetry pipelines (SecDataOps), the security–IT operations contract (SecOps)
Prerequisites Basic networking and operating systems; what an alert, a log and an incident are; SecOps helps
Key outputs SOC charter, service catalogue, triage playbooks, priority and escalation matrices, pass-down log, metrics pack, MDR contract terms
Term status established function; the compact label “SOC Ops” is informal

Detection tools are cheap to switch on and expensive to listen to. An alert is only a question: is this activity malicious? If nobody answers it in time, or answers it carelessly, the tool might as well not exist.

Two public cases show what operations adds to tools. A 2014 US Senate committee majority staff report on the 2013 Target breach, built from press reports and expert analyses, found that Target “appears to have failed to respond to multiple automated warnings from the company’s anti-intrusion software that the attackers were installing malware on Target’s system”[7]. The report also suggested that staff “could have viewed this alert as a false positive if the system was frequently alarming”. In 2023 the US State Department, which had paid for a premium mailbox audit log, built a custom rule on it and investigated its alerts even though the rule had produced false positives before; it became the first victim to detect the Storm-0558 intrusion into Microsoft-hosted mailboxes[8]. The review board found that both the log and the analysis were needed (both cases are in Use cases and scenarios).

Without deliberate SOC operations, the symptoms are familiar: a queue analysts scroll rather than work; alerts closed as “false positive” with no reason; unwatched nights and weekends; escalations without context; a provider contract nobody can check; and analysts who leave (MITRE notes they pick up “highly marketable IT skills in just 12–18 months”[1]). NCSC’s guidance warns that pigeonholing analysts “often leads to issues, like triage fatigue, poor job satisfaction and therefore poor security”[3].

  • Not detection engineering. Writing, testing and retiring detection rules is DetectionOps. SOC Ops consumes detections and returns dispositions and tuning requests.
  • Not the incident response lifecycle. Containment, eradication, recovery and forensics for confirmed incidents belong to ResponseOps; incident command and MTTx definitions for any incident belong to IncidentOps. SOC Ops owns the triage decision, including whether an alert appears to meet the written incident criteria, and the hand-off; the IR lead records the declaration (an Atlas proposal shared with ResponseOps, see Roles, tiers and RACI).
  • Not threat intelligence or hunting (ThreatOps), and not the telemetry pipeline (SecDataOps).
  • Not “SecOps” in the collaboration sense. SecOps is the contract between security and IT operations; some people use “SecOps” to mean the SOC itself. Microsoft’s Cloud Adoption Framework, for example, names a role group “Security operations (SecOps/SOC)”[9].
  • Not a NOC. MITRE distinguishes the SOC, “primarily looking for cyber attacks”, from a network operations centre that operates and maintains equipment[1].
  • Not SOC 2. “SOC” also stands for System and Organization Controls, the AICPA’s suite of assurance reports on a service organisation’s controls[10]. A SOC 2 report says nothing about whether you run a security operations centre.
  • Not physical security monitoring, which some organisations also call a SOC; MITRE puts “Cyber” in its book title for that reason[1].

MITRE dates the rise of SOCs to the 1990s[1]; ENISA dates the term CSIRT (computer security incident response team) to the same decade and calls CERT “the oldest term”[2]. MITRE lists seven names for this kind of team (CSIRT, CIRT, CIRC, CSIRC, SOC, CSOC, CERT) and observes that “There is no one universally agreed-to term for this set of cybersecurity specialists”[1]. The published body of SOC practice is younger:

Year Milestone
2014 MITRE publishes Ten Strategies of a World-Class Cybersecurity Operations Center (Carson Zimmerman)[1]
2016 SOC-CMM, an open SOC capability and maturity model, is launched from a master’s thesis[11]
2017 FIRST publishes the first version of its CSIRT Services Framework[4]
2020 ENISA publishes How to set up CSIRT and SOC[2]
2022 MITRE’s second edition, 11 Strategies (Knerler, Parker, Zimmerman); NCSC publishes Building a SOC[1][3]
2024 NIST CSF 2.0 frames detection as adverse-event analysis with explicit incident declaration (DE.AE-08)[12]
2025 NIST SP 800-61 Rev. 3 recommends SIEM and SOAR for continuous monitoring[13]; ASD’s ACSC, CISA and partners publish SIEM and SOAR implementation guidance[14][15]

Two shifts run through this history. The estate spread out: MITRE’s second edition covers on-premises, remote, operational technology (OT), cloud and mobile assets[1]. And automation moved the bottleneck: some SOCs claimed SIEM analytics and SOAR made tier 1 unnecessary, but in MITRE’s reading they “shrunk, but not eliminated” the triage pool[1].

Term Meaning on this page
Event / alert Something observed in a log or sensor (most are routine) / an event or group of events that a detection flags for review
Incident Activity that meets your written incident criteria. NIST CSF 2.0: “Incidents are declared when adverse events meet the defined incident criteria” (DE.AE-08)[12]; CIS Safeguard 17.9 asks you to differentiate “between an incident and an event”[16]
Triage Deciding quickly what an alert is, how urgent it is and who handles it next
Disposition The recorded outcome of triage. This page uses four values: true positive (malicious or policy-violating), benign positive (the detection worked, but the activity is legitimate), false positive (the detection logic or data was wrong), undetermined. DetectionOps uses the same four values and reports undetermined alerts separately
Escalation Inside the SOC: passing an alert or case to tier 2 or the shift lead, with the evidence so far. Calling owners or on-call staff is escalation outside the SOC
Hand-off and declaration Passing a case that appears to meet the incident criteria to the IR lead (ResponseOps), who records the declaration
Case The record that holds an alert or group of alerts, notes, timestamps and the final disposition
Constituency MITRE’s word for the users, sites, assets, data and organisations a SOC serves[1]
Tier 1 / tier 2 / tier 3 (analysts) Front-line triage; in-depth investigation; specialists such as hunters or malware analysts. Microsoft’s tier 1 analysts “follow documented procedures to rapidly resolve them (or escalate them to investigation analysts as appropriate)”[9]. A bare “tier” on this page always means an analyst tier
Asset tier 1 / 2 / 3 A separate scale for the criticality of assets and identities, always written “asset tier”: 1 = crown jewels and admin accounts, 2 = servers and key SaaS, 3 = user devices and standard accounts (priority matrix). Asset tier 1 is the most critical; automation is confined to asset tier 3
SIEM A platform “that collects, centralises, and analyses log data from sources within a network or system for the purpose of cyber security” (ASD and partners)[14]
SOAR A platform “that automates the response to anomalous activity detected on a network” by running playbooks; ASD adds that “These automated actions do not replace human incident responders”[14]
EDR / XDR Endpoint detection and response; extended detection and response, a market label for suites that apply the same detect-and-respond model across endpoint, identity, email and cloud telemetry
Playbook / SOP Written steps for a type of alert or incident; in a SOAR, partly executable
Pass-down log The written shift handover record[1]
SLO / SLA MITRE: an SLO is an objective the SOC aims for; an SLA is a measure it is held to by agreement or contract[1]
MSSP / MDR Providers that run security services for you; NIST’s example is “outsourcing a security operations center [SOC] to a managed security services provider [MSSP]”[13]. Working definition used here: MDR focuses on detection, investigation and response on your telemetry, usually including remote containment
Dwell time Time from an adversary’s entry to detection[1]

The Microsoft Sentinel closing classifications use the same four ideas (“True Positive: suspicious activity”, “Benign Positive: suspicious but expected”, “False Positive: incorrect alert logic” or “incorrect data”, “Undetermined”)[17], while the Defender portal uses “True positive”, “Informational, expected activity” and “False positive”[18]. Map your tool’s values to one vocabulary before you measure anything.

Mental model: a queue with a feedback loop. Think of the SOC as a service desk for questions that machines cannot answer alone. Alerts arrive at some rate; each takes analyst time to handle; staff on shift determine how long alerts wait: queueing arithmetic, not heroics, sets the time to triage. Every disposition is also data. A well-run SOC runs two loops: the fast loop (triage, escalate, close) and the slow loop (dispositions → tuning → better alerts). A SOC that only runs the fast loop gets busier every month.

Why “benign positive” matters: in interviews with 21 practitioners from seven SOCs, Alahmadi, Axon and Martinovic found that, “despite the perceived volume of FPs, most are attributed to benign triggers—true alarms, explained by legitimate behavior in the organization’s environment”[19]. Lumping these with broken detections hides the fix. A benign positive usually needs context (an allow-list, an asset tag, a scoped suppression); a false positive needs a detection fix.

Page Shared scope What SOC Ops hands over or receives
SecOps The word “SecOps” is used for both Receives from operations: logs, agent health, change calendar, pre-agreed containment authority
SecDataOps Log sources and their quality Hands over: silent sources, parsing gaps, enrichment needs
DetectionOps Alert quality Hands over: dispositions per rule and tuning requests; receives tested detections with triage notes
ThreatOps Context during triage Receives intelligence and hunt findings; hands over observations worth hunting
ResponseOps The alert-to-incident boundary Hands over: cases that meet the incident criteria, with an escalation package; the IR lead declares
IncidentOps Incident command, MTTx caveats Uses its definitions and caveats for time metrics
VulnerabilityOps Exploited weaknesses seen in alerts Hands over: exposures that alerts reveal
AIOps for Security ML and LLM assistance in triage Receives evaluation and governance of AI tools
Figure 1. Where SOC Ops sits in the flow of security work
incident hand-offdispositions, tuning requestssilent sources, gapsSecDataOps: telemetry andparsingDetectionOps: testeddetectionsSOC Ops: triage, decide,escalateThreatOps: intelligence andhuntsResponseOps: declare,contain, recoverSecOps: security and IToperations contract
Diagram source (Mermaid)
flowchart TB
  DATA["SecDataOps: telemetry and parsing"] --> DET["DetectionOps: tested detections"]
  DET --> SOC["SOC Ops: triage, decide, escalate"]
  TI["ThreatOps: intelligence and hunts"] --> DET
  TI -.-> SOC
  SOC -->|"incident hand-off"| RSP["ResponseOps: declare, contain, recover"]
  SOC -->|"dispositions, tuning requests"| DET
  SOC -->|"silent sources, gaps"| DATA
  SECOPS["SecOps: security and IT operations contract"] -.-> SOC
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class SOC core
  class DATA,DET,TI,RSP,SECOPS neutral

Text equivalent: Telemetry from SecDataOps feeds detections from DetectionOps, which produce alerts for SOC Ops. ThreatOps feeds intelligence into DetectionOps and context into triage. SOC Ops hands suspected incidents to ResponseOps, which declares, contains and recovers. SOC Ops also sends dispositions and tuning requests back to DetectionOps, and reports of silent sources and data gaps back to SecDataOps. SecOps, the contract between security and IT operations, supplies the operational support the SOC depends on.

MITRE’s second strategy is “Give the SOC the Authority to Do Its Job”: “An effective SOC has a charter and set of authorities, signed by constituency executive(s), which enable it to advocate for needed resources and gain cooperation to execute its mission”[1]. Its charter elements include authority to monitor, to scan, to “Coordinate or directly apply active or passive countermeasures” and to retain digital artifacts[1]; a one-page template is in Templates.

The principles below are this Atlas’s proposal, distilled from the sources cited in each line:

  1. Every alert that reaches a human gets a decision and a reason. Untriaged alerts are unmanaged risk. MITRE: “No alert should sit untriaged beyond the triage SLO, which is usually less than an hour”[1].
  2. Analysts work a curated queue, never a firehose. “Do not ask analysts to monitor an unfiltered feed”[1].
  3. Dispositions are data. Each closed alert is evidence for improving or retiring a detection; FIRST calls fine-grained qualification “an important input for continuous improvement of detection use cases”[4].
  4. Speed targets come with quality checks. MITRE warns that mean-time-to-close SLAs “may force analysts to perform weak alert investigation”[1].
  5. Authority is written down before it is needed. Who may isolate a host or disable an account, on which assets, without asking.
  6. Coverage is honest. The SOC publishes its hours, blind spots and silent sources.
  7. People are the capacity. Staffing, rotation and growth are operating decisions, not HR afterthoughts[20].
  8. Outsourcing moves execution, not accountability. “No matter the outsourcing arrangement, constituency system owners and executives are still accountable for security and risk reduction”[1].

Non-goals. The SOC is not the place to answer requests such as “Can you tell me what employee x was doing all day” outside a legitimate, authorised investigation (NCSC’s example[3]), not the owner of every security project, and not a 24x7 help desk for IT problems.

Fundamentals: services, people, data and tools

Link to section “Fundamentals: services, people, data and tools”

SOC-CMM assesses five domains (Business, People, Process, Technology, Services)[5]; the table maps similar building blocks to what goes wrong when one is missing.

Building block What it is Failure if missing
Mandate and services Charter, constituency, service catalogue, hours Asked to do everything, measured on nothing
People and roles Analysts, shift leads, engineers, manager One expert holds everything; nights uncovered
Process Triage method, playbooks, matrices, handover, tuning, QA Every analyst decides differently; nothing improves
Data Log sources, asset criticality, identity context, intelligence Alerts without context; silent sources unnoticed
Technology SIEM, EDR/XDR, case management, SOAR, paging Alerts in six consoles; no timestamps to measure

NCSC groups SOC functions into four: informing the SOC, developing capability, detect and respond, and supporting the SOC[3]. This page covers mainly the third and the management of all four.

Tiered or tierless? MITRE treats the choice as open: “Many SOCs that have more than a dozen analysts find it necessary and appropriate to tier analysis”, while others succeed without tiers, and “Highly effective SOCs enable their staff to reach outside their assigned duties on a routine basis”[1]. Two warnings: “Do not give the analyst a rigid 120 seconds to investigate every single alert or else”, and of tier 1: “This is a growth position, not an underclass”[1].

Role Main job Typical decisions
Triage analyst (tier 1) Work the queue with playbooks Close with disposition, escalate, request enrichment
Investigation analyst (tier 2) Deep investigation, scoping Hand off to the IR lead when the incident criteria are met; recommend containment
Specialist (tier 3) Hunting, malware analysis, forensics Hypotheses, new detection requests
Shift lead Owns the shift: queue health, handover, escalations out of the SOC Re-prioritise, call in help, page on-call
SOC engineer Sources, SIEM, SOAR, integrations Fix silent sources and pipelines (with SecDataOps)
SOC manager Staffing, metrics, stakeholders, providers Coverage model, targets, budget
IR lead (ResponseOps) Declares and runs security incidents Declaration, containment strategy, eradication, recovery
Asset or service owner Owns the affected system Provides context; carries out or accepts disruption

RACI for the core activities (R = responsible, A = accountable, C = consulted, I = informed):

Activity Tier 1 Tier 2 Shift lead SOC manager Others
Triage an alert R C A I –
Hand off a case that meets the incident criteria C R A I IR lead C
Declare a security incident – C C I IR lead A, R; system owner C
Contain a user device or standard account (asset tier 3, pre-approved) R C A I IR lead I; asset owner I
Isolate a critical asset or service – C I I IR lead R; system owner R; executive sponsor A
Shift handover R R A I –
Tuning request from dispositions R C C I Detection engineering A
Change triage SLOs C C C A Stakeholders I

The declaration and critical-containment rows are an Atlas proposal shared with ResponseOps: the SOC hands off, the IR lead declares and is accountable from then on, and an executive owns taking a critical service offline. If the IR lead delegates declaring the lowest-severity incidents to the shift lead, write that into the charter.

Skills and qualification. MITRE describes a “check-ride” qualification that each new hire must pass within a set period, training in which analysts “pick out activity from a real or synthetic set of log data, using the same tools that the SOC has in operation”, and apprenticeship-style pairing[1]. A skills matrix (this Atlas’s proposal):

Role Must show before working alone Evidence of readiness
Tier 1 Case tools; each playbook family; escalation package; pass-down log Shadow, then reverse-shadow shifts; shift-lead sign-off per playbook family; clean sampled cases
Tier 2 Scoping across endpoint, identity and cloud logs; incident criteria; side effects of containment Supervised hand-offs accepted by the IR lead; a tabletop
Shift lead Queue under load; handover; whom to page; the authority matrix Supervised shifts; a night drill led
SOC engineer Source onboarding, parsers, SOAR change review A reviewed, tested playbook change (Lab 3)

Lifecycle: from Day 0 design to Day 2 tuning

Link to section “Lifecycle: from Day 0 design to Day 2 tuning”
  • Day 0 – design. Decide the mission and constituency, the threat profile, the services, the hours and the sourcing model. NCSC insists the capability be “proportionate to the threat faced by your organisation”[21], and the collection’s introduction says “there is no one-size-fits-all SOC”[22].
  • Day 1 – stand-up. Onboard priority sources, build the asset and identity context, write the first playbooks, set triage SLOs, run a test week. ASD and partners advise establishing a baseline of normal activity first and warn that “Baselines are not built in a day”; during baselining “there is a significant risk that if the system is already compromised, malicious activities will be accepted as normal”[14].
  • Day 2 – operate and improve. Run shifts, handovers and the tuning loop; review metrics weekly; test detections and the SOC itself (purple-team exercises, tabletop exercises); reassess maturity yearly. Keep the baseline current as assets and behaviour change: ASD warns that “a failed query will return no report”, which “can be mistaken for there being nothing to report” when the real cause is a baseline that no longer reflects business-as-usual activity[14].

Rhythm. A shift huddle at every handover; a daily review of open escalations; a weekly tuning review with detection engineering (noisy rules ranked by analyst time); a monthly service review (with your provider, if any); a quarterly coverage and staffing review; a yearly maturity and charter review.

Hours of coverage. MITRE lists options between 8x5 and 24x7: extended hours (12x5), weekend shifts, monitoring-only nights, follow-the-sun, or handing off-hours triage to a parent SOC or a provider[1]. NCSC adds a point that is easy to miss: “The effectiveness of a 24/7 SOC would be greatly reduced if other IT functions are only covered 9-5 with no on-call arrangements”[3]. A night analyst who cannot reach anyone with authority over the affected system can only watch.

Escalation outside the SOC. Write down, per priority, who is called, by whom and within what time: IR lead, asset owner, legal and privacy, communications. The priority matrix in the reference implementation is the starting point.

Oversight. NCSC says a SOC needs “robust governance in order to make sure the SOC is operating legally, complying with regulation, organisational policies and ensuring that the SOC powers are not misused or abused”[3]. In practice: a steering group that approves the charter and coverage, audit trails on analyst searches of sensitive data, and a privacy review of monitoring scope.

Continuity of the SOC itself. For MITRE, the SOC must “continue the essential function of monitoring and defending the constituency” through emergencies, and planning should be pragmatic: “Thermonuclear war is rare. Bad weather is not.”[1] Plan for a SIEM or case-tool outage (a fallback queue), for identity, email or chat that is compromised or down (out-of-band contacts and break-glass access that are “insulated from that compromise”, in MITRE’s words[1]; ResponseOps treats these services as suspect until scoping clears them), and for a shift that cannot be staffed. MITRE suggests practising twice a year; see Checklists.

The logical architecture below fits most SOCs, whether the components are one vendor’s platform, several tools or a provider’s service. Three design choices matter more than product names:

  1. One queue. Alerts from SIEM, EDR, email security and cloud services should land in one case system, deduplicated and grouped, so that analysts work one prioritised list and every alert gets timestamps. MITRE: analysts “should be presented with discrete views into the data that can be fully evaluated over the course of their shift”[1].
  2. Context before eyes. Enrichment (asset owner and criticality, identity and group membership, recent changes, intelligence matches, previous dispositions) should run before an analyst opens the alert. NIST recommends integrating “up-to-date CTI and other contextual information (e.g., asset inventories) into adverse event analysis” (DE.AE-07)[13].
  3. A protected SOC enclave. The SOC’s tools hold the most sensitive map of the estate. MITRE’s recommendation is blunt: “Limit exposure of SOC monitoring infrastructure, sensors, analyst workstations, or any other SOC equipment from the general constituency’s domain”[1]. NCSC adds that attackers with SOC data could “hide their tracks”[3].
Figure 2. Logical reference architecture of a SOC
SOC enclavealertsapproved actionsincident hand-offsSources: endpoints, identity,email, cloud, networkCollection and parsingSIEM / XDR analyticsCase management: onequeueSOAR: enrich, route, actAnalysts and shift leadContext: assets, identities,intelligenceResponseOps
Diagram source (Mermaid)
flowchart TB
  SRC["Sources: endpoints, identity, email, cloud, network"]
  subgraph ENC["SOC enclave"]
    PIPE["Collection and parsing"]
    SIEM["SIEM / XDR analytics"]
    CASE["Case management: one queue"]
    SOAR["SOAR: enrich, route, act"]
    AN["Analysts and shift lead"]
  end
  CTX["Context: assets, identities, intelligence"]
  IR["ResponseOps"]
  SRC --> PIPE
  PIPE --> SIEM
  SIEM -->|"alerts"| CASE
  CASE <--> SOAR
  CTX --> SOAR
  CASE --> AN
  AN -->|"approved actions"| SOAR
  AN -->|"incident hand-offs"| IR
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class PIPE,SIEM,CASE,SOAR,AN core
  class SRC,CTX,IR neutral

Text equivalent: Sources in the monitored estate (endpoints, identity, email, cloud and network) send data to collection and parsing inside a protected SOC enclave. The SIEM or XDR analytics raise alerts into a single case-management queue. A SOAR layer enriches and routes cases using context about assets, identities and intelligence, and carries out actions that analysts approve. Analysts and the shift lead work the queue and hand suspected incidents to ResponseOps.

Alternatives and trade-offs. An XDR suite with built-in case management cuts integration work but ties triage to one vendor’s telemetry; a SIEM-centred design sees more sources but needs more engineering; a provider-run design moves the platform, not the context. ASD and partners warn that SOAR is “usually not suitable for immature environments”; a working SIEM comes first[14].

End-to-end workflow: from alert to decision

Link to section “End-to-end workflow: from alert to decision”

The happy path is short: an enriched alert arrives, an analyst picks it up within the triage SLO, works the playbook, and records a disposition or escalates. Two clocks run: the triage clock stops at the first human action; the decision target (for example, P1 decided within 4 hours) runs until the disposition or hand-off. SOCs fail on the exception paths: expired suppressions, assets nobody owns, alerts tier 1 cannot settle, and alerts at night.

Figure 3. Triage workflow with its exception paths
noyesnoyesyesnoyesnoyesnoyesnoAlert enters the queuePart of an open case?Automatic enrichmentAttach to caseActive suppression?Tier 1 triage: first actionAuto-close as benign; QAsampleCan tier 1 decide?Malicious?Escalate to tier 2Incident criteria met?Logic or data wrong?Hand off to the IR leadClose: true positiveClose: false positiveClose: benign positive
Diagram source (Mermaid)
---
config:
  layout: elk
---
flowchart TB
  A["Alert enters the queue"] --> B{"Part of an open case?"}
  B -->|"no"| D["Automatic enrichment"]
  B -->|"yes"| C["Attach to case"]
  D --> E{"Active suppression?"}
  E -->|"no"| G["Tier 1 triage: first action"]
  E -->|"yes"| F["Auto-close as benign; QA sample"]
  G --> H{"Can tier 1 decide?"}
  H -->|"yes"| J{"Malicious?"}
  H -->|"no"| I["Escalate to tier 2"]
  I --> J
  J -->|"yes"| L{"Incident criteria met?"}
  J -->|"no"| K{"Logic or data wrong?"}
  L -->|"yes"| M["Hand off to the IR lead"]
  L -->|"no"| N["Close: true positive"]
  K -->|"yes"| FP["Close: false positive"]
  K -->|"no"| BP["Close: benign positive"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class A,D,G,C,F,FP,BP,N core
  class B,E,H,J,K,L decision
  class M,I neutral

Text equivalent: An alert enters the queue. If it belongs to an open case it is attached to that case; otherwise it is enriched. If an active suppression (with an owner and an expiry date) matches, it is auto-closed as benign and joins a quality-assurance sample. Otherwise a tier 1 analyst picks it up, which stops the triage clock. If tier 1 cannot decide (the playbook does not settle it, or the decision target would be missed), it goes to tier 2, who make the same decisions. Malicious activity that meets the incident criteria is handed off to the IR lead, who declares the incident; other malicious activity is closed as a true positive and the owner told. Activity that is not malicious is closed as a false positive when the detection logic or data was wrong (with a tuning request), or as a benign positive when it was legitimate (with a tuning note).

NIST’s recommendation for DE.AE-08 is to “Apply incident criteria to known and assumed characteristics of analyzed activity, and consider known false positives to determine whether an incident should be declared”[13]. That presupposes written criteria (CIS Safeguard 17.9[16]); they mark the boundary with ResponseOps.

Every metric in Metrics and KPIs comes from the case record, so its states and timestamps are part of the architecture. Microsoft Sentinel’s incident table, for example, writes a row for every change, with CreatedTime, FirstModifiedTime, ModifiedBy and Classification, and Microsoft’s sample query computes time to triage as FirstModifiedTime - CreatedTime[23]. That breaks when automation touches the case first: ModifiedBy is the “User or system that modified the incident”[23], and automation rules can “Triage new incidents by changing their status from New to Active and assigning an owner”[24]. With enrichment and routing ahead of the analyst, as above, FirstModifiedTime measures automation latency. Measure the first human change instead (see the reference implementation).

Figure 4. Case states and the timestamps that SOC metrics depend on
createdfirst human actiondispositionto tier 2criteria metcriteria metdispositionIR lead declaresreopenedNewTriageClosedInvestigatingHandoff
Diagram source (Mermaid)
stateDiagram-v2
  [*] --> New: created
  New --> Triage: first human action
  Triage --> Closed: disposition
  Triage --> Investigating: to tier 2
  Triage --> Handoff: criteria met
  Investigating --> Handoff: criteria met
  Investigating --> Closed: disposition
  Handoff --> Closed: IR lead declares
  Closed --> Triage: reopened
  Closed --> [*]

Text equivalent: A case is created in state New. The first human action moves it to Triage and ends the time-to-triage interval. From Triage it is closed with a disposition, escalated to tier 2 (Investigating) or, if the incident criteria are met, moved to Handoff; Investigating leads to the same two exits. A case in Handoff is closed on the SOC side once the IR lead declares the incident and is linked to the incident record, where the SOC’s further scoping and monitoring work is logged. A closed case can be reopened into Triage; count reopenings, because they signal hasty closure.

A hand-off the IR lead declines returns to Investigating with the reason recorded: good coaching material for tier 2.

MITRE describes the end-of-shift “pass-down or shift handoff, whereby the outgoing team briefs the incoming team on various issues of note”, and recommends a standard template filled in by the shift lead (the pass-down log follows MITRE’s list of contents)[1]. It even suggests that the outgoing analysts and the incoming shift lead sign it. A paper signature is optional; explicit acceptance is not.

Figure 5. Handover: an open item changes owner only when it is accepted
yesnoyesnoOpen item at shift endIn the pass-down log?Briefed to incoming leadDropped: no ownerIncoming lead accepts?Outgoing team and incominglead signOutgoing owner keeps it,clarifiesIncoming lead assigns anowner
Diagram source (Mermaid)
---
config:
  layout: elk
---
flowchart TB
  A["Open item at shift end"] --> B{"In the pass-down log?"}
  B -->|"yes"| C["Briefed to incoming lead"]
  B -->|"no"| X["Dropped: no owner"]
  C --> D{"Incoming lead accepts?"}
  D -->|"yes"| F["Outgoing team and incoming lead sign"]
  D -->|"no"| E["Outgoing owner keeps it, clarifies"]
  E --> C
  F --> G["Incoming lead assigns an owner"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class A,C,F,G core
  class B,D decision
  class X,E risk

Text equivalent: Each open item at the end of a shift (a case, an escalation, an outage, a promise) is either written in the pass-down log or dropped with no owner. A logged item is briefed to the incoming shift lead. If the lead does not accept it, the outgoing owner keeps it, clarifies it and briefs it again. Once it is accepted, the outgoing team and the incoming lead sign the log, and the incoming lead assigns the item an owner.

Practice Goal Trigger Owner Output Verification
Triage to a playbook Consistent, fast decisions Every alert Tier 1 Disposition, notes, next step Weekly QA sample
Scoped suppression Remove known-benign noise safely Repeated benign positives Shift lead + detection engineer Match, reason, owner, expiry Expiry report; sampled suppressions
Escalation or hand-off with a package Tier 2 or the IR lead can act at once Tier 1 cannot decide; incident criteria met Tier 1 (escalation), tier 2 (hand-off) Timeline, entities, evidence links, hypothesis Escalation acceptance; declined hand-offs
Shift handover Nothing dropped between shifts End of every shift Shift leads Signed pass-down log Missed-item review
Tuning loop Fewer, better alerts Weekly review of dispositions Detection engineering Tuning tickets, retired rules Analyst minutes per true positive
Source health watch Notice silent or degraded sources Missing data, parse failures SOC engineer Ticket to SecDataOps Sources silent beyond threshold

A triage method that fits on a card. For each alert, answer in order: What fired, and what does the rule look for? Who and what is involved, and how critical are they? Is this normal for this user, host or service (history, change calendar, peers)? What happened just before and after? What would I expect to see if this were malicious, and do I see it? What is my decision, my confidence, and the evidence for it? The last question is the one most often skipped, and it is what makes the case record useful for tuning and audit.

Playbooks. MITRE’s rule of thumb: “if an incident type is handled by the SOC, on average, once a month or more, there should be an approved SOP on file for that incident”[1]. Its contents include scope, “Stimulating conditions” (when to use it) and procedures; too little detail produces inconsistency, too much makes the playbook obsolete quickly[1]. Write playbooks per alert family (identity, endpoint, email, cloud control plane), not per rule, and link each detection to its playbook.

Knowledge ownership. Playbooks decay unless someone owns them. MITRE suggests one knowledge management system for all playbooks, with the official ones “segregated from other casual information capture”, and calls documentation and institutional knowledge “one of the best ways to ensure the SOC is prepared for staff departures”[1]. This Atlas proposes a named owner and review date per playbook family, an edit whenever a case exposes a gap, and environment notes (known-benign patterns, asset quirks, contacts) in a SOC-only wiki inside the enclave, where MITRE also places it[1].

Escalation package. MITRE describes contract terms that protect the client SOC “from being flooded with poor quality escalations”[1]; the same logic applies between tiers and at the hand-off. Templates lists the minimum fields.

Source Status What it gives SOC Ops
NIST CSF 2.0, Detect (DE.CM, DE.AE) and Respond (RS.MA)[12] Framework of outcomes (NIST) Outcomes: monitor, analyse adverse events, declare incidents against criteria, triage and escalate incident reports
NIST SP 800-61 Rev. 3 (2025)[13] Recommendations SIEM and SOAR for continuous monitoring; contract clarity with providers; roles
CIS Controls v8.1 (Controls 8 and 17)[25][16] Prescriptive safeguards Centralise logs, retain at least 90 days, review weekly or more often; incident thresholds
FIRST CSIRT Services Framework 2.1[4] Community services taxonomy Monitoring and detection, event analysis, log and sensor management, use-case lifecycle
ENISA, How to set up CSIRT and SOC (2020)[2] Good-practice guide (EU agency) Setup phases; typical SOC KPIs
NCSC, Building a SOC (2022)[22] Government guidance (UK) Threat-proportionate operating model, functions, hours, governance
ASD, CISA and partners, SIEM and SOAR guidance (2025)[14] Government guidance (multinational) Baseline, log standards, silent-source detection, SOAR readiness, cost
MITRE, 11 Strategies (2022)[1] Practitioner handbook Charter, structure, staffing, 24x7, handover, metrics, outsourcing
SOC-CMM (v2.4 model)[5] Open maturity model Self-assessment across five domains
MITRE ATT&CK[26] Knowledge base of adversary behaviour Shared language for alerts and coverage (owned by DetectionOps)

None of these certifies a SOC; sector rules on monitoring, retention and reporting come from your legal and compliance team.

Category Job in the SOC Examples (verified, not endorsed) Selection questions
SIEM Collect, correlate, alert, search Microsoft Sentinel[27], Google Security Operations[28], Elastic Security[29], Wazuh (open source)[30] Parsing of your sources; a learnable query language; cost per volume; retention tiers
EDR / XDR Endpoint telemetry, detection, remote isolation Platform suites from endpoint vendors Containment API; offline behaviour; console quality
Case management One queue, timestamps, evidence TheHive[31]; built into most SIEM and XDR suites Grouping; custom dispositions; export of the change history
SOAR Enrich, route, run playbooks Sentinel automation rules and playbooks[24]; SOAR modules of SIEM suites Who can edit playbooks; approval steps; audit log; rate limits
Paging and chat Reach on-call people Your incident paging tool (IncidentOps) Escalation policies; acknowledgement tracking

Date-sensitive facts worth knowing: Microsoft states that after 31 March 2027 Microsoft Sentinel “will no longer be supported in the Azure portal and will be available only in the Microsoft Defender portal”[27]. Google describes Google Security Operations as “a unified experience across SIEM, SOAR, and threat intelligence”[28].

Lock-in sits in detection content, playbooks and skills written for one query language (DetectionOps), not in licences.

Automation earns its keep in SOC Ops in four places: enrichment (always), grouping and deduplication (always), closing known-benign patterns (with owners, expiry dates and sampling) and containment (pre-approved, low-risk actions on asset-tier-3 devices and standard accounts, only for rules with a measured high true share, and with a rate limit). MITRE accepts automated response where “a set of indicators is correct often enough”, and says the decision “must include the risk calculation of the increase in the speed of the response vs the risk of taking the wrong action”[1]. Microsoft Sentinel’s automation rules, for example, can suppress, triage and assign incidents and carry an expiration date, which Microsoft suggests for “noise” incidents caused by planned, time-limited activities such as penetration testing[24].

The guardrails matter more than the tools. ASD and partners warn that a misconfigured SOAR “may misidentify regular user or system behaviour as an event or incident and take automated measures to isolate and respond”, disrupting service[14]. FIRST’s framework says “in order to avoid alert fatigue, automation is key” and that recurring cases “should be identified and automated”[4]. Both are true; the discipline is to automate the recurring and reversible first.

AI assistants. Vendors now market generative AI for SOC work; Google, for instance, advertises “AI-generated case summaries and recommendations on how to respond”[28]. Treat these as aids whose output a person checks, measure them like detections, and govern them as AIOps for Security describes. MITRE’s caution still applies: “no matter how much automation, correlation, ML, and artificial intelligence a SOC has, there must be a human that evaluates the output of all that machinery”[1].

Reference implementation: the SOC operating pack

Link to section “Reference implementation: the SOC operating pack”

The minimum operating artefacts for a small-to-medium SOC (in-house, hybrid or provider-run). Values are illustrative; agree yours with analysts, asset owners and any provider.

1. Service catalogue (excerpt).

Service Hours Scope Output Target (example)
Alert triage 24x7 (nights by provider) All alerts reaching the queue Disposition and notes on every alert P1 triage started within 15 minutes
Investigation 08:00–18:00, on-call outside Escalated alerts Decision: incident or not, with evidence P1 decision within 4 hours
User-reported suspicious activity 08:00–18:00 Phishing reports, help-desk referrals Answer to reporter, case if needed Reply within 1 business day
Containment (pre-approved) 24x7 Asset-tier-3 endpoints (user devices) and standard user accounts Host isolated or account disabled, owner notified Within 30 minutes of the decision
Monthly service report Monthly All services Metrics, top tuning items, coverage gaps Fifth working day

2. Priority matrix (severity of the detection × criticality of the asset or identity). Asset tiers rank what is attacked, not who investigates, and asset tier 1 is the most critical:

Severity \ asset tier Asset tier 1 (crown jewels, admins) Asset tier 2 (servers, key SaaS) Asset tier 3 (user devices)
High P1: triage within 15 min; page on-call outside hours P1 P2: triage within 30 min
Medium P2 P2 P3: triage within 60 min
Low P3 P3 P4: triage within 4 h (business hours)

3. Pass-down log template. Based on the contents MITRE lists[1]; keep it in the case tool so it is searchable.

Example status: template; not executed.

PASS-DOWN LOG         Date: ____-__-__   Shift: day / evening / night
Outgoing lead: __________   Incoming lead: __________
Analysts on duty: ______________________________________________

1. Open cases (ID, priority, owner, next step, deadline)
2. Escalations outside the SOC (to whom, when, status)
3. Tips and referrals received (help desk, users, partners)
4. Sensor, log-source or tool outages (since when, ticket)
5. Odd activity not yet a case (what, where, why it matters)
6. Required duties done (queue empty to P3, health check, reports)
7. Unfinished tasks and promises made to anyone
8. Anything not covered by an SOP

Handed over (outgoing lead): ________   Accepted (incoming lead): ________

4. Metrics queries. If you run Microsoft Sentinel, the incident table already holds what the metrics need, with one trap: FirstModifiedTime includes changes made by automation (see the case record). Step 1 lists who and what changes your incidents; step 2 measures time to triage from the first change by a listed analyst; step 3 counts dispositions per analytics rule.

Example status: written for this page, adapted from Microsoft’s documented sample queries; parsed and semantically checked on 2026-10-17 with Microsoft’s Kusto.Language library (npm @kusto/language-service-next 12.4.1) against the documented SecurityIncident schema; not executed (requires a Sentinel workspace). The classification strings are the API values TruePositive, BenignPositive, FalsePositive and Undetermined[32]; check them, and the ModifiedBy values, in your workspace.

// Step 1: who and what modifies incidents (people, automation rules, playbooks, sync)
SecurityIncident
| where TimeGenerated > ago(14d)
| summarize Updates = count(), Incidents = dcount(IncidentNumber) by ModifiedBy
| order by Updates desc
// Step 2: time to triage = first change by a listed analyst - created
let lookback = 30d;
let utcOffset = 0h;  // the SOC's offset from UTC; daylight saving is not handled
let analysts = dynamic(["<analyst as shown by step 1>", "<another analyst>"]);
let slo = datatable(Severity: string, Rank: int, SloMin: int) [
    "High", 1, 30, "Medium", 2, 60, "Low", 3, 240, "Informational", 4, 240
];
let firstHuman = SecurityIncident
    | where TimeGenerated > ago(lookback)
    | where ModifiedBy in~ (analysts)
    | summarize FirstHumanAction = min(LastModifiedTime) by IncidentNumber;
SecurityIncident
| where TimeGenerated > ago(lookback)
| summarize arg_max(TimeGenerated, *) by IncidentNumber
| where CreatedTime > ago(lookback)
| join kind=leftouter firstHuman on IncidentNumber
| lookup slo on Severity
| extend Local = CreatedTime + utcOffset
| extend Weekday = dayofweek(Local) between (1d .. 5d)
| extend Shift = iff(Weekday and hourofday(Local) between (8 .. 17), "day", "off-hours")
| extend TriageMin = datetime_diff('minute', FirstHumanAction, CreatedTime)
| extend NoHuman = isnull(FirstHumanAction)
| extend AutoClosed = NoHuman and Status == "Closed"
| extend WaitMin = iff(NoHuman, datetime_diff('minute', now(), CreatedTime), TriageMin)
| summarize Incidents = count(), AutoClosedCount = countif(AutoClosed),
    Untouched = countif(NoHuman and not(AutoClosed)),
    Breaches = countif(not(AutoClosed) and WaitMin > SloMin),
    P50 = percentile(TriageMin, 50), P90 = percentile(TriageMin, 90)
    by Rank, Severity, Shift
| order by Rank asc, Shift asc

Aggregates ignore nulls[33], so the percentiles cover only incidents a person has touched; the query counts untouched incidents separately and treats one older than its target as a breach. Incidents that automation closed with no human action are reported, not counted as breaches (they belong in the QA sample). The targets are Lab 1’s, keyed on severity; key on priority if you store it as a tag. After onboarding to the Defender portal, several changes to one incident within 5–10 minutes reach Sentinel as one update carrying only the latest[24], so treat triage times under about ten minutes as approximate.

A Sentinel-native alternative to the allow-list: an update-trigger automation rule whose documented Updated by condition equals user (it separates users from automation rules, playbooks, applications and alert grouping)[24] can tag an incident on its first human change; the tag write is itself an automation row, so take its LastModifiedTime as the first human change, give or take seconds.

// Step 3: dispositions per analytics rule; true share over decided alerts only
SecurityIncident
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated, *) by IncidentNumber
| where Status == "Closed" and ClosedTime > ago(30d)
| mv-expand RuleId = RelatedAnalyticRuleIds to typeof(string)
| summarize TruePos = countif(Classification == "TruePositive"),
    Benign = countif(Classification == "BenignPositive"),
    FalsePos = countif(Classification == "FalsePositive"),
    Undetermined = countif(Classification == "Undetermined"), Closed = count()
    by RuleId
| extend Decided = TruePos + Benign + FalsePos
| extend TrueShare = iff(Decided > 0, round(100.0 * TruePos / Decided, 1), real(null))
| order by Closed desc

The suppression record format and a tested first-pass triage policy are in Lab 3.

All three labs were executed (Labs 1 and 2 on 2026-10-16 and 2026-10-17, Lab 3 on 2026-10-17 after each revision; Ubuntu 20.04, Python 3.8.10, standard library only) and show real output; all data is synthetic and nothing touches a production system.

Lab 1 (beginner): read a week of the queue

Link to section “Lab 1 (beginner): read a week of the queue”

Objective. Compute time to triage by shift, triage-SLO breaches and alert quality per rule from an alert export, and see why one average hides the night. Prerequisites. Python 3.8 or later. Cost and risk. None.

  1. Save the export as alerts.csv (Monday 12 to Thursday 15 October 2026; dispositions use this page’s four values):

Example status: executed on 2026-10-16 as input to the script below.

alert_id,rule,severity,created,first_action,closed,disposition,escalated
A-01,impossible-travel,medium,2026-10-12T09:02,2026-10-12T09:09,2026-10-12T09:31,benign,no
A-02,impossible-travel,medium,2026-10-12T10:15,2026-10-12T10:20,2026-10-12T10:41,benign,no
A-03,mfa-fatigue,high,2026-10-12T11:40,2026-10-12T11:44,2026-10-12T13:10,true,yes
A-04,new-admin-account,high,2026-10-12T14:05,2026-10-12T14:12,2026-10-12T14:50,benign,no
A-05,malware-quarantined,low,2026-10-12T16:30,2026-10-12T17:55,2026-10-12T18:02,false,no
A-06,impossible-travel,medium,2026-10-12T22:47,2026-10-13T07:58,2026-10-13T08:10,benign,no
A-07,malware-quarantined,low,2026-10-13T03:12,2026-10-13T08:05,2026-10-13T08:09,false,no
A-08,encoded-powershell,high,2026-10-13T08:20,2026-10-13T08:26,2026-10-13T11:45,true,yes
A-09,mfa-fatigue,high,2026-10-13T12:01,2026-10-13T12:09,2026-10-13T12:40,false,no
A-10,impossible-travel,medium,2026-10-13T13:33,2026-10-13T13:52,2026-10-13T14:02,benign,no
A-11,encoded-powershell,high,2026-10-13T15:10,2026-10-13T15:14,2026-10-13T15:58,benign,yes
A-12,new-admin-account,high,2026-10-13T23:30,2026-10-14T08:02,2026-10-14T09:15,true,yes
A-13,malware-quarantined,low,2026-10-14T09:40,2026-10-14T10:55,2026-10-14T11:01,false,no
A-14,impossible-travel,medium,2026-10-14T10:05,2026-10-14T10:11,2026-10-14T10:20,benign,no
A-15,mfa-fatigue,high,2026-10-14T14:22,2026-10-14T14:25,2026-10-14T15:30,undetermined,no
A-16,encoded-powershell,high,2026-10-14T19:48,2026-10-15T08:01,2026-10-15T08:40,false,no
  1. Save the script as soc_queue_report.py:

Example status: executed on 2026-10-16 (Python 3.8.10).

#!/usr/bin/env python3
"""Weekly SOC queue report: triage times, SLO breaches and alert quality."""
import csv
import math
import sys
from collections import defaultdict
from datetime import datetime

TRIAGE_SLO_MIN = {"high": 30, "medium": 60, "low": 240}  # agree these with your analysts
BUSINESS_HOURS = range(8, 18)  # 08:00-17:59 local time, Monday to Friday


def minutes(start, end):
    fmt = "%Y-%m-%dT%H:%M"
    delta = datetime.strptime(end, fmt) - datetime.strptime(start, fmt)
    return int(delta.total_seconds() // 60)


def pct(values, p):
    """Nearest-rank percentile: robust and easy to explain to stakeholders."""
    ordered = sorted(values)
    rank = max(1, math.ceil(p / 100 * len(ordered)))
    return ordered[rank - 1]


def after_hours(created):
    moment = datetime.strptime(created, "%Y-%m-%dT%H:%M")
    return moment.weekday() >= 5 or moment.hour not in BUSINESS_HOURS


def main(path):
    with open(path, newline="") as handle:
        rows = list(csv.DictReader(handle))
    for r in rows:
        r["ttt"] = minutes(r["created"], r["first_action"])  # time to triage
    ttt = [r["ttt"] for r in rows]
    print(f"alerts: {len(rows)}")
    print(f"time to triage (min): median {pct(ttt, 50)}, "
          f"p90 {pct(ttt, 90)}, max {max(ttt)}")
    off = [r for r in rows if after_hours(r["created"])]
    on = [r for r in rows if not after_hours(r["created"])]
    for label, group in (("business hours", on), ("after hours", off)):
        median = pct([r["ttt"] for r in group], 50)
        print(f"  {label + ':':15} {len(group)} alerts, median {median}")

    breaches = [r for r in rows if r["ttt"] > TRIAGE_SLO_MIN[r["severity"]]]
    print(f"triage SLO breaches: {len(breaches)}")
    for r in breaches:
        limit = TRIAGE_SLO_MIN[r["severity"]]
        print(f"  {r['alert_id']} {r['rule']} ({r['severity']}): "
              f"{r['ttt']} min > {limit}")

    counts = defaultdict(int)
    for r in rows:
        counts[r["disposition"]] += 1
    mix = ", ".join(f"{k} {v}" for k, v in sorted(counts.items()))
    print(f"dispositions: {mix}")

    print("per rule (decided alerts only):")
    by_rule = defaultdict(lambda: defaultdict(int))
    for r in rows:
        by_rule[r["rule"]][r["disposition"]] += 1
    for rule, d in sorted(by_rule.items()):
        decided = d["true"] + d["benign"] + d["false"]
        share = 100 * d["true"] / decided if decided else 0.0
        print(f"  {rule:20} n={decided:2} true={d['true']} benign={d['benign']} "
              f"false={d['false']} true-share={share:.0f}%")

    esc = [r for r in rows if r["escalated"] == "yes"]
    good = [r for r in esc if r["disposition"] == "true"]
    print(f"escalation rate: {100 * len(esc) / len(rows):.0f}% "
          f"({len(esc)}), confirmed by tier 2: {len(good)} of {len(esc)}")
    return 1 if breaches else 0


if __name__ == "__main__":
    sys.exit(main(sys.argv[1]))
  1. Run python3 soc_queue_report.py alerts.csv.

Expected output (exit code 1, because SLOs were breached):

alerts: 16
time to triage (min): median 7, p90 551, max 733
  business hours: 12 alerts, median 6
  after hours:    4 alerts, median 512
triage SLO breaches: 4
  A-06 impossible-travel (medium): 551 min > 60
  A-07 malware-quarantined (low): 293 min > 240
  A-12 new-admin-account (high): 512 min > 30
  A-16 encoded-powershell (high): 733 min > 30
dispositions: benign 7, false 5, true 3, undetermined 1
per rule (decided alerts only):
  encoded-powershell   n= 3 true=1 benign=1 false=1 true-share=33%
  impossible-travel    n= 5 true=0 benign=5 false=0 true-share=0%
  malware-quarantined  n= 3 true=0 benign=0 false=3 true-share=0%
  mfa-fatigue          n= 2 true=1 benign=0 false=1 true-share=50%
  new-admin-account    n= 2 true=1 benign=1 false=0 true-share=50%
escalation rate: 25% (4), confirmed by tier 2: 3 of 4

Validation. A-16 was created at 19:48 and first touched at 08:01 the next morning: 733 minutes. The overall median of 7 minutes looks excellent, yet the four after-hours alerts waited a median of 512 minutes, two of them high severity: the gap MITRE describes when a SOC that is not 24x7 “must catch up every morning on the previous night’s event data”[1]. impossible-travel produced five benign positives and no true positive: a candidate for context (VPN egress ranges, travel calendar), not deletion. Troubleshooting. ValueError: time data ... does not match format means a timestamp is not YYYY-MM-DDTHH:MM; KeyError means a header is misspelt; an empty group raises IndexError in pct(), so check that both shifts have alerts. Extension. Feed it a real export, and add reopen counts. Teardown. Delete the two files.

Lab 2 (intermediate): how many analysts does the queue need?

Link to section “Lab 2 (intermediate): how many analysts does the queue need?”

Objective. Estimate analysts per shift band from arrival rate and handling time with the Erlang C queueing formula, then convert seat-hours into full-time staff. Prerequisites. Python 3.8 or later. Cost and risk. None; the result is a planning estimate, not a budget.

Erlang C gives the probability that an arrival must wait when c servers handle offered load A (arrival rate × handling time), assuming Poisson arrivals and exponential handling times (the M/M/c queue); call-centre planners use it to size teams[34]. SOC alerts arrive in bursts and handling times vary widely, so treat the answer as a floor.

Example status: executed on 2026-10-16 (Python 3.8.10).

#!/usr/bin/env python3
"""Rough SOC triage staffing: Erlang C per shift band, then seats to FTE."""
import math

# Inputs (illustrative; replace with your own measured numbers)
BANDS = {            # band: (hours per week, alerts reaching analysts per hour)
    "weekday day":   (50, 12.0),   # Mon-Fri 08:00-18:00
    "weekday night": (70, 3.0),    # Mon-Fri 18:00-08:00
    "weekend":       (48, 2.5),    # Sat-Sun, all day
}
HANDLE_MIN = 9.0          # mean minutes of analyst work per alert (triage + notes)
TARGET_WAIT_MIN = 15.0    # triage should start within 15 minutes ...
TARGET_SHARE = 0.80       # ... for 80% of alerts
MIN_SEATS = 2             # two-person integrity on shift (MITRE recommendation)
# 2,080 contract hours minus 43 days of leave, holidays, sickness and training
PRODUCTIVE_HOURS_PER_FTE = 1736


def erlang_c(agents, load):
    """Probability that an alert waits (M/M/c queue, Erlang C)."""
    if agents <= load:
        return 1.0
    b = 1.0
    for n in range(1, agents + 1):           # Erlang B by recursion, numerically stable
        b = load * b / (n + load * b)
    return agents * b / (agents - load * (1 - b))


def seats_needed(per_hour):
    load = per_hour * HANDLE_MIN / 60.0       # offered load in erlangs
    agents = max(1, math.ceil(load))
    while True:
        wait_p = erlang_c(agents, load)
        share = 1 - wait_p * math.exp(-(agents - load) * TARGET_WAIT_MIN / HANDLE_MIN)
        if agents > load and share >= TARGET_SHARE:
            return agents, load, share
        agents += 1


def main():
    seat_hours = 0.0
    for band, (hours, per_hour) in BANDS.items():
        agents, load, share = seats_needed(per_hour)
        seats = max(agents, MIN_SEATS)
        util = 100 * load / seats
        seat_hours += seats * hours
        print(f"{band:13} {load:4.2f} erlangs: queue needs {agents}, staff {seats}, "
              f"util {util:2.0f}%, {100 * share:2.0f}% within {TARGET_WAIT_MIN:.0f} min")
    weekly_hours = sum(h for h, _ in BANDS.values())
    fte = seat_hours * 52 / PRODUCTIVE_HOURS_PER_FTE
    print(f"hours covered per week: {weekly_hours:.0f} of 168")
    print(f"analyst seat-hours per week: {seat_hours:.0f}")
    print(f"triage FTE before management, leads and engineering: {fte:.1f}")
    per_seat = 168 * 52 / PRODUCTIVE_HOURS_PER_FTE
    print(f"FTE per 24x7 seat at this leave model: {per_seat:.2f}")


if __name__ == "__main__":
    main()

Run python3 soc_staffing.py. Expected output:

weekday day   1.80 erlangs: queue needs 3, staff 3, util 60%, 95% within 15 min
weekday night 0.45 erlangs: queue needs 1, staff 2, util 22%, 82% within 15 min
weekend       0.38 erlangs: queue needs 1, staff 2, util 19%, 87% within 15 min
hours covered per week: 168 of 168
analyst seat-hours per week: 386
triage FTE before management, leads and engineering: 11.6
FTE per 24x7 seat at this leave model: 5.03

Validation. Seat-hours: 3 × 50 + 2 × 70 + 2 × 48 = 386 per week. FTE: 386 × 52 ÷ 1,736 = 11.6. At night, queueing alone would need one analyst; the second seat comes from MITRE’s advice that 24x7 SOCs “maintain a minimum staff of two analysts at all times” (integrity, safety, cross-checking, loneliness)[1]. The 5.03 FTE per round-the-clock seat matches MITRE’s “roughly five full time equivalents (FTEs), including fill-in for vacation and sick leave”[1]. Troubleshooting. If a band never meets the target, handling time is too long for that arrival rate: look at enrichment and noise before adding people. Extension. Add a band for the busiest two hours; vary HANDLE_MIN by ±50 %. Teardown. Delete the file.

Lab 3 (advanced): first-pass triage as code, with guardrails

Link to section “Lab 3 (advanced): first-pass triage as code, with guardrails”

Objective. Express suppressions, routing and automatic containment as tested code, so that automation cannot close alerts on unknown assets, use expired or whole-rule suppressions, isolate any asset more critical than asset tier 3 (user devices), act on an unproven rule, or isolate more hosts per hour than agreed. Prerequisites. Python 3.8 or later. Cost and risk. None; the script only prints decisions. In a real SOAR, run the same tests in CI before a playbook change is deployed.

  1. Save the policy as policy.json. The rules block holds each rule’s measured dispositions (Lab 1 computes them); known-bad-hash stands for an EDR detection of a file hash already confirmed malicious:

Example status: executed on 2026-10-17 as input.

{
  "suppressions": [
    {"id": "SUP-7", "rule": "impossible-travel", "match": {"user": "svc-vpn-probe"},
     "reason": "synthetic VPN availability probe", "owner": "soc-lead",
     "expires": "2026-11-30"},
    {"id": "SUP-3", "rule": "malware-quarantined", "match": {"host": "lab-av-02"},
     "reason": "nightly antivirus test file on a lab host", "owner": "soc-lead",
     "expires": "2026-09-30"}
  ],
  "auto_contain": {
    "action": "isolate-host", "lowest_tier": 3, "max_per_hour": 2,
    "min_decided": 20, "min_true_share": 0.9,
    "rules": {
      "known-bad-hash": {"decided": 46, "true": 45},
      "encoded-powershell": {"decided": 3, "true": 1}
    }
  },
  "page_outside_hours": ["P1"]
}
  1. Save the asset context as assets.json and the night’s alerts as alerts.json (Friday 16 October 2026, 02:05 to 02:24):

Example status: executed on 2026-10-17 as input.

{
  "build-07": {"tier": 2, "owner": "platform-eng"},
  "lab-av-02": {"tier": 3, "owner": "secops-lab"},
  "lap-0423": {"tier": 3, "owner": "workplace-it"},
  "lap-0511": {"tier": 3, "owner": "workplace-it"},
  "lap-0602": {"tier": 3, "owner": "workplace-it"},
  "lap-0715": {"tier": 3, "owner": "workplace-it"}
}
[
  {"id": "A-21", "rule": "impossible-travel", "severity": "medium",
   "host": "lap-0423", "user": "svc-vpn-probe", "created": "2026-10-16T02:05"},
  {"id": "A-22", "rule": "malware-quarantined", "severity": "low",
   "host": "lab-av-02", "user": "lab-runner", "created": "2026-10-16T02:07"},
  {"id": "A-23", "rule": "known-bad-hash", "severity": "high",
   "host": "lap-0423", "user": "m.ito", "created": "2026-10-16T02:10"},
  {"id": "A-24", "rule": "encoded-powershell", "severity": "high",
   "host": "lap-0511", "user": "j.diaz", "created": "2026-10-16T02:12"},
  {"id": "A-25", "rule": "known-bad-hash", "severity": "high",
   "host": "build-07", "user": "ci-runner", "created": "2026-10-16T02:15"},
  {"id": "A-26", "rule": "known-bad-hash", "severity": "high",
   "host": "lap-0602", "user": "a.okafor", "created": "2026-10-16T02:18"},
  {"id": "A-27", "rule": "known-bad-hash", "severity": "high",
   "host": "lap-0715", "user": "p.silva", "created": "2026-10-16T02:21"},
  {"id": "A-28", "rule": "new-admin-account", "severity": "high",
   "host": "vm-unknown-9", "user": "tmp-admin", "created": "2026-10-16T02:24"}
]
  1. Save the engine as triage_policy.py:

Example status: executed on 2026-10-17 (Python 3.8.10).

#!/usr/bin/env python3
"""First-pass triage as code: suppress, contain or route, with guardrails."""
import json
import sys
from datetime import date, datetime, timedelta

# The priority matrix of the operating pack: (severity, asset tier) -> priority
PRIORITY = {
    ("high", 1): "P1", ("high", 2): "P1", ("high", 3): "P2",
    ("medium", 1): "P2", ("medium", 2): "P2", ("medium", 3): "P3",
    ("low", 1): "P3", ("low", 2): "P3", ("low", 3): "P4",
}
REQUIRED = ("id", "rule", "match", "reason", "owner", "expires")
BUSINESS_HOURS = range(8, 18)  # 08:00-17:59 local time, Monday to Friday


def check_policy(policy):
    for s in policy["suppressions"]:
        missing = [f for f in REQUIRED if not s.get(f)]  # an empty match is missing
        if missing:
            raise ValueError(f"suppression {s.get('id', '?')} lacks {', '.join(missing)}")
    return policy


def load_policy(path):
    with open(path) as handle:
        return check_policy(json.load(handle))


def after_hours(moment):
    return moment.weekday() >= 5 or moment.hour not in BUSINESS_HOURS


def containment_blocker(rule, tier, policy, recent):
    """None if automatic containment is allowed, otherwise the reason it is not."""
    auto = policy["auto_contain"]
    stats = auto["rules"][rule]
    share = stats["true"] / stats["decided"] if stats["decided"] else 0.0
    if tier < auto["lowest_tier"]:
        return f"asset tier {tier}"
    if stats["decided"] < auto["min_decided"]:
        return f"only {stats['decided']} decided alerts for this rule"
    if share < auto["min_true_share"]:
        return f"true share {share:.0%} is below {auto['min_true_share']:.0%}"
    if len(recent) >= auto["max_per_hour"]:
        return f"limit of {auto['max_per_hour']} isolations per hour reached"
    return None


def decide(alert, policy, assets, today, recent):
    """Return (action, notes); recent lists the isolations of the last hour."""
    notes = []
    created = datetime.fromisoformat(alert["created"])
    asset = assets.get(alert["host"])
    if asset is None:
        notes.append("unknown asset: treated as asset tier 1, no automation")
        tier, owner = 1, "unknown"
    else:
        tier, owner = asset["tier"], asset["owner"]
        for s in policy["suppressions"]:
            if s["rule"] != alert["rule"]:
                continue
            if not all(alert.get(k) == v for k, v in s["match"].items()):
                continue
            if date.fromisoformat(s["expires"]) < today:
                notes.append(f"{s['id']} expired {s['expires']}: renew or delete it")
                continue
            why = f"suppressed by {s['id']} ({s['reason']}); goes into QA sample"
            return "auto-close", notes + [why]
        if alert["rule"] in policy["auto_contain"]["rules"]:
            recent[:] = [t for t in recent if created - t < timedelta(hours=1)]
            blocker = containment_blocker(alert["rule"], tier, policy, recent)
            if blocker is None:
                recent.append(created)
                return policy["auto_contain"]["action"], notes + [f"notify {owner}"]
            notes.append(f"containment needs a human: {blocker}")
    priority = PRIORITY[(alert["severity"], tier)]
    if priority in policy["page_outside_hours"] and after_hours(created):
        return f"page-on-call {priority}", notes + [f"outside hours; owner {owner}"]
    note = f"severity {alert['severity']}, asset tier {tier}"
    return f"triage-{priority}", notes + [note]


def main(policy_path, assets_path, alerts_path, today):
    policy = load_policy(policy_path)
    with open(assets_path) as handle:
        assets = json.load(handle)
    with open(alerts_path) as handle:
        alerts = json.load(handle)
    recent = []
    for alert in sorted(alerts, key=lambda a: a["created"]):
        action, notes = decide(alert, policy, assets, today, recent)
        print(f"{alert['id']} {alert['rule']:19} -> {action}")
        for note in notes:
            print(f"      {note}")


if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2], sys.argv[3], date.fromisoformat(sys.argv[4]))
  1. Save the guardrail tests as test_triage_policy.py:

Example status: executed on 2026-10-17 (Python 3.8.10).

import unittest
from datetime import date

from triage_policy import PRIORITY, check_policy, decide

POLICY = {
    "suppressions": [{"id": "S1", "rule": "r", "match": {"host": "laptop"},
                      "reason": "x", "owner": "o", "expires": "2026-01-01"}],
    "auto_contain": {"action": "isolate-host", "lowest_tier": 3, "max_per_hour": 1,
                     "min_decided": 20, "min_true_share": 0.9,
                     "rules": {"good": {"decided": 50, "true": 49},
                               "noisy": {"decided": 50, "true": 20}}},
    "page_outside_hours": ["P1"],
}
ASSETS = {"crown": {"tier": 1, "owner": "fin"}, "server": {"tier": 2, "owner": "ops"},
          "laptop": {"tier": 3, "owner": "it"}, "laptop2": {"tier": 3, "owner": "it"}}
TODAY = date(2026, 10, 16)
NIGHT, DAY = "2026-10-16T02:00", "2026-10-16T10:00"  # a Friday
MATRIX = {"high": ("P1", "P1", "P2"),  # the published matrix, asset tiers 1 to 3
          "medium": ("P2", "P2", "P3"),
          "low": ("P3", "P3", "P4")}


def alert(rule, host, severity="high", created=NIGHT):
    return {"rule": rule, "host": host, "severity": severity, "created": created}


class GuardrailTests(unittest.TestCase):
    def test_expired_suppression_does_not_close(self):
        action, _ = decide(alert("r", "laptop", "low"), POLICY, ASSETS, TODAY, [])
        self.assertEqual(action, "triage-P4")

    def test_suppression_must_be_scoped(self):
        whole_rule = dict(POLICY["suppressions"][0], match={})
        with self.assertRaises(ValueError):
            check_policy({"suppressions": [whole_rule]})

    def test_tier_1_and_tier_2_are_never_auto_isolated(self):
        for host in ("crown", "server"):
            action, _ = decide(alert("good", host), POLICY, ASSETS, TODAY, [])
            self.assertEqual(action, "page-on-call P1")

    def test_low_fidelity_rule_is_not_auto_isolated(self):
        action, _ = decide(alert("noisy", "laptop"), POLICY, ASSETS, TODAY, [])
        self.assertEqual(action, "triage-P2")

    def test_rate_limit_hands_over_to_a_human(self):
        recent = []
        first, _ = decide(alert("good", "laptop"), POLICY, ASSETS, TODAY, recent)
        second, _ = decide(alert("good", "laptop2"), POLICY, ASSETS, TODAY, recent)
        self.assertEqual((first, second), ("isolate-host", "triage-P2"))

    def test_unknown_asset_gets_no_automation(self):
        action, _ = decide(alert("good", "ghost", "low", DAY), POLICY, ASSETS, TODAY, [])
        self.assertEqual(action, "triage-P3")

    def test_priority_matches_the_published_matrix(self):
        for severity, row in MATRIX.items():
            for tier, expected in enumerate(row, start=1):
                with self.subTest(severity=severity, tier=tier):
                    self.assertEqual(PRIORITY[(severity, tier)], expected)


if __name__ == "__main__":
    unittest.main()
  1. Run python3 triage_policy.py policy.json assets.json alerts.json 2026-10-16, then python3 -m unittest -v test_triage_policy.

Expected output:

A-21 impossible-travel   -> auto-close
      suppressed by SUP-7 (synthetic VPN availability probe); goes into QA sample
A-22 malware-quarantined -> triage-P4
      SUP-3 expired 2026-09-30: renew or delete it
      severity low, asset tier 3
A-23 known-bad-hash      -> isolate-host
      notify workplace-it
A-24 encoded-powershell  -> triage-P2
      containment needs a human: only 3 decided alerts for this rule
      severity high, asset tier 3
A-25 known-bad-hash      -> page-on-call P1
      containment needs a human: asset tier 2
      outside hours; owner platform-eng
A-26 known-bad-hash      -> isolate-host
      notify workplace-it
A-27 known-bad-hash      -> triage-P2
      containment needs a human: limit of 2 isolations per hour reached
      severity high, asset tier 3
A-28 new-admin-account   -> page-on-call P1
      unknown asset: treated as asset tier 1, no automation
      outside hours; owner unknown
test_expired_suppression_does_not_close (test_triage_policy.GuardrailTests) ... ok
test_low_fidelity_rule_is_not_auto_isolated (test_triage_policy.GuardrailTests) ... ok
test_priority_matches_the_published_matrix (test_triage_policy.GuardrailTests) ... ok
test_rate_limit_hands_over_to_a_human (test_triage_policy.GuardrailTests) ... ok
test_suppression_must_be_scoped (test_triage_policy.GuardrailTests) ... ok
test_tier_1_and_tier_2_are_never_auto_isolated (test_triage_policy.GuardrailTests) ... ok
test_unknown_asset_gets_no_automation (test_triage_policy.GuardrailTests) ... ok

----------------------------------------------------------------------
Ran 7 tests in 0.000s

OK

Validation. A-22 shows the expiry working: SUP-3 lapsed on 30 September, so a human gets the alert and a note to renew or delete the suppression. A-23 and A-26 are isolated: known-bad-hash was right in 45 of 46 decided alerts (98 %), above the 90 % bar. A-24 is not: encoded-powershell has three decided alerts and Lab 1’s true share of 33 %, so it qualifies only after tuning. A-25 is a proven rule on an asset-tier-2 build server, so a human decides, and as a P1 outside hours it pages on-call. A-27 hits the limit of two isolations an hour. A-28, from an unregistered asset, gets no automation and a crown jewel’s priority. The thresholds are illustrative; agree yours with asset owners. Troubleshooting. A suppression without an owner fails at load time with ValueError: suppression SUP-7 lacks owner, one with an empty match with ValueError: suppression SUP-3 lacks match (both tested by editing the file); KeyError: 'tier' means an incomplete asset record. Extension. Read the rule statistics from Lab 1’s output and let them expire after 90 days. Teardown. Delete the five files.

1. Target, 2013: alerts without operations (sourced). The Senate Commerce Committee majority staff report, built on press reports and expert analyses, says (citing Bloomberg Businessweek) that Target’s malware-detection system “triggered urgent alerts with each installation of the data exfiltration malware”, that the security team “neither reacted to the alarms nor allowed the FireEye software to automatically delete the malware”, and that the alert carried the generic name “malware.binary”[7]. Target’s antivirus “also detected malicious behavior” on the same server. The gap was response, not detection; the report hedges (“appears to have failed”) and could not see Target’s forensics. SOC Ops lessons: a high-severity malware alert on an internal server needs a triage SLO and an owner; generic alert names slow triage (ASD recommends names with links to ATT&CK phases[14]); declining automatic blocking needs a compensating human response time.

2. US State Department, 2023: a custom alert taken seriously (sourced). The Cyber Safety Review Board reports that a State SOC analyst saw multiple alerts from a custom rule, “Big Yellow Taxi”, built on the MailItemsAccessed audit log. “Though the alerts showed activity that could have been considered normal—and, indeed, State had seen false positive Big Yellow Taxi detections in the past—State investigated these incidents and ultimately determined that the alert indicated malicious activity”; State triaged it as a moderate-level event and contacted Microsoft on 16 June 2023[8]. The Board credits both halves: State “found Storm-0558 because it had purchased enhanced logging through the G5 licenses”, which few, if any, other victims had, yet “Just purchasing the additional logging alone would not have been enough”; State’s SOC used the data to build custom rules from three years of experience with anomalous mailbox access[8]. SOC Ops lessons: a rule’s false-positive history is a reason to improve it, not to stop reading it; and the data you can afford to keep defines what the SOC can ever see (the Board recommended default logging without extra charge[8]).

3. Hybrid coverage for a mid-size firm (illustrative scenario). Context: 800 staff in one time zone, a four-person security team, nobody watching at night (Lab 1’s pattern). Method: in-house triage and investigation from 08:00 to 18:00; an MDR provider for nights and weekends with written authority to isolate asset-tier-3 endpoints (user devices) and disable non-admin accounts; P1s escalated to an in-house on-call lead within 15 minutes; one case system and one pass-down log at 08:00 and 18:00. Trade-offs: MITRE warns that a provider may understand the mission less well and that outsourcing creates friction in the feedback loop[1]. Success measures: after-hours P1 time to triage (p90), provider escalations accepted, handover defects. Pitfall: a contract that measures the provider on time to close.

4. Alert flood after an EDR rollout (illustrative scenario). Context: a new EDR suite on 3,000 endpoints triples the queue in week one. MITRE describes the pattern for SIEM: the SOC “begins to recognize all the activity in its logs that previously went unnoticed”, so workload can rise rather than fall[1]. Method: freeze new rules for two weeks; rank rules by analyst minutes, not alert count; fix the top five with context or scoped suppressions; sample 10 % of auto-closed alerts weekly. Success measures: analyst minutes per true positive, share of alerts closed with a reason, queue age at shift end. Pitfall: blanket suppression of a whole rule family, which removes the noise and the signal together.

Only half of the SOCs in a SANS survey cited by MITRE in 2022 had a formal metrics program[1]. ENISA’s list of typical SOC KPIs runs from detection speed and coverage to false-positive rates, escalations and workload per incident[2]. The table turns such KPIs into definitions you can compute from the case record.

KPI Definition and formula Data source Pitfall Cadence
Time to triage First human action − created; report p50 and p90 per priority and shift Case history (first change by a person) Automation that touches the case first fakes the clock; untouched cases vanish from percentiles, so count them past target as breaches Daily, weekly
Triage SLO attainment Alerts triaged within SLO ÷ alerts, per priority and shift Case timestamps One estate-wide number hides the night Weekly
Queue age at handover Age of the oldest untriaged alert at each shift change Pass-down log, case tool Not collected unless the handover asks for it Every shift
Alert quality per rule True share = TP ÷ (TP + BP + FP); noise minutes = handling minutes on BP + FP Dispositions, handling time Undetermined closures hide uncertainty; report them separately Weekly with detection engineering
Closed with a reason Alerts closed with disposition and note ÷ alerts closed Case tool Free-text “FP” with no reason counts as no reason Weekly
Escalation acceptance (tier 2) Tier-1 escalations confirmed by tier 2 ÷ tier-1 escalations Case links Very high can mean tier 1 escalates too little Monthly
Reopen rate Cases reopened ÷ cases closed Case history Rises when time-to-close targets bite Monthly
Time to hand off Hand-off to the IR lead − created, for cases that were declared; also count declined hand-offs Case and incident records Small numbers; use medians and named cases. ResponseOps’ time to declare adds the IR lead’s decision Monthly
Time to contain (pre-approved actions) Containment action − decision time SOAR or EDR audit log Different from incident recovery; do not call it “MTTR” Monthly
Time to detect (dwell time; often reported as MTTD) Detection − earliest evidence of compromise, for confirmed incidents Post-incident timelines Known only after investigation; tiny samples Quarterly
Source health Critical sources reporting within their expected interval ÷ critical sources SIEM health data Counts sources, not fields; parsing failures look healthy Daily
Analyst load Handling minutes ÷ analyst minutes on shift; overtime; attrition Case tool, rosters Measuring individuals invites gaming Monthly

Which “R”? MTTR can mean respond, recover, repair or resolve, so this page names the interval instead (time to hand off, time to contain); the caveats of all MTTx measures are taught on IncidentOps. MITRE’s SOC example: one email left unanswered for two days “can demolish a mean time to respond metric” even if a hundred others were answered within an hour, so prefer medians and percentiles[1].

“False-positive rate” is a misnomer in most SOC reports. In statistics the false-positive rate is FP ÷ (FP + TN), and a SOC never sees its true negatives. What SOC dashboards usually report is the share of alerts that were not true positives: the false discovery rate, one minus precision. With four dispositions, count benign positives on the non-true side, as DetectionOps’ rule precision does, and keep undetermined out of both. AIOps defines precision and recall once; AIOps for Security explains why base rates make low precision normal in security.

Time to detect needs context. Mandiant’s M-Trends 2026 reports a global median dwell time of 14 days for 2025 investigations, up from 11, and internal detection in 52 % of cases, up from 43 %[35]. These are intrusions Mandiant was hired to investigate, so use them as context, not as a target. MITRE warns that time to detect “can be used as a weapon against the SOC” (“why didn’t you see the APT for three months?”)[1].

Vanity metrics and gaming. MITRE lists behaviours that badly chosen metrics cause, among them “Closing tickets out before investigation is concluded” and “Opening and closing tickets to boost productivity numbers”, and recommends quality controls on any metric that incorporates time[1]. Counts of alerts handled, events ingested and rules deployed are activity, not outcomes; report them only next to quality measures.

Figure 6. How SOC metrics feed decisions
yesyesyesCase records: timestamps,dispositions, notesWeekly metrics packNoisy rule?Slow shift?Silent source?Tuning ticket to DetectionOpsStaffing or coverage changeFix ticket to SecDataOpsFewer, better alerts
Diagram source (Mermaid)
flowchart TB
  CASES["Case records: timestamps, dispositions, notes"] --> WEEK["Weekly metrics pack"]
  WEEK --> Q1{"Noisy rule?"}
  WEEK --> Q2{"Slow shift?"}
  WEEK --> Q3{"Silent source?"}
  Q1 -->|"yes"| T["Tuning ticket to DetectionOps"]
  Q2 -->|"yes"| S["Staffing or coverage change"]
  Q3 -->|"yes"| F["Fix ticket to SecDataOps"]
  T --> ALERTS["Fewer, better alerts"]
  S --> ALERTS
  F --> ALERTS
  ALERTS --> CASES
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class CASES,WEEK,ALERTS core
  class Q1,Q2,Q3 decision
  class T,S,F neutral

Text equivalent: Case records feed a weekly metrics pack that asks three questions: is a rule noisy, is a shift slow, is a source silent? A noisy rule produces a tuning ticket to DetectionOps, a slow shift a staffing or coverage change, a silent source a fix ticket to SecDataOps. Each aims at fewer, better alerts, which produce the next month’s case records.

The SOC is a target. Its tools map the estate, hold credentials for response actions and reveal what is and is not watched. MITRE: “A SOC can execute its mission in part because the adversary does not know where or how monitoring and response capabilities operate”[1]. Practical controls: separate administrative identities and phishing-resistant MFA for SOC tools; SOC workstations and management planes outside the general domain where feasible[1]; least-privilege roles in SIEM and SOAR, with playbook changes reviewed like code; and an audit log of analyst searches.

Privacy and lawful monitoring. NIST CSF 2.0 includes monitoring of “Personnel activity and technology usage” (DE.CM-03)[12], and SP 800-61 Rev. 3 expects legal experts to review procedures for compliance with laws “including the right to privacy”[13]. NCSC suggests written operating principles, such as searching sensitive data only “to detect malicious, suspicious or abusive behaviour”, and segregated insider-threat tooling[3]. Employee-monitoring law differs by country and sector; ask your legal and privacy teams.

Evidence and retention. Case records, pass-down logs and SOAR audit trails are evidence; keep them immutable and time-synchronised. CIS Safeguard 8.10 sets a minimum of 90 days of audit-log retention[25]. ASD and partners suggest starting with at least one year for administrative and security logs and 90 days for informational logs, tied to the organisation’s time to detect[14]. M-Trends 2026 shows why: it describes BRICKSTORM intrusions with dwell times of nearly 400 days, against which “standard 90-day log retention policies leave organizations completely blind”[35]. Pipelines and storage tiers are SecDataOps territory; the SOC states what it needs.

Compliance. Frameworks such as CIS Controls ask for log review “on a weekly, or more frequent, basis” (8.11)[25]; a SOC exceeds that easily, but audit evidence must show it. Mapping SOC outputs to audit evidence is ComplianceOps work.

People dominate cost. MITRE estimates that taking a SOC from 8x5 to 24x7 “requires an increase of at least eight FTEs”[1]. ASD and partners add licensing or data-use costs, scarce specialists, training, governance and maintenance, and note that teams running their own SIEM or SOAR “should expect to dedicate multiple staff to these tasks on a full-time basis”[14].

Unit costs that help decisions. Cost per alert handled is easy and misleading; analyst minutes per true positive and cost per covered hour (24x7 versus 12x5 versus provider nights) support real choices. Data cost per source is a SecDataOps conversation: a source that never informs a decision is a candidate for cheaper storage.

Burnout is a capacity problem. A six-month anthropological study of a corporate SOC by Sundaramurthy and colleagues concluded that “burnout is a human capital management problem resulting from the cyclic interaction of a number of human, technical, and managerial factors”, naming skills, empowerment, creativity and growth as the factors to manage[20]. Alahmadi and colleagues found that alarm validation “is a tedious task that can cause alarm burnout and eventually desensitization”[19]. MITRE’s levers: rotate between day and night every couple of months, give night shifts focused project work and feedback, pay fair market value, and let analysts automate what they have mastered[1].

  • Case quality review. Each week a shift lead re-reviews a sample of closed alerts per analyst and of auto-closed alerts. MITRE: “Auditing or reviewing some alerts to ensure proper handling can help address any potential gaming of the metrics”[1].
  • End-to-end tests. Run a harmless test technique on a lab host (with DetectionOps) and measure the chain: did the alert fire, was it triaged in time, was the package complete? ASD recommends exercises that test SIEM and SOAR performance[14]; MITRE lists red, purple and breach-and-attack-simulation tests[1].
  • Tabletop exercises for the hand-off to ResponseOps and for provider escalations.
  • Maturity self-assessment once a year with the same model each time. SOC-CMM’s own guidance says “Self-assessment, however, has limited objectivity” and offers third-party assessment[36].
  • Provider assurance. A monthly service review with sampled cases, escalation timings and missed items (Templates).
Symptom Likely cause Diagnosis Remediation Prevention
Queue grows every week New sources or rules without tuning; the SIEM “S-curve”[1] Analyst minutes per rule; arrivals by source Freeze new rules briefly; fix the top five by minutes Acceptance criteria for new detections with DetectionOps
Night alerts wait until morning No out-of-hours coverage, or nobody to act Time to triage by shift (Lab 1) Provider nights, on-call, or extended hours A documented coverage decision with its accepted risk
Alerts closed “FP” with no reason Time-to-close pressure; no disposition taxonomy Weekly QA sample Mandatory disposition and note Pair speed targets with quality checks
Detections silently stop Source silent, parser broken, connector credential expired Source-health check; last event per source See the runbook Heartbeat thresholds per critical source
Escalations or hand-offs bounce back Missing evidence; unclear incident criteria Acceptance rates; rejected samples Escalation package template Written criteria (CIS 17.9[16]); coaching
Provider escalates everything (or nothing) Contract measures volume or closure speed Sampled cases in the service review Re-baseline SLAs on escalation quality Contract checklist in Templates
Automation disrupts business Playbook without guardrails or tests SOAR audit log Roll back; limit to proven rules and asset-tier-3 devices Tested guardrails and rate limits (Lab 3); change review
Analysts leave within two years Monotony, no growth, night isolation Attrition, overtime, exit interviews Rotation, automation projects, career paths Burnout treated as a capacity risk

Worked runbook: a critical log source goes silent overnight

Link to section “Worked runbook: a critical log source goes silent overnight”

ASD and partners put it plainly: “The absence of logs from systems should be a concern to organisations”, and organisations “should have processes in place to identify when systems have stopped generating logs and telemetry”[14]. A silent source does not raise an alert by itself, so the SOC must look for the silence. This runbook is the shift-level response to a source outage; for a single rule that goes quiet while its data still flows, follow DetectionOps’ runbook for a rule that went silent.

Example status: KQL for Microsoft Sentinel, written for this page; parsed and semantically checked on 2026-10-17 with Kusto.Language (as in the reference implementation); not executed (requires a workspace). Table names are examples; replace them and the thresholds with your critical sources.

let expected = datatable(SourceTable: string, MaxSilentMin: int) [
    "SigninLogs", 30,
    "DeviceProcessEvents", 15,
    "AWSCloudTrail", 30
];
let arrivals = union isfuzzy=true
    (SigninLogs | where TimeGenerated > ago(1d)
        | summarize LastIngested = max(ingestion_time()), LastEvent = max(TimeGenerated)
        | extend SourceTable = "SigninLogs"),
    (DeviceProcessEvents | where TimeGenerated > ago(1d)
        | summarize LastIngested = max(ingestion_time()), LastEvent = max(TimeGenerated)
        | extend SourceTable = "DeviceProcessEvents"),
    (AWSCloudTrail | where TimeGenerated > ago(1d)
        | summarize LastIngested = max(ingestion_time()), LastEvent = max(TimeGenerated)
        | extend SourceTable = "AWSCloudTrail");
expected
| join kind=leftouter arrivals on SourceTable
| extend SilentMin = datetime_diff('minute', now(), LastIngested)
| where isnull(LastIngested) or SilentMin > MaxSilentMin
| project SourceTable, LastIngested, LastEvent, SilentMin, MaxSilentMin

The query measures silence by ingestion time (“is anything arriving?”) and shows the newest event time beside it, since event time lags by the source’s normal delay (Azure activity logs can take 3 to 20 minutes[37]). With isfuzzy=true, a table that no longer exists, say from a removed connector, is skipped rather than failing the query[38], and the join reports it as silent.

Figure 7. Runbook flow for a silent log source
yesnonoyesSource-health check: sourcesilentEvents visible at the source?Pipeline problem: page SOCengineerSource problem or outage:contact owner or vendorRecord blind spot anddependent detectionsCompensate with othertelemetry and manual checksData flowing again?Re-run dependent detectionsover the gapClose: cause, gap window,prevention
Diagram source (Mermaid)
flowchart TB
  A["Source-health check: source silent"] --> B{"Events visible at the source?"}
  B -->|"yes"| C["Pipeline problem: page SOC engineer"]
  B -->|"no"| D["Source problem or outage: contact owner or vendor"]
  C --> E["Record blind spot and dependent detections"]
  D --> E
  E --> F["Compensate with other telemetry and manual checks"]
  F --> G{"Data flowing again?"}
  G -->|"no"| F
  G -->|"yes"| H["Re-run dependent detections over the gap"]
  H --> I["Close: cause, gap window, prevention"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class A risk
  class B,G decision
  class C,D,E,F,H,I core

Text equivalent: A source-health check finds a silent source. If events are visible at the source, it is a pipeline problem and the SOC engineer is paged; if not, the owner or vendor is contacted. Either way the blind spot and dependent detections are recorded, and the shift compensates with other telemetry until data flows again. Then the dependent detections are re-run over the gap window, and the case is closed with the cause, the gap window and a prevention action.

  • “Buy a SIEM, get a SOC.” Tools create questions; people with time, authority and playbooks answer them.
  • “Collect everything.” ASD and partners observe that teams more often collect too much than too little, which “makes it more difficult to develop effective rules and filters”[14]. Collect for a detection or an investigation need.
  • “Tier 1 is dead.” Automation shrinks triage; it has not removed the need for a human to evaluate machine output[1].
  • “Faster closure means a better SOC.” Time-to-close targets breed hasty closure; MITRE lists the behaviours they cause[1].
  • “Suppress it for now.” A suppression without an owner and an expiry date is a permanent blind spot.
  • “A real SOC must be 24x7.” MITRE notes the stigma some organisations attach to not running around the clock[1], and NCSC points out that a 9-to-5 operation “still provides 100% more monitoring than having no SOC at all”[3]. Choose hours from risk and from who can act at night.
  • Ranking analysts by alerts closed. It rewards the wrong work and drives people out; measure the team’s outcomes and coach on sampled cases.

MITRE frames the choice well: “The question is not if the constituency requires security monitoring and response capabilities, but how it will procure them”[1].

Build or extend in-house SOC operations when you run your own IT, have many thousands of devices or accounts, a security leader such as a CISO, detection needs that standard services do not meet, and the will to fund several dedicated people for years (MITRE’s criteria[1]). ASD and partners also recommend an in-house capability for organisations that manage sensitive information or provide critical or unique services[14].

Prefer a provider (MSSP or MDR) when you have fewer than a couple of thousand devices and a few thousand accounts, little security investment, budget for no more than a couple of security staff, and an immediate need (MITRE’s criteria[1]). Hybrid models, such as in-house days with provider nights, fit the split cases.

Do not start a SOC yet when:

  • The basics are missing. If logs are not collected, centralised and time-synchronised (CIS Control 8[25]), a SOC will watch nothing. Fix logging first.
  • Nobody can act on what the SOC finds. NCSC’s warning about IT functions covered only 9-to-5 applies (see Operating model and governance); agree authority and on-call first.
  • The environment is small and secure by design. NCSC: “For environments that are secure by design, a ‘full-fat SOC’ is not always required”[21]. A managed EDR service with alerting to an on-call person may be proportionate.
  • You want SOAR before the SIEM works. ASD and partners rank effective log analysis above SOAR[14].

Hidden responsibilities. Even a fully outsourced SOC needs internal owners for the contract, asset and identity context, the authority matrix and on-call action, and someone who reads the monthly report critically.

Figure 8. Choosing a SOC sourcing model
noyesnoyesnoyesyesnoLogs centralised and anowner for response actions?Not yet: fix logging andauthority firstThousands of users orassets, and a security leader?Managed service: MDR orMSSPSpecific needs or highlysensitive data?Hybrid: in-house days,provider nightsFunding for 10 or more stafffor 24x7?In-house SOC, 24x7In-house core hours plusprovider or on-call
Diagram source (Mermaid)
flowchart TB
  A{"Logs centralised and an owner for response actions?"}
  A -->|"no"| X["Not yet: fix logging and authority first"]
  A -->|"yes"| B{"Thousands of users or assets, and a security leader?"}
  B -->|"no"| M["Managed service: MDR or MSSP"]
  B -->|"yes"| C{"Specific needs or highly sensitive data?"}
  C -->|"no"| H["Hybrid: in-house days, provider nights"]
  C -->|"yes"| D{"Funding for 10 or more staff for 24x7?"}
  D -->|"yes"| I["In-house SOC, 24x7"]
  D -->|"no"| E["In-house core hours plus provider or on-call"]
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class A,B,C,D decision
  class M,H,I,E core
  class X risk

Text equivalent: If logs are not centralised or nobody owns response actions, do not start yet: fix logging and authority. Otherwise, without thousands of users or assets and a security leader, use an MDR or MSSP service. With them but without specific detection needs or highly sensitive data, a hybrid of in-house days and provider nights fits. With such needs, an organisation that can fund ten or more staff runs an in-house 24x7 SOC; one that cannot runs in-house core hours with a provider or on-call rota outside them.

The tree simplifies. Score the options when the answer is close (weights are an example; agree your own):

Criterion (weight) In-house 24x7 Hybrid MDR / MSSP
Context about your business and systems (25 %) High Medium-high Low-medium
Time to useful coverage (15 %) Slow (months to hire) Medium Fast
Cost predictability (15 %) Medium (people) Medium High (contract)
Control over detections and data (20 %) High High Depends on contract
Resilience to staff turnover (10 %) Low-medium Medium Provider’s problem, until the contract ends
Fit with sensitive data and regulation (15 %) High Medium-high Check data location and access

MITRE adds two warnings to any provider choice: knowledge leaves when a contract ends, and a contract needs ratios that show “how stretched their analysts may be”[1]. NIST SP 800-61 Rev. 3 asks that provider responsibilities “be clearly defined in a contract”, including “restrictions on what the service provider can do”[13].

This is this Atlas’s model. It is not SOC-CMM, which scores six maturity stages from 0 (non-existent) to 5 (optimizing) across 27 aspects in five domains, plus capability levels for technology and services[39][5]. Use SOC-CMM for a formal assessment and the table to choose the next step.

Level Observable behaviour Evidence Next step
1. Reactive Alerts in several consoles, checked when someone has time; no hours defined No timestamps; closures without reasons One queue; dispositions; published hours
2. Managed queue One queue; playbooks for main alert families; triage SLOs; handover log SLO attainment measured; pass-down log Weekly tuning; source-health checks
3. Measured Metrics by shift and priority; dispositions drive tuning; QA sampling; documented coverage Monthly pack; tuning tickets closed; QA findings End-to-end tests; guarded automation
4. Tested Detection-to-hand-off chain tested regularly; automation with owners, expiry and tests; provider SLAs evidenced Test results; SOAR change reviews Threat-informed coverage; capacity planning
5. Adaptive Coverage driven by threat profile and tests; hunting feeds detections; metrics drive investment Coverage reviews tied to budget Stay proportionate; re-assess yearly

SOC-CMM’s advice applies here too: aim for the right level, “which is not necessarily the highest level”[39].

Phase Actions Owner Milestone and metric
Days 1–30 Charter with authority to contain; hours and on-call; all alert sources into one queue; four disposition values; playbooks for the top three alert families; pass-down log SOC manager, CISO Every alert has a timestamped case; baseline time to triage by shift
Days 31–60 Triage SLOs per priority; weekly tuning review; source-health checks; QA sampling; night-coverage decision SOC manager, shift leads, detection engineering SLO attainment reported; top five noisy rules tuned; coverage decision signed
Days 61–90 Automate enrichment and two low-risk closures (owner, expiry); first end-to-end test and tabletop with ResponseOps; first service report; SOC-CMM self-assessment SOC engineer, SOC manager Analyst minutes per true positive falling; test results recorded; maturity baseline

Quick wins: one queue, mandatory dispositions, and a pass-down log cost almost nothing and make every later decision measurable. Dependencies: asset and identity context (often from IT), a change calendar (SecOps), and logging (SecDataOps).

  • Every alert source routes into one case queue with created and first-action timestamps
  • Four disposition values agreed and mapped to your tool’s classifications
  • Published hours of coverage, and who is called outside them
  • Written authority: which containment actions, on which assets, by whom, without asking
  • Incident criteria written down, and the hand-off to the IR lead agreed
  • A pass-down log template in use from the first shift
  • Pass-down log read and accepted; open P1 and P2 cases have named owners
  • Source-health view checked; silent sources recorded as blind spots
  • Queue age noted; anything older than its SLO raised with the shift lead
  • On-call contacts for responders and critical asset owners confirmed for the shift
  • Time to triage p50 and p90 by priority and shift; SLO attainment
  • Top five rules by analyst minutes on benign and false positives; tuning tickets raised
  • Suppressions expiring in the next 14 days reviewed; expired ones renewed with a reason or deleted
  • QA sample reviewed (closed and auto-closed alerts); coaching notes recorded
  • Escalation acceptance and rejected escalations reviewed with tier 2
  • Night-time arrival rate and P1 count known (Lab 1); staffing estimated (Lab 2)
  • Two analysts per shift, or a provider with documented night staffing
  • Someone with authority over critical systems reachable at night
  • Night-shift feedback, rotation and project work planned
  • Telemetry the provider receives, and what it cannot see
  • Actions the provider may take alone, per asset tier; actions it must request
  • Escalation contacts, channels and times per priority; tested with a drill
  • Shared case system or a defined case exchange; joint pass-down at each handover
  • Monthly service review with sampled cases, not only a dashboard
  • Fallback queue if the SIEM or case tool is down, and how its records rejoin the case system
  • Out-of-band contacts and channel independent of corporate email, chat and identity
  • Break-glass access to SOC tools that survives a compromise of the identity provider
  • Owner notifications for disruptive actions sent by a path the adversary is unlikely to read
  • A named fallback when a shift cannot be staffed

1. SOC charter (one page). Elements follow MITRE’s list of charter contents[1].

Example status: template; not executed.

SOC CHARTER                           Version: 1.0   Signed by: <chief executive>
Mission: detect, analyse and coordinate response to cybersecurity incidents for
         <constituency: organisations, sites, users, systems, cloud tenants>.
Services: alert triage (hours), investigation (hours, on-call), containment
          (pre-approved list), user reports, monthly reporting.
Authorities, within the constituency:
  - deploy and operate monitoring on networks, hosts and cloud services
  - scan for configuration and vulnerability status
  - apply or direct countermeasures: isolate hosts, disable accounts,
    block network connections (per the authority matrix)
  - collect, retain and analyse logs and other digital artefacts lawfully
Support expected from: IT operations, help desk, asset owners, NOC, providers.
Governance: steering group <names>, metrics reported monthly, review yearly.
Out of scope: HR investigations without a referral, physical security.

2. Escalation package (minimum fields).

Example status: template; not executed.

ESCALATION  Case: ____  Priority: P_  Raised by: ____  Time (UTC): ____
What fired:        rule name, ATT&CK technique if known, first and last seen
Who and what:      users, hosts, cloud resources, asset tier and owner
What I checked:    history, change calendar, related alerts, intelligence
Evidence:          links to searches, screenshots, file hashes, log excerpts
My assessment:     hypothesis, confidence (low/medium/high), what would refute it
Done so far:       containment taken (by whom, when) or why none
Ask:               decision needed, from whom, by when

3. Tuning request (from SOC to DetectionOps).

Example status: template; not executed.

TUNING REQUEST    Rule: ____   Period: ____   Raised by: ____
Volume: __ alerts, __ benign, __ false, __ true, __ undetermined
Analyst time on non-true alerts: __ min
Pattern: what the benign/false alerts have in common (with case links)
Proposed change: context, threshold, scope, suppression (owner, expiry)
Risk if changed: which true behaviour could be missed; how we will test it

4. MDR or MSSP contract checklist. Drawn from MITRE’s outsourcing guidance[1] and NIST SP 800-61 Rev. 3[13].

Clause What good looks like
Scope of telemetry Named sources and assets; gaps listed
Authority to act Actions per asset tier; what needs your approval; restrictions written down
Escalation SLAs Notification times per priority, measured from alert creation; escalation content defined
Quality, not just speed Sampled case reviews; no time-to-close targets that reward weak investigation
Analyst capacity A ratio that shows how stretched analysts are; attrition and training disclosed
Data protection Where data is stored, who can access it, sub-processors, vetting
Exit Export of cases, detections, playbooks and knowledge at contract end
Joint governance Monthly service review, quarterly tabletop, named contacts

5. Monthly SOC scorecard.

Measure This month Target Trend Comment
P1 time to triage, p90 (day / out-of-hours) 15 min / 30 min
SLO attainment, all priorities 95 %
Closed with a reason 100 %
True share, top 10 rules per rule
Analyst minutes per true positive falling
Escalation acceptance (tier 2) 60–85 % Too high may mean under-escalation
Critical sources healthy 100 %
Expired suppressions 0
Overtime hours / attrition

The targets are examples for a mid-size SOC; agree yours with the people doing the work, as MITRE recommends for SLOs[1].

SOC Ops (this page) CSIRT NOC IncidentOps SecOps
Core question Is this alert malicious, and who acts? How do we handle this incident? Is the network up and performing? Who commands any incident? How do security and IT operations work to one contract?
Trigger Alerts and reports Declared incidents, external reports Availability and performance events Any service-affecting incident Findings, baselines, exceptions
Typical output Dispositions, hand-offs Containment, coordination, notifications Restored service Incident roles, communications Owned remediation work
Overlap In smaller organisations, SOC and CSIRT are often treated as one[2] Often includes monitoring Sometimes shares the night floor Commands service-affecting incidents; security work goes to ResponseOps Supplies logs, change calendar, authority

MSSP and MDR are market labels without standard definitions: MSSPs tend to be device- and alert-centric (scale, generic tuning), MDR providers investigation- and response-centric (containment included; authority, data and exit questions). Check the contract, not the label; in-house and hybrid models are compared in the decision framework.

Is a SOC the same as a SIEM? No. A SIEM is a tool; a SOC is a team with authority and processes. NIST recommends SIEM and SOAR for continuous monitoring[13]; neither decides anything.

How many analysts do we need? Start from arrival rate, handling time and hours of coverage (Lab 2), then add leads, tier 2 and engineering. Ratios such as analysts per thousand devices have high variance; MITRE concludes that SOC staffing needs “require a more complicated equation than a simple ratio”[1].

Should every alert be triaged, even low severity? Every alert that reaches a human queue, yes; that is why the queue must be curated. Alerts nobody will read should become reports or searches, or be retired with DetectionOps.

When does an alert become an incident? When it meets your written incident criteria (NIST CSF DE.AE-08[12]). In this Atlas’s proposal the SOC hands it off, the IR lead (ResponseOps) records the declaration, and the SOC stays involved for scoping and monitoring, logged on the incident record, not on the closed case.

Can AI replace tier 1? Not on current evidence. AI can summarise, enrich and draft; someone must still own the decision and its errors (AIOps for Security).

  • AI agents in triage. Vendors market assistants that summarise cases and propose responses[28]. Summarising is an established capability, autonomous triage is in active development, and claims beyond that are marketing until independently evaluated (what do they miss, at what base rate, under attack?).
  • Platform consolidation. SIEM, SOAR, XDR and case management increasingly ship as one platform (Microsoft is moving Sentinel into the Defender portal by March 2027[27]); portability of detections and case history becomes a selection criterion.
Stage What to do Outcome
Prerequisites Networking, Windows and Linux logs, identity basics; read SecOps You can read a sign-in log and a process event
1-day primer This page’s first two parts; MITRE’s 11 Strategies fundamentals chapter[1]; NCSC’s SOC guidance[22] You can explain triage, dispositions and escalation
1-week practice Labs 1–3; install an open-source SIEM such as Wazuh in a lab[30]; write two playbooks You can measure a queue and write a guarded automation
30 days Shadow shifts; run a weekly tuning review; draft a charter and a pass-down template for your team You can run a shift and improve the queue
Specialise DetectionOps, ThreatOps, ResponseOps, SecDataOps Depth in one neighbouring discipline
Portfolio A metrics pack built from a real or synthetic export; an MDR contract review; a staffing model for 24x7 Evidence of SOC management skill

Leaders: read Deciding and adopting, the judgement call below, and MITRE’s strategies 2, 3 and 10 (authority, structure, metrics)[1].

Area Remember
What SOC Ops is running the SOC as a service: queue, people, shifts, metrics, sourcing
Boundaries Detections → DetectionOps; incidents → ResponseOps; data → SecDataOps; intelligence → ThreatOps; IT contract → SecOps
Decide Hours of coverage, authority to contain, incident criteria, build or buy
Practise One queue, four dispositions, playbooks per alert family, escalation packages, signed handovers, weekly tuning
Measure Time to triage p50/p90 by shift and priority, true share per rule, analyst minutes per true positive, escalation acceptance, source health
Staff About 5 FTE per 24x7 seat; two on shift; rotation and growth
Automate Enrichment and grouping first; closures with owner, expiry and sampling; auto-containment only for proven rules on asset-tier-3 devices, rate-limited; critical assets only with a human
Watch out Unfiltered feeds, suppressions without expiry, time-to-close targets, silent sources, one analyst alone at night, outsourcing as abdication
Next Measure your night (Lab 1), size it (Lab 2), guard your automation (Lab 3)

A judgement call: a night shift of our own, or MDR for nights?

Link to section “A judgement call: a night shift of our own, or MDR for nights?”

After this page you should be able to explain what SOC operations covers and where it hands over; design a triage workflow, priority matrix and handover; operate a queue with dispositions, tuning and guarded automation; and evaluate staffing, metrics and sourcing choices.

1. Why keep “benign positive” separate from “false positive”?

They need different fixes: a benign positive (detection worked, activity legitimate) needs context such as an allow-list or a scoped suppression; a false positive (logic or data wrong) needs a detection fix.

2. Your overall median time to triage is 7 minutes, but after-hours alerts wait a median of 512. What do you report and decide?

Report time to triage by shift and priority, never one estate-wide median. The decision is a coverage decision: extended hours, provider nights, on-call paging for P1, or an explicitly accepted risk signed by the executive who owns it.

3. Your MDR contract measures the provider on mean time to close under 30 minutes. What is wrong?

It rewards closing quickly, not deciding correctly; MITRE warns that such SLAs push analysts toward weak investigation (principle 4). Measure notification time for serious alerts, escalation quality and sampled cases, with medians and percentiles.

4. How many FTE do two round-the-clock analyst seats need if each FTE gives 1,736 productive hours a year?

2 × 168 × 52 ÷ 1,736 ≈ 10.1 FTE, before shift leads, tier 2, engineers and managers. MITRE’s estimate is roughly 10 to 11.

5. A rule produced 400 benign positives and no true positive in three months. Delete it?

Not automatically. Ask detection engineering what it should catch and whether context (for example known VPN egress points) would separate the benign cases. Retire it only if the behaviour is covered elsewhere or the risk is accepted, and record why.

6. Sign-in logs were silent from 01:40 to 04:10; data now flows again. What is left to do?

Check for late events, re-run the dependent detections over the gap window (ask ThreatOps to hunt it if anything turns up), record gap and cause, report it, and add a heartbeat or credential-expiry alert.

7. Which Atlas page owns each task: writing a new detection rule; eradicating a confirmed ransomware intrusion; onboarding a new SaaS audit log?

DetectionOps; ResponseOps; SecDataOps. SOC Ops triages the alerts, hands off the incident and reports the gap or need.

8. A vendor playbook isolates any host that triggers encoded-powershell. What do you check before enabling it?

The rule’s measured true share over enough decided alerts (Lab 1 shows 33 % on three), which asset tiers it may touch, a rate limit, owner notification, and tests proving each guardrail (Lab 3).

A SOC is often described by its tools, yet tools only raise questions. Target’s alerts reportedly went unanswered; the State Department’s premium audit log mattered because its SOC built a rule on it and investigated what the rule raised.

Run the SOC as a service with a queue: curate what reaches analysts, decide every alert with a recorded reason, hand over in writing, measure by shift and by rule, and feed every disposition back into better detections. Staff honestly, automate the recurring, reversible and proven first, and keep humans in charge of disruptive actions on what matters most. Buy help where it is cheaper or faster, but never the accountability. The test of SOC operations is not the number of alerts handled; it is whether the one alert that matters at 03:00 reaches someone who can act, and whether the next shift knows what they did.

Further reading: the two companion documents to the ASD practitioner guidance (executive guidance and Priority logs for SIEM ingestion), linked from the CISA overview page.

Changelog

  • 2026-10-16 — First version (author draft r0): term status confirmed established (as the coverage matrix proposed); 8 diagrams; Labs 1–3 executed (Python 3.8.10), KQL examples not executed; evidence in research/soc-ops-ledger.md.
  • 2026-10-17 — Revision r1 after the independent review (reviews/soc-ops-r1.md; response reviews/soc-ops-r1-response.md): case lesson matched to the CSRB; triage query on the first human change; Lab 3 rebuilt and re-run; hand-off and declaration aligned with ResponseOps; skills and continuity added; KQL parsed; figures 3–5 redrawn; repetition cut.
  • 2026-10-17 — Revision r2 after the second independent review (reviews/soc-ops-r1.md, second pass; response reviews/soc-ops-r1-response.md): asset tiers defined and named at every use; Lab 3 re-run; Figures 3–4 and FAQ adjusted; SOC-CMM, CIS and FIRST citations tightened; Sentinel “Updated by” alternative added.
  • 2026-10-17, post-score polish: MITRE knowledge-ownership paragraph; [^ms-auto] date; DetectionOps disposition wording; repeated call-backs cut; official MITRE and ASD URLs still blocked (ledger §H, L).

  1. 11 Strategies of a World-Class Cybersecurity Operations Center — Kathryn Knerler, Ingrid Parker, Carson Zimmerman, The MITRE Corporation, 2022 (2nd edition). mitre.org refused automated access; text checked in the Internet Archive copy of the PDF. Checked 2026-10-16; re-checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  2. How to set up CSIRT and SOC: Good practice guide — ENISA, 2020-12-10. Checked 2026-10-16. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  3. Building a SOC: Designing an operating model — UK National Cyber Security Centre, 2022-05-23. Checked 2026-10-16. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  4. CSIRT Services Framework, Version 2.1.0 — FIRST, released PDF (TLP:WHITE, November 2019; first framework published 2017); the HTML version is labelled “for review purposes”. Checked 2026-10-16; PDF checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  5. SOC-CMM model (v2.4) — SOC-CMM, live page. Checked 2026-10-16. ↩︎ ↩︎ ↩︎ ↩︎

  6. Security Operations Center (glossary entry) — NIST CSRC Glossary, live page (acronym entry only). Checked 2026-10-16. ↩︎

  7. A “Kill Chain” Analysis of the 2013 Target Data Breach, reprinted in S. Hrg. 113-531 — US Senate Committee on Commerce, Science, and Transportation, majority staff report, 2014-03-26. Checked 2026-10-16; re-checked 2026-10-17. ↩︎ ↩︎

  8. Review of the Summer 2023 Microsoft Exchange Online Intrusion — Cyber Safety Review Board, 2024-03-20. Checked 2026-10-16; re-checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  9. Security teams, roles, and functions — Microsoft Learn, Cloud Adoption Framework, updated 2025-12-04. Checked 2026-10-16. ↩︎ ↩︎

  10. System and Organization Controls: SOC Suite of Services — AICPA & CIMA, live page. Checked 2026-10-16. ↩︎

  11. SOC-CMM introduction — SOC-CMM, live page. Checked 2026-10-16. ↩︎

  12. The NIST Cybersecurity Framework (CSF) 2.0 — NIST CSWP 29, 2024-02-26. Checked 2026-10-16. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  13. SP 800-61 Rev. 3: Incident Response Recommendations and Considerations for Cyber Risk Management — NIST, April 2025. Checked 2026-10-16. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  14. Implementing SIEM and SOAR platforms: practitioner guidance — Australian Signals Directorate’s ACSC with CISA and international partners, 2025-05-27. cyber.gov.au timed out; text checked in the Internet Archive copy. Checked 2026-10-16; re-checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  15. Guidance for SIEM and SOAR Implementation — CISA, 2025-05-27. Checked 2026-10-16. ↩︎

  16. CIS Controls v8.1 Assessment Specification: Control 17, Incident Response Management — Center for Internet Security, live documentation (v8.1 per the specification’s home page). Checked 2026-10-16; version re-checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  17. Investigate incidents with Microsoft Sentinel (legacy) — Microsoft Learn, updated 2026-07-02. Checked 2026-10-16. ↩︎

  18. Manage incidents in the Microsoft Defender portal (legacy) — Microsoft Learn, updated 2026-07-02. Checked 2026-10-16. ↩︎

  19. 99% False Positives: A Qualitative Study of SOC Analysts’ Perspectives on Security Alarms — Bushra A. Alahmadi, Louise Axon, Ivan Martinovic, 31st USENIX Security Symposium, 2022. Checked 2026-10-16. ↩︎ ↩︎

  20. A Human Capital Model for Mitigating Security Analyst Burnout — Sathya Chandran Sundaramurthy et al., SOUPS 2015. Checked 2026-10-16. ↩︎ ↩︎

  21. Building a SOC: Things to consider — UK National Cyber Security Centre, 2022-05-23. Checked 2026-10-16. ↩︎ ↩︎

  22. Building a Security Operations Centre (SOC) — UK National Cyber Security Centre, published 2022-05-23. Checked 2026-10-16. ↩︎ ↩︎ ↩︎

  23. Manage your SOC better with incident metrics — Microsoft Learn, updated 2026-07-02. Checked 2026-10-16; re-checked 2026-10-17. ↩︎ ↩︎

  24. Automate threat response in Microsoft Sentinel with automation rules — Microsoft Learn, updated 2026-06-30 (ms.date 2024-10-16). Checked 2026-10-16; re-checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  25. CIS Controls v8.1 Assessment Specification: Control 8, Audit Log Management — Center for Internet Security, live documentation; the specification’s home page is titled “CIS Controls Assessment Specification for Controls v8.1”. Checked 2026-10-16; version re-checked 2026-10-17. ↩︎ ↩︎ ↩︎ ↩︎

  26. MITRE ATT&CK — The MITRE Corporation, live knowledge base. Checked 2026-10-16. ↩︎

  27. What is Microsoft Sentinel SIEM? — Microsoft Learn, updated 2026-01-28. Checked 2026-10-16. ↩︎ ↩︎ ↩︎

  28. Google Security Operations — Google Cloud, product page, undated. Checked 2026-10-16. ↩︎ ↩︎ ↩︎ ↩︎

  29. Elastic Security — Elastic, product page, undated. Checked 2026-10-16. ↩︎

  30. Getting started with Wazuh — Wazuh documentation, current version. Checked 2026-10-16. ↩︎ ↩︎

  31. TheHive — StrangeBee, product page, undated. Checked 2026-10-16. ↩︎

  32. SecurityInsightsIncidentClassification struct — Microsoft Learn, Azure SDK for .NET reference, live page. Checked 2026-10-16. ↩︎

  33. summarize operator — Microsoft Learn, Kusto Query Language reference, updated 2025-01-21. Checked 2026-10-17. ↩︎

  34. Erlang (unit): Erlang C formula — Wikipedia, live page (secondary source for a standard queueing formula). Checked 2026-10-16. ↩︎

  35. M-Trends 2026: Data, Insights, and Strategies From the Frontlines — Mandiant (Google Cloud), 2026-03-24. Checked 2026-10-16. ↩︎ ↩︎

  36. SOC-CMM methodology — SOC-CMM, live page. Checked 2026-10-16. ↩︎

  37. Log data ingestion time in Azure Monitor — Microsoft Learn, updated 2026-07-29. Checked 2026-10-17. ↩︎

  38. union operator — Microsoft Learn, Kusto Query Language reference, updated 2024-08-11. Checked 2026-10-17. ↩︎

  39. SOC-CMM maturity levels — SOC-CMM, live page. Checked 2026-10-16. ↩︎ ↩︎

Related Ops

SecOps

SecOps is an ambiguous label. Here it means the working contract between security and IT operations: agreed hardening baselines, one owned remediation queue with deadlines, governed exceptions and joint metrics. Others use it for SOC work or a Google product.

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.

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.

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.

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.

IncidentOps

IncidentOps is a rare, informal label for incident management: declaring, coordinating, communicating and learning from unplanned service disruptions. This page teaches that established practice, with a focus on human coordination under pressure.

VulnerabilityOps

VulnerabilityOps (usually "VulnOps") runs vulnerability management as a continuous flow: decide what must be fixed by when from exploitation evidence and asset exposure, hand dated work to owners, verify every fix, and keep remediation capacity ahead of arrivals.

Practise and get certified

From xOps School

Practise SOC Ops and get certified

The free SOC Ops 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)