---
title: "SOC Ops"
slug: soc-ops
category: security
term_status: established
evidence_status: verified
summary: "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."
audience: [SOC analysts and shift leads, SOC managers, CISOs and security leaders, Buyers of MDR services, Beginners]
last_verified: 2026-10-17
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."
related: [secops, detectionops, responseops, threatops, secdataops, aiops-for-security, incidentops, vulnerabilityops]
---

## What SOC Ops is

### In 30 seconds

**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"[^mitre11]. 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](#deciding-and-adopting).

::: note Term status: established (the function), informal (the label)
"SOC Ops" is shorthand for SOC operations. The function is well established: MITRE publishes a 2022 handbook on running a
SOC[^mitre11], ENISA a good-practice guide[^enisa], the UK NCSC a design guide[^ncsc-om], and FIRST a services framework noting that
larger organisations sometimes assign its "Information Security Event Management" service area to a SOC[^first]; an open maturity
model exists (SOC-CMM)[^soccmm-model]. NIST's glossary lists SOC only as an acronym[^nist-gl]. None of these bodies defines the label
"SOC Ops"; this Atlas uses it for the operating discipline.
:::

### At a glance

| 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](../detectionops/)), the incident response lifecycle ([ResponseOps](../responseops/)), intelligence and hunting ([ThreatOps](../threatops/)), telemetry pipelines ([SecDataOps](../secdataops/)), the security–IT operations contract ([SecOps](../secops/)) |
| Prerequisites | Basic networking and operating systems; what an alert, a log and an incident are; [SecOps](../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 |

### The problem it solves

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"[^senate-target]. 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[^csrb]. The review board found that both the log and the analysis were needed (both cases are in
[Use cases and scenarios](#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"[^mitre11]). NCSC's guidance warns that pigeonholing
analysts "often leads to issues, like triage fatigue, poor job satisfaction and therefore poor security"[^ncsc-om].

### What it is not: boundaries and naming

- **Not detection engineering.** Writing, testing and retiring detection rules is [DetectionOps](../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](../responseops/); incident command and MTTx definitions for any incident belong to [IncidentOps](../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](#roles-tiers-and-raci)).
- **Not threat intelligence or hunting** ([ThreatOps](../threatops/)), and **not the telemetry pipeline** ([SecDataOps](../secdataops/)).
- **Not "SecOps" in the collaboration sense.** [SecOps](../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)"[^ms-caf].
- **Not a NOC.** MITRE distinguishes the SOC, "primarily looking for cyber attacks", from a network operations centre that operates
  and maintains equipment[^mitre11].
- **Not SOC 2.** "SOC" also stands for System and Organization Controls, the AICPA's suite of assurance reports on a service
  organisation's controls[^aicpa]. 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[^mitre11].

::: caution Editorial distinction
The split between SOC Ops, DetectionOps, ResponseOps, ThreatOps and SecDataOps follows the flow of security work so that each topic
is taught once. It is this Atlas's choice, not an industry standard. ENISA notes that "In smaller organisations, CSIRTs and SOCs are
often considered to be synonymous"[^enisa], and in a small team the same three people do all five jobs.
:::

## Where it comes from and where it sits

### Origins and evolution

MITRE dates the rise of SOCs to the 1990s[^mitre11]; ENISA dates the term CSIRT (computer security incident response team) to the
same decade and calls CERT "the oldest term"[^enisa]. 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"[^mitre11]. The
published body of SOC practice is younger:

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

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[^mitre11]. 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[^mitre11].

### Glossary and mental model

| 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)[^csf2]; CIS Safeguard 17.9 asks you to differentiate "between an incident and an event"[^cis17] |
| 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](../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](../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[^mitre11] |
| 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)"[^ms-caf]. 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](#reference-implementation-the-soc-operating-pack)). 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)[^asd] |
| 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"[^asd] |
| 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[^mitre11] |
| 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[^mitre11] |
| 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]"[^sp80061]. *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[^mitre11] |

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")[^ms-close], while the
Defender portal uses "True positive", "Informational, expected activity" and "False positive"[^ms-xdr]. 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"[^alahmadi]. 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.

### How SOC Ops relates to other Ops

| Page | Shared scope | What SOC Ops hands over or receives |
|---|---|---|
| [SecOps](../secops/) | The word "SecOps" is used for both | Receives from operations: logs, agent health, change calendar, pre-agreed containment authority |
| [SecDataOps](../secdataops/) | Log sources and their quality | Hands over: silent sources, parsing gaps, enrichment needs |
| [DetectionOps](../detectionops/) | Alert quality | Hands over: dispositions per rule and tuning requests; receives tested detections with triage notes |
| [ThreatOps](../threatops/) | Context during triage | Receives intelligence and hunt findings; hands over observations worth hunting |
| [ResponseOps](../responseops/) | The alert-to-incident boundary | Hands over: cases that meet the incident criteria, with an escalation package; the IR lead declares |
| [IncidentOps](../incidentops/) | Incident command, MTTx caveats | Uses its definitions and caveats for time metrics |
| [VulnerabilityOps](../vulnerabilityops/) | Exploited weaknesses seen in alerts | Hands over: exposures that alerts reveal |
| [AIOps for Security](../aiops-for-security/) | ML and LLM assistance in triage | Receives evaluation and governance of AI tools |

::: figure Where SOC Ops sits in the flow of security work
```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.
:::

## Principles and operating model

### A proposed charter and principles

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"[^mitre11].
Its charter elements include authority to monitor, to scan, to "Coordinate or directly apply active or passive countermeasures" and
to retain digital artifacts[^mitre11]; a one-page template is in [Templates](#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"[^mitre11].
2. **Analysts work a curated queue, never a firehose.** "Do not ask analysts to monitor an unfiltered feed"[^mitre11].
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"[^first].
4. **Speed targets come with quality checks.** MITRE warns that mean-time-to-close SLAs "may force analysts to perform weak alert
   investigation"[^mitre11].
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[^sundaramurthy].
8. **Outsourcing moves execution, not accountability.** "No matter the outsourcing arrangement, constituency system owners and
   executives are still accountable for security and risk reduction"[^mitre11].

**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[^ncsc-om]), not the owner of every security project, and not a 24x7 help desk for IT problems.

### Fundamentals: services, people, data and tools

SOC-CMM assesses five domains (Business, People, Process, Technology, Services)[^soccmm-model]; 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[^ncsc-om]. This page covers mainly the third and the management of all four.

### Roles, tiers and RACI

**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"[^mitre11].
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"[^mitre11].

| 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](../secdataops/)) |
| SOC manager | Staffing, metrics, stakeholders, providers | Coverage model, targets, budget |
| IR lead ([ResponseOps](../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](../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[^mitre11]. 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

- **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"[^ncsc-tc], and the collection's introduction
  says "there is no one-size-fits-all SOC"[^ncsc-soc].
- **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"[^asd].
- **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[^asd].

### Operating model and governance

**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[^mitre11]. 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"[^ncsc-om]. 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](#reference-implementation-the-soc-operating-pack)
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"[^ncsc-om]. 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."[^mitre11]
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[^mitre11]; [ResponseOps](../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](#checklists).

## Architecture and workflow

### Reference architecture

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"[^mitre11].
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)[^sp80061].
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"[^mitre11]. NCSC adds that attackers with SOC data could "hide their tracks"[^ncsc-om].

::: figure Logical reference architecture of a SOC
```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[^asd].

### 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 Triage workflow with its exception paths
```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"[^sp80061].
That presupposes written criteria (CIS Safeguard 17.9[^cis17]); they mark the boundary with [ResponseOps](../responseops/).

### The case record and its timestamps

Every metric in [Metrics and KPIs](#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`[^ms-metrics]. That breaks when automation touches the case first: `ModifiedBy` is the "User or
system that modified the incident"[^ms-metrics], and automation rules can "Triage new incidents by changing their status from New
to Active and assigning an owner"[^ms-auto]. With enrichment and routing ahead of the analyst, as above, `FirstModifiedTime` measures
automation latency. Measure the first *human* change instead (see the
[reference implementation](#reference-implementation-the-soc-operating-pack)).

::: figure Case states and the timestamps that SOC metrics depend on
```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.

### Shift handover

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](#reference-implementation-the-soc-operating-pack)
follows MITRE's list of contents)[^mitre11]. It even suggests that the outgoing analysts and the incoming shift lead sign it. A paper
signature is optional; explicit acceptance is not.

::: figure Handover: an open item changes owner only when it is accepted
```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.
:::

## Practices, frameworks and tools

### Core practices and playbooks

| 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](../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"[^mitre11]. Its contents include scope, "Stimulating conditions" (when to use it) and
procedures; too little detail produces inconsistency, too much makes the playbook obsolete quickly[^mitre11]. 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"[^mitre11]. 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[^mitre11].

**Escalation package.** MITRE describes contract terms that protect the
client SOC "from being flooded with poor quality escalations"[^mitre11]; the same logic applies between tiers and at the hand-off.
[Templates](#templates) lists the minimum fields.

### Methods, standards and frameworks

| Source | Status | What it gives SOC Ops |
|---|---|---|
| NIST CSF 2.0, Detect (DE.CM, DE.AE) and Respond (RS.MA)[^csf2] | 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)[^sp80061] | Recommendations | SIEM and SOAR for continuous monitoring; contract clarity with providers; roles |
| CIS Controls v8.1 (Controls 8 and 17)[^cis8][^cis17] | Prescriptive safeguards | Centralise logs, retain at least 90 days, review weekly or more often; incident thresholds |
| FIRST CSIRT Services Framework 2.1[^first] | Community services taxonomy | Monitoring and detection, event analysis, log and sensor management, use-case lifecycle |
| ENISA, *How to set up CSIRT and SOC* (2020)[^enisa] | Good-practice guide (EU agency) | Setup phases; typical SOC KPIs |
| NCSC, *Building a SOC* (2022)[^ncsc-soc] | Government guidance (UK) | Threat-proportionate operating model, functions, hours, governance |
| ASD, CISA and partners, SIEM and SOAR guidance (2025)[^asd] | Government guidance (multinational) | Baseline, log standards, silent-source detection, SOAR readiness, cost |
| MITRE, *11 Strategies* (2022)[^mitre11] | Practitioner handbook | Charter, structure, staffing, 24x7, handover, metrics, outsourcing |
| SOC-CMM (v2.4 model)[^soccmm-model] | Open maturity model | Self-assessment across five domains |
| MITRE ATT&CK[^attack] | Knowledge base of adversary behaviour | Shared language for alerts and coverage (owned by [DetectionOps](../detectionops/)) |

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

### Tooling landscape (as of October 2026)

| Category | Job in the SOC | Examples (verified, not endorsed) | Selection questions |
|---|---|---|---|
| SIEM | Collect, correlate, alert, search | Microsoft Sentinel[^sentinel], Google Security Operations[^gsecops], Elastic Security[^elastic], Wazuh (open source)[^wazuh] | 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[^thehive]; 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[^ms-auto]; 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](../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"[^sentinel]. Google describes Google Security Operations as
"a unified experience across SIEM, SOAR, and threat intelligence"[^gsecops].

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

### Automation and AI

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"[^mitre11]. 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[^ms-auto].

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[^asd]. FIRST's framework says "in order to avoid
alert fatigue, automation is key" and that recurring cases "should be identified and automated"[^first]. 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"[^gsecops]. Treat these as aids whose output a person checks, measure them like detections, and
govern them as [AIOps for Security](../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"[^mitre11].

## SOC Ops in practice

### 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[^mitre11]; keep it in the case tool so it is searchable.

_Example status: template; not executed._

```text
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](#metrics-and-kpis) need,
with one trap: `FirstModifiedTime` includes changes made by automation (see [the case record](#the-case-record-and-its-timestamps)).
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[^ms-enum]; check them, and the `ModifiedBy` values, in your workspace._

```kql
// 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
```

```kql
// 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[^kql-summarize], 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[^ms-auto], 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)[^ms-auto] 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.

```kql
// 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](#lab-3-advanced-first-pass-triage-as-code-with-guardrails).

### Hands-on labs

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

**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._

```text
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
```

2. Save the script as `soc_queue_report.py`:

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

```python
#!/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]))
```

3. Run `python3 soc_queue_report.py alerts.csv`.

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

```text
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"[^mitre11]. `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?

**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[^erlang]. 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)._

```python
#!/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:**

```text
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)[^mitre11]. 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"[^mitre11]. **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.

::: note What the calculator leaves out
Shift leads, tier 2 investigators, engineers, managers, training time inside shifts, and the effect of incidents that pull analysts
off the queue for hours. MITRE puts typical ratios of triage to advanced analysis at "anywhere from 1:1 to 1:3" on day shift[^mitre11].
:::

#### 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._

```json
{
  "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"]
}
```

2. 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._

```json
{
  "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"}
}
```

```json
[
  {"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"}
]
```

3. Save the engine as `triage_policy.py`:

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

```python
#!/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]))
```

4. Save the guardrail tests as `test_triage_policy.py`:

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

```python
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()
```

5. Run `python3 triage_policy.py policy.json assets.json alerts.json 2026-10-16`, then `python3 -m unittest -v test_triage_policy`.

**Expected output:**

```text
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
```

```text
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.

### Use cases and scenarios

**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"[^senate-target]. 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[^asd]); 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[^csrb]. 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[^csrb]. *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[^csrb]).

**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[^mitre11]. *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[^mitre11]. *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.

## Measure, govern, pay

### Metrics and KPIs

Only half of the SOCs in a SANS survey cited by MITRE in 2022 had a formal metrics program[^mitre11]. ENISA's list of typical SOC
KPIs runs from detection speed and coverage to false-positive rates, escalations and workload per incident[^enisa]. 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](../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[^mitre11].

**"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](../aiops/) defines precision and recall once; [AIOps for Security](../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 %[^mtrends]. 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?")[^mitre11].

::: example Worked calculation: one month of a mid-size SOC (illustrative numbers)
**Inputs.** 4,200 alerts reached analysts. Dispositions: 168 true positives, 2,520 benign positives, 1,302 false positives, 210
undetermined. Mean handling time: true positive 35 min, benign 7 min, false 6 min, undetermined 20 min. 260 tier-1 escalations, 182 confirmed by tier 2.

**Alert quality.** Decided alerts = 168 + 2,520 + 1,302 = 3,990. True share = 168 ÷ 3,990 = **4.2 %**. Benign 63.2 %, false 32.6 %.

**Where the time goes.** Handling = 168 × 35 + 2,520 × 7 + 1,302 × 6 + 210 × 20 = 35,532 min = **592 h**. Benign and false
positives take 25,452 min = 424 h, or **72 %** of triage time; analyst time per true positive is 35,532 ÷ 168 = 212 min.

**Escalation to tier 2.** Rate 260 ÷ 4,200 = 6.2 %; acceptance 182 ÷ 260 = **70 %**.

**What to do with it.** If context removes 1,000 benign positives a month from the top three rules, the SOC recovers 1,000 × 7 = 7,000
min ≈ 117 h, about 0.8 FTE at Lab 2's 1,736 productive hours a year (≈ 145 h a month): the tuning loop's business case, in hours. The
4.2 % true share is not good or bad by itself; compare it per rule and over time.
:::

**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[^mitre11]. Counts of alerts handled, events ingested and rules deployed are activity, not outcomes;
report them only next to quality measures.

::: figure How SOC metrics feed decisions
```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.
:::

### Quality, security, privacy and compliance

**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"[^mitre11]. Practical controls: separate administrative identities and phishing-resistant MFA for SOC tools; SOC workstations
and management planes outside the general domain where feasible[^mitre11]; 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)[^csf2],
and SP 800-61 Rev. 3 expects legal experts to review procedures for compliance with laws "including the right to privacy"[^sp80061].
NCSC suggests written operating principles, such as searching sensitive data only "to detect malicious, suspicious or abusive
behaviour", and segregated insider-threat tooling[^ncsc-om]. 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[^cis8]. 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[^asd]. 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"[^mtrends].
Pipelines and storage tiers are [SecDataOps](../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)[^cis8]; a SOC exceeds
that easily, but audit evidence must show it. Mapping SOC outputs to audit evidence is [ComplianceOps](../complianceops/) work.

### Cost, capacity and sustainability

**People dominate cost.** MITRE estimates that taking a SOC from 8x5 to 24x7 "requires an increase of at least eight FTEs"[^mitre11].
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"[^asd].

**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](../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[^sundaramurthy]. Alahmadi and colleagues found
that alarm validation "is a tedious task that can cause alarm burnout and eventually desensitization"[^alahmadi]. 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[^mitre11].

### Assurance of the operating model

- **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"[^mitre11].
- **End-to-end tests.** Run a harmless test technique on a lab host (with [DetectionOps](../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[^asd]; MITRE lists red, purple and breach-and-attack-simulation tests[^mitre11].
- **Tabletop exercises** for the hand-off to [ResponseOps](../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[^soccmm-method].
- **Provider assurance.** A monthly service review with sampled cases, escalation timings and missed items ([Templates](#templates)).

## When things go wrong

### Common failures and troubleshooting

| Symptom | Likely cause | Diagnosis | Remediation | Prevention |
|---|---|---|---|---|
| Queue grows every week | New sources or rules without tuning; the SIEM "S-curve"[^mitre11] | Analyst minutes per rule; arrivals by source | Freeze new rules briefly; fix the top five by minutes | Acceptance criteria for new detections with [DetectionOps](../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](#worked-runbook-a-critical-log-source-goes-silent-overnight) | 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[^cis17]); 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](#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

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"[^asd]. 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](../detectionops/#worked-runbook-the-rule-that-went-silent).

::: example Illustrative incident: identity sign-in logs stop at 01:40
**Trigger.** At 02:10 a source-health check reports that sign-in logs from the identity provider have been silent for 30 minutes
(threshold 30). It is the night shift: two analysts on shift, engineers on call.

1. **Confirm (≤ 10 min).** Delay or silence? Check the identity provider's own console, the connector status and the SIEM's
   ingestion health. Events in the console but not in the SIEM point to the pipeline; no events anywhere, to the source.
2. **Assess the blind spot (≤ 10 min).** List the detections that depend on this source (password spraying, impossible travel, MFA
   fatigue, new admin roles); record them and the gap start in the pass-down log; tell the shift lead.
3. **Compensate (≤ 20 min).** Watch other telemetry for the same behaviours (EDR sign-in events, VPN logs, directory audit log) and
   check the identity provider console by hand every hour until the feed returns.
4. **Escalate the fix.** Page the SOC engineer or the [SecDataOps](../secdataops/) on-call. Common causes: an expired connector
   secret, a disabled connector, a schema change, an ingestion or cost cap, a network change (check the change calendar).
5. **Recover and close the gap.** When data flows again, check whether the source re-delivers late events. Scheduled detections may
   not look back far enough to cover the gap, so the shift re-runs the dependent detections over the gap window by hand. If the
   gap was long or a re-run finds anything, ask [ThreatOps](../threatops/) to hunt the window, as the DetectionOps runbook does.
6. **Close out.** Record gap start and end, cause, compensating checks and re-run results in the case; report the gap in the monthly
   service report; add a heartbeat threshold or credential-expiry alert so the cause is caught earlier next time.
:::

_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._

```kql
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[^ms-ingest]). With `isfuzzy=true`, a table that no
longer exists, say from a removed connector, is skipped rather than failing the query[^kql-union], and the join reports it as silent.

::: figure Runbook flow for a silent log 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.
:::

### Anti-patterns and myths

- **"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"[^asd]. 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[^mitre11].
- **"Faster closure means a better SOC."** Time-to-close targets breed hasty closure; MITRE lists the behaviours they cause[^mitre11].
- **"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[^mitre11], and NCSC
  points out that a 9-to-5 operation "still provides 100% more monitoring than having no SOC at all"[^ncsc-om]. 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.

## Deciding and adopting

### When to use and when not to use

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

**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[^mitre11]). ASD and partners also recommend an in-house capability for organisations that manage sensitive information or
provide critical or unique services[^asd].

**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[^mitre11]). 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[^cis8]), 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](#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"[^ncsc-tc]. 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[^asd].

**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.

### Decision framework: build, buy or hybrid

::: figure Choosing a SOC sourcing model
```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"[^mitre11]. 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"[^sp80061].

### Maturity model

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[^soccmm-maturity][^soccmm-model]. 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"[^soccmm-maturity].

### Adoption roadmap: 30/60/90 days

| 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](../secops/)), and logging ([SecDataOps](../secdataops/)).

## Toolkit

### Checklists

#### Quick start (first week)

- [ ] 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

#### Start of every 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

#### Weekly operations review

- [ ] 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

#### Before going 24x7 (or buying nights)

- [ ] 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

#### Provider onboarding (MSSP or MDR)

- [ ] 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

#### SOC continuity (practise twice a year)

- [ ] 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

### Templates

**1. SOC charter (one page).** Elements follow MITRE's list of charter contents[^mitre11].

_Example status: template; not executed._

```text
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._

```text
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._

```text
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[^mitre11] and NIST SP 800-61 Rev. 3[^sp80061].

| 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[^mitre11].

### Deep comparisons

| | SOC Ops (this page) | CSIRT | NOC | [IncidentOps](../incidentops/) | [SecOps](../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[^enisa] | 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](#decision-framework-build-buy-or-hybrid).

### FAQ

**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[^sp80061]; 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"[^mitre11].

**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](../detectionops/).

**When does an alert become an incident?** When it meets your written incident criteria (NIST CSF DE.AE-08[^csf2]). In this Atlas's
proposal the SOC hands it off, the IR lead ([ResponseOps](../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](../aiops-for-security/)).

## Going further

### Advanced topics and emerging direction

- **AI agents in triage.** Vendors market assistants that summarise cases and propose responses[^gsecops]. 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[^sentinel]); portability of detections and case history becomes a selection criterion.

### Learning path

| Stage | What to do | Outcome |
|---|---|---|
| Prerequisites | Networking, Windows and Linux logs, identity basics; read [SecOps](../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[^mitre11]; NCSC's SOC guidance[^ncsc-soc] | You can explain triage, dispositions and escalation |
| 1-week practice | Labs 1–3; install an open-source SIEM such as Wazuh in a lab[^wazuh]; 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](../detectionops/), [ThreatOps](../threatops/), [ResponseOps](../responseops/), [SecDataOps](../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](#deciding-and-adopting), the judgement call below, and MITRE's strategies 2, 3 and 10
(authority, structure, metrics)[^mitre11].

## Wrap-up

### Executive recap and cheat sheet

| Area | Remember |
|---|---|
| What | SOC Ops is running the SOC as a service: queue, people, shifts, metrics, sourcing |
| Boundaries | Detections → [DetectionOps](../detectionops/); incidents → [ResponseOps](../responseops/); data → [SecDataOps](../secdataops/); intelligence → [ThreatOps](../threatops/); IT contract → [SecOps](../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?

::: decision Illustrative dilemma
**Situation.** A 1,200-person company runs a four-analyst SOC from 08:00 to 18:00 on weekdays. Its queue report (like Lab 1) shows
a median after-hours time to triage above eight hours, including high-severity identity alerts. The board will fund one of two
options for round-the-clock coverage.

**Option A: build an in-house night and weekend shift.** Lab 2's arithmetic says two seats around the clock need about 10 FTE
(2 × 168 × 52 ÷ 1,736 ≈ 10.1), so roughly six to eight hires on top of the current team, in line with MITRE's "at least eight FTEs"
to go from 8x5 to 24x7[^mitre11]. Full control and context, but months to hire and train, and night analysts who may feel
"underinformed, underutilized, and unrecognized"[^mitre11]. MITRE's mitigation, rotating staff between days and nights every couple
of months, spreads experience but puts today's day team on nights in turn.

**Option B: contract an MDR provider for nights and weekends.** Coverage within weeks from a provider that already runs night
shifts, but less context (ASD: "subtler activity may go unnoticed without in-depth knowledge of the environment"[^asd]) and a
handover twice a day.

**Assumptions.** The main night-time risk is identity compromise and ransomware staging; the company can define containment authority
for laptops and standard accounts; nobody with authority over core systems is on call at night today.

**Reasoning.** Option A looks like the "serious" choice, but until rotation is running the night shift would be new hires without
senior staff on site, working a thin queue, with nobody in authority to call.
Option B is faster, but only safe with written authority, a 15-minute P1 escalation to an in-house on-call lead, joint handovers and
sampled case reviews. The better first move is **B with those contract terms plus an in-house on-call rota**, measured for six months
on after-hours time to triage, escalation acceptance and missed items found in reviews; revisit A if the volume of nightly
investigations or the sensitivity of the data grows.

**Uncertainty.** Provider quality varies and is hard to judge before a contract; a paid trial with a red-team drill at 03:00 is the
cheapest evidence. If regulation requires data to stay in-house, the decision flips to A or to a hybrid with tighter data controls.
:::

### Learning objectives and mastery check

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.

::: answer 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.
:::

::: answer 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.
:::

::: answer 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.
:::

::: answer 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.
:::

::: answer 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.
:::

::: answer 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.
:::

::: answer 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.
:::

::: answer 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).
:::

### Conclusion

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.

## Sources and further reading

[^mitre11]: [11 Strategies of a World-Class Cybersecurity Operations Center](https://www.mitre.org/news-insights/publication/11-strategies-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](https://web.archive.org/web/2024/https://www.mitre.org/sites/default/files/2022-04/11-strategies-of-a-world-class-cybersecurity-operations-center.pdf). Checked 2026-10-16; re-checked 2026-10-17.
[^enisa]: [How to set up CSIRT and SOC: Good practice guide](https://www.enisa.europa.eu/publications/how-to-set-up-csirt-and-soc) — ENISA, 2020-12-10. Checked 2026-10-16.
[^ncsc-soc]: [Building a Security Operations Centre (SOC)](https://www.ncsc.gov.uk/collection/building-a-security-operations-centre) — UK National Cyber Security Centre, published 2022-05-23. Checked 2026-10-16.
[^ncsc-om]: [Building a SOC: Designing an operating model](https://www.ncsc.gov.uk/collection/building-a-security-operations-centre/operating-model/designing-an-operating-model) — UK National Cyber Security Centre, 2022-05-23. Checked 2026-10-16.
[^ncsc-tc]: [Building a SOC: Things to consider](https://www.ncsc.gov.uk/collection/building-a-security-operations-centre/operating-model/things-to-consider) — UK National Cyber Security Centre, 2022-05-23. Checked 2026-10-16.
[^first]: [CSIRT Services Framework, Version 2.1.0](https://www.first.org/standards/frameworks/csirts/FIRST_CSIRT_Services_Framework_v2.1.0_bugfix1.pdf) — FIRST, released PDF (TLP:WHITE, November 2019; first framework published 2017); the [HTML version](https://www.first.org/standards/frameworks/csirts/csirt_services_framework_v2.1) is labelled "for review purposes". Checked 2026-10-16; PDF checked 2026-10-17.
[^soccmm-intro]: [SOC-CMM introduction](https://www.soc-cmm.com/introduction) — SOC-CMM, live page. Checked 2026-10-16.
[^soccmm-model]: [SOC-CMM model (v2.4)](https://www.soc-cmm.com/introduction/model) — SOC-CMM, live page. Checked 2026-10-16.
[^soccmm-maturity]: [SOC-CMM maturity levels](https://www.soc-cmm.com/introduction/maturity) — SOC-CMM, live page. Checked 2026-10-16.
[^soccmm-method]: [SOC-CMM methodology](https://www.soc-cmm.com/introduction/methodology) — SOC-CMM, live page. Checked 2026-10-16.
[^nist-gl]: [Security Operations Center (glossary entry)](https://csrc.nist.gov/glossary/term/security_operations_center) — NIST CSRC Glossary, live page (acronym entry only). Checked 2026-10-16.
[^csf2]: [The NIST Cybersecurity Framework (CSF) 2.0](https://doi.org/10.6028/NIST.CSWP.29) — NIST CSWP 29, 2024-02-26. Checked 2026-10-16.
[^sp80061]: [SP 800-61 Rev. 3: Incident Response Recommendations and Considerations for Cyber Risk Management](https://csrc.nist.gov/pubs/sp/800/61/r3/final) — NIST, April 2025. Checked 2026-10-16.
[^asd]: [Implementing SIEM and SOAR platforms: practitioner guidance](https://www.cyber.gov.au/sites/default/files/2025-05/implementing_siem_and_soar_platforms_-_practitioner_guidance.pdf) — 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](https://web.archive.org/web/2025/https://www.cyber.gov.au/sites/default/files/2025-05/implementing_siem_and_soar_platforms_-_practitioner_guidance.pdf). Checked 2026-10-16; re-checked 2026-10-17.
[^cisa-siem]: [Guidance for SIEM and SOAR Implementation](https://www.cisa.gov/resources-tools/resources/guidance-siem-and-soar-implementation) — CISA, 2025-05-27. Checked 2026-10-16.
[^cis8]: [CIS Controls v8.1 Assessment Specification: Control 8, Audit Log Management](https://cas.docs.cisecurity.org/en/latest/source/Controls8/) — Center for Internet Security, live documentation; the specification's [home page](https://cas.docs.cisecurity.org/en/latest/) is titled "CIS Controls Assessment Specification for Controls v8.1". Checked 2026-10-16; version re-checked 2026-10-17.
[^cis17]: [CIS Controls v8.1 Assessment Specification: Control 17, Incident Response Management](https://cas.docs.cisecurity.org/en/latest/source/Controls17/) — Center for Internet Security, live documentation (v8.1 per the specification's home page). Checked 2026-10-16; version re-checked 2026-10-17.
[^ms-caf]: [Security teams, roles, and functions](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/secure/teams-roles) — Microsoft Learn, Cloud Adoption Framework, updated 2025-12-04. Checked 2026-10-16.
[^ms-metrics]: [Manage your SOC better with incident metrics](https://learn.microsoft.com/en-us/azure/sentinel/manage-soc-with-incident-metrics) — Microsoft Learn, updated 2026-07-02. Checked 2026-10-16; re-checked 2026-10-17.
[^ms-auto]: [Automate threat response in Microsoft Sentinel with automation rules](https://learn.microsoft.com/en-us/azure/sentinel/automate-incident-handling-with-automation-rules) — Microsoft Learn, updated 2026-06-30 (ms.date 2024-10-16). Checked 2026-10-16; re-checked 2026-10-17.
[^ms-close]: [Investigate incidents with Microsoft Sentinel (legacy)](https://learn.microsoft.com/en-us/azure/sentinel/investigate-cases) — Microsoft Learn, updated 2026-07-02. Checked 2026-10-16.
[^ms-xdr]: [Manage incidents in the Microsoft Defender portal (legacy)](https://learn.microsoft.com/en-us/defender-xdr/manage-incidents) — Microsoft Learn, updated 2026-07-02. Checked 2026-10-16.
[^ms-enum]: [SecurityInsightsIncidentClassification struct](https://learn.microsoft.com/en-us/dotnet/api/azure.resourcemanager.securityinsights.models.securityinsightsincidentclassification?view=azure-dotnet) — Microsoft Learn, Azure SDK for .NET reference, live page. Checked 2026-10-16.
[^sentinel]: [What is Microsoft Sentinel SIEM?](https://learn.microsoft.com/en-us/azure/sentinel/overview) — Microsoft Learn, updated 2026-01-28. Checked 2026-10-16.
[^gsecops]: [Google Security Operations](https://cloud.google.com/security/products/security-operations) — Google Cloud, product page, undated. Checked 2026-10-16.
[^elastic]: [Elastic Security](https://www.elastic.co/security) — Elastic, product page, undated. Checked 2026-10-16.
[^wazuh]: [Getting started with Wazuh](https://documentation.wazuh.com/current/getting-started/index.html) — Wazuh documentation, current version. Checked 2026-10-16.
[^thehive]: [TheHive](https://strangebee.com/thehive/) — StrangeBee, product page, undated. Checked 2026-10-16.
[^attack]: [MITRE ATT&CK](https://attack.mitre.org/) — The MITRE Corporation, live knowledge base. Checked 2026-10-16.
[^senate-target]: [A "Kill Chain" Analysis of the 2013 Target Data Breach, reprinted in S. Hrg. 113-531](https://www.govinfo.gov/content/pkg/CHRG-113shrg92594/html/CHRG-113shrg92594.htm) — US Senate Committee on Commerce, Science, and Transportation, majority staff report, 2014-03-26. Checked 2026-10-16; re-checked 2026-10-17.
[^csrb]: [Review of the Summer 2023 Microsoft Exchange Online Intrusion](https://www.cisa.gov/sites/default/files/2025-03/CSRBReviewOfTheSummer2023MEOIntrusion508.pdf) — Cyber Safety Review Board, 2024-03-20. Checked 2026-10-16; re-checked 2026-10-17.
[^mtrends]: [M-Trends 2026: Data, Insights, and Strategies From the Frontlines](https://cloud.google.com/blog/topics/threat-intelligence/m-trends-2026) — Mandiant (Google Cloud), 2026-03-24. Checked 2026-10-16.
[^alahmadi]: [99% False Positives: A Qualitative Study of SOC Analysts' Perspectives on Security Alarms](https://www.usenix.org/system/files/sec22-alahmadi.pdf) — Bushra A. Alahmadi, Louise Axon, Ivan Martinovic, 31st USENIX Security Symposium, 2022. Checked 2026-10-16.
[^sundaramurthy]: [A Human Capital Model for Mitigating Security Analyst Burnout](https://www.usenix.org/system/files/conference/soups2015/soups15-paper-sundaramurthy.pdf) — Sathya Chandran Sundaramurthy et al., SOUPS 2015. Checked 2026-10-16.
[^erlang]: [Erlang (unit): Erlang C formula](https://en.wikipedia.org/wiki/Erlang_(unit)) — Wikipedia, live page (secondary source for a standard queueing formula). Checked 2026-10-16.
[^aicpa]: [System and Organization Controls: SOC Suite of Services](https://www.aicpa-cima.com/resources/landing/system-and-organization-controls-soc-suite-of-services) — AICPA & CIMA, live page. Checked 2026-10-16.
[^ms-ingest]: [Log data ingestion time in Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/data-ingestion-time) — Microsoft Learn, updated 2026-07-29. Checked 2026-10-17.
[^kql-union]: [union operator](https://learn.microsoft.com/en-us/kusto/query/union-operator?view=microsoft-sentinel) — Microsoft Learn, Kusto Query Language reference, updated 2024-08-11. Checked 2026-10-17.
[^kql-summarize]: [summarize operator](https://learn.microsoft.com/en-us/kusto/query/summarize-operator?view=microsoft-sentinel) — Microsoft Learn, Kusto Query Language reference, updated 2025-01-21. Checked 2026-10-17.

**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](https://www.cisa.gov/resources-tools/resources/guidance-siem-and-soar-implementation).

**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).
