Skip to content

Crisis Communication & Leadership

Crisis communication and leadership is the human control system that runs above the technical response: the incident command structure (who decides, who speaks, who executes — adapted from emergency management’s ICS), the situation reporting rhythm (facts separated from inference, on a cadence, to named stakeholders), the regulatory clock management (GDPR’s 72-hour breach notification, SEC material-incident disclosure windows, sector regulators, insurers), and the decision discipline that keeps a bad hour from becoming a bad quarter. The doctrine’s core: in a crisis, the organization runs on the information flow, not on the technical facts — an incident whose containment is on track but whose communication is chaotic produces worse outcomes (regulatory, legal, reputational, internal) than a harder incident with clean command. The leader’s job is neither cheerleading nor micromanaging the terminal: it is deciding under uncertainty on a deadline — declare or not, notify or not, isolate the segment or take the segment down, pay or not (with the legal and ethical frame that question deserves) — while the responders respond and the record stays honest. The craft’s center: communication is an engineering artifact like any other in this handbook — templates, matrices, logs, drills, and metrics — and organizations that treat it as art discover their improvisation skills at the worst possible moment.

Every hour of a major incident, three clocks run simultaneously. The technical clock (containment progressing or not) is the one responders own. The regulatory clock is unforgiving and mostly blind to nuance: GDPR requires breach notification to the supervisory authority within 72 hours of awareness (Art. 33 — and “awareness” starts when you know, not when you’re sure); the SEC requires public companies to disclose material cybersecurity incidents within four business days of the materiality determination; NIS2 imposes early-warning timelines on EU operators [verify sector-specific implementations]; cyber insurers and customer contracts layer their own notice windows. Miss them and the violation is yours, whatever the attacker did. The narrative clock is fastest of all: social media, journalists, customers, and employees form the story within hours — and the story forms without you if you don’t speak, because silence reads as either incompetence or concealment.

The failure modes are all leadership-shaped. Command ambiguity: two people believe they’re deciding, so nobody is — the isolation decision waits four hours for a meeting that never convenes. Facts-infection: unverified inference (“it’s ransomware”, “no data was accessed”) enters the sitrep stream and later becomes the company’s public statement, then the regulator’s finding of a false statement. Single-channel collapse: command runs on the same chat platform the adversary has (or that DDoS takes down), and the response loses its nervous system mid-incident. Notification paralysis: legal and security disagree about materiality for days while the 72-hour clock runs — the disagreement is legitimate, the missing decision process for resolving it is not. And drill absence: the first time the exec team practices a sitrep should never be during a real incident; tabletop discipline (BT4’s exercise program) is where these muscles build cheaply.

MAJOR INCIDENT DECLARED (BT4)
|
+-------------+--------------+
v v v
+----------+ +-----------+ +-----------+
| INCIDENT | | COMMS | | LEGAL/ |
| COMMANDER| | LEAD | | REGULATORY|
| decides: | | owns: | | owns: |
| contain, | | sitreps, | | clocks, |
| escalate,| | statements| | privilege,|
| resources| | spokespeople | notifications |
+----+-----+ +-----+-----+ +------+----+
| | |
v v v
+------------------------------------------+
| DECISION LOG (crisistimeline) |
| timestamp | actor | fact/inference | |
| decision | rationale | approver |
+--------------------+---------------------+
|
+-----------------+------------------+
v v v
TECHNICAL TEAM STAKEHOLDERS PUBLIC
(BT4 tactical) exec, board, customers, media,
task sync, regulators, insurer, social, employees
no comms duty key customers via spokespeople only

Command structure adapts ICS to corporate reality: one Incident Commander (technical authority — declares severity, owns containment decisions, escalates to exec), one Communications Lead (owns everything that leaves the room — sires, statements, spokespeople), one Legal/Regulatory Lead (owns the clocks, privilege, notification decisions), and a Scribe (the decision log — chronically undervalued, and the single role whose absence costs the most in the post-incident review and the regulator’s questions). Severity tiers (SEV1-4, set in BT4’s process) map to activation: SEV1 convenes the full structure with a cadence; SEV3 runs on the SOC’s normal escalation.

Situation reporting is the heartbeat. A sitrep is short, fixed-format, and ruthlessly separated: FACTS (what we have verified, with evidence), ASSESSMENT (what we believe, with confidence), ACTIONS (what’s in flight, who owns), DECISIONS NEEDED (what’s blocked on leadership, with the deadline) — every sitrep ends with next update time because the absence of a promised update creates the information vacuum that rumor fills. Cadence: SEV1 hourly to the exec stream, written, no exceptions — verbal-only command collapses the moment three time zones wake up.

Regulatory clocks are managed like detection SLAs: each obligation is a timer with an owner. The 72-hour GDPR clock starts at awareness; the materiality determination process for SEC disclosure needs a pre-agreed decision procedure (who convenes, what evidence standard, what deadline) so the determination itself doesn’t become the bottleneck. Legal privilege is the frame for honest internal analysis: route forensic findings through counsel where the strategy warrants, and label documents accordingly — but know the boundary, because privilege doesn’t cover operational facts shared with insurers, regulators, or customers.

The decision log is the spine of all of it — timestamped entries for every significant decision, its rationale, its approver, and whether it rested on fact or inference. Post-incident (BT4’s review), the log is what converts an incident into learning; during the incident, it is what keeps three conflicting memories from rewriting the response.

Deep dive: legal privilege in incident response — the boundary that matters

Routing investigation work through counsel can protect analysis from discovery — but the protection has edges, and teams that misunderstand them either over-claim (and lose credibility with regulators) or under-use (and hand over work product they could have protected). The working boundary: privilege covers analysis and legal advice prepared for counsel (the forensic report counsel commissioned, the risk assessment memo) — it does not cover operational facts (what systems were affected, when awareness occurred, what was notified) shared with regulators, insurers, or customers; those facts are discoverable wherever they live, including in your sitreps. The practical discipline: label counsel-directed work clearly at creation, keep the fact/inference separation in the log (facts were going to be discoverable anyway — the honest log is consistent), and never let “privilege” become a reason to skip recording a decision. Regulators ask when did you know and what did you decide — a privilege shield does not reach “we didn’t write it down”, and “we didn’t write it down” reads as concealment.

TechniqueWhat it doesWhere it failsNotes
Incident command (ICS-adapted)Single decision authority per domainTwo commanders by accidentNamed backups per role
Sitrep cadenceFixed-format, hourly, ends with next-updateFacts/inference bleedcrisistimeline enforces
Decision logTimestamped rationale + approverWritten after the factScribe is a real role
Notification clock tableEach obligation as timer + ownerMateriality process missingLegal owns, security informs
Pre-approved statementsHolding statements drafted in peacetimeLegal review in hour 3Update, don’t compose
Spokesperson funnelOne voice externally, named per audienceEveryone answers journalistsComms lead routes
Out-of-band command channelCommand survives email/chat compromisePersonal-account policy violationPre-provisioned, tested
Tabletop programExecs practice before the real oneAnnual-and-forgetBT4 exercise program
Stakeholder matrixWho gets what, when, via whomEmpty cells at SEV1commatrix validates
Post-incident comms reviewComms treated like technical RCASkipped when “it went fine”BT20 metric input

crisistimeline — a decision-log and sitrep engine for incident command: records events with enforced discipline (every entry is FACT or INFERENCE, every decision names an approver), generates the hourly sitrep in fixed format from the log, and computes the regulatory clock countdowns from the awareness timestamp. The tool makes the honest record the easy record.

#!/usr/bin/env python3
"""crisistimeline — incident decision log + sitrep generator + clock watch.
Commands:
add --type fact|inference|decision|action --actor NAME
--text "..." [--approver NAME] [--rationale "..."]
sitrep --incident-id INC-2026-001 [--audience exec|regulator]
clocks --awareness "2026-10-14T08:00:00" (GDPR/SEC countdowns)
Log: append-only JSONL (crisis_log.jsonl). Honest record by construction.
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path
LOG = Path("crisis_log.jsonl")
TYPES = ("fact", "inference", "decision", "action")
def now():
return datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
def append(entry):
with LOG.open("a", encoding="utf-8") as fh:
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
def load():
if not LOG.exists():
return []
entries = []
with LOG.open(encoding="utf-8") as fh:
for n, line in enumerate(fh, 1):
try:
entries.append(json.loads(line))
except json.JSONDecodeError:
sys.exit(f"fail-loud: log line {n} corrupt — fix before use")
return entries
def cmd_add(a):
if a.type not in TYPES:
sys.exit(f"fail-loud: type must be one of {TYPES}")
if a.type == "decision" and not a.approver:
sys.exit("fail-loud: decisions require --approver "
"(a decision without a named owner is a rumor)")
entry = {"ts": now(), "type": a.type, "actor": a.actor, "text": a.text}
if a.approver:
entry["approver"] = a.approver
if a.rationale:
entry["rationale"] = a.rationale
append(entry)
print(f"logged [{entry['type']}] {entry['ts']}")
def cmd_sitrep(a):
entries = load()
if not entries:
sys.exit("fail-loud: empty log — nothing to report is not a clean report")
facts = [e for e in entries if e["type"] == "fact"]
infer = [e for e in entries if e["type"] == "inference"]
actions = [e for e in entries if e["type"] == "action"]
decisions = [e for e in entries if e["type"] == "decision"]
last = entries[-1]["ts"]
lines = [f"SITREP {a.incident_id} as-of {last}",
"", "FACTS (verified):"]
lines += [f" {e['ts']} {e['actor']}: {e['text']}" for e in facts[-5:]]
lines += ["", "ASSESSMENT (inference, confidence owner-stated):"]
lines += [f" {e['ts']} {e['actor']}: {e['text']}" for e in infer[-3:]] \
or [" - none recorded"]
lines += ["", "ACTIONS IN FLIGHT:"]
lines += [f" {e['ts']} {e['actor']}: {e['text']}" for e in actions[-5:]] \
or [" - none recorded"]
lines += ["", "DECISIONS TAKEN:"]
lines += [f" {e['ts']} {e['actor']} (approved: {e.get('approver', '?')}): "
f"{e['text']}" for e in decisions[-3:]] or [" - none recorded"]
lines += ["", f"NEXT UPDATE: (set and keep the promise)"]
print("\n".join(lines))
def cmd_clocks(a):
try:
aware = datetime.fromisoformat(a.awareness.replace("Z", "+00:00"))
except ValueError:
sys.exit(f"fail-loud: cannot parse awareness time {a.awareness!r}")
nowdt = datetime.now(aware.tzinfo)
elapsed = nowdt - aware
gdpr_deadline = aware + timedelta(hours=72)
print(f"awareness : {aware.isoformat()}")
print(f"elapsed : {elapsed}")
print(f"GDPR 72h deadline: {gdpr_deadline.isoformat()} "
f"({gdpr_deadline - nowdt} remaining)")
# SEC: 4 business days from materiality DETERMINATION (not awareness)
print("SEC 4-business-day window: starts at MATERIALITY DETERMINATION, "
"not awareness — log that determination as a decision entry "
"with --approver when it is made")
if elapsed > timedelta(hours=72):
print("WARNING: 72h elapsed — notification decision must be "
"documented (log it now)")
def main():
ap = argparse.ArgumentParser(description="crisis decision log + sitrep")
sub = ap.add_subparsers(dest="cmd", required=True)
p1 = sub.add_parser("add", help="append a log entry")
p1.add_argument("--type", required=True, help="fact|inference|decision|action")
p1.add_argument("--actor", required=True)
p1.add_argument("--text", required=True)
p1.add_argument("--approver")
p1.add_argument("--rationale")
p1.set_defaults(func=cmd_add)
p2 = sub.add_parser("sitrep", help="generate sitrep from log")
p2.add_argument("--incident-id", required=True)
p2.add_argument("--audience", default="exec")
p2.set_defaults(func=cmd_sitrep)
p3 = sub.add_parser("clocks", help="regulatory clock countdowns")
p3.add_argument("--awareness", required=True,
help="ISO timestamp of awareness")
p3.set_defaults(func=cmd_clocks)
args = ap.parse_args()
args.func(args)
if __name__ == "__main__":
main()

The type system (lines 15-16, enforced at line 48) is the tool’s whole philosophy: every log entry is fact (verified, evidence-backed), inference (belief with a stated owner), decision (which requires an approver — line 49-51 exits loudly otherwise, because a decision without a named owner is how command ambiguity is born), or action (in-flight work). The sitrep generator (lines 57-83) is a fixed-format projection of the log — no free composition, no “polishing” — with the last N entries per section and the mechanical separation of facts from inference: the discipline that keeps “no data was accessed” (inference stated as fact in hour two) out of the regulator-bound statement in hour seventy. The trailing “NEXT UPDATE” line (line 82) is deliberate nagging: the sitrep is not done until the promise of the next one exists.

cmd_clocks() (lines 86-101) computes the GDPR 72-hour window from the awareness timestamp and prints the warning when elapsed — but the more important line is the SEC guidance print (lines 97-99): the four-business-day window starts at the materiality determination, not awareness, and the tool instructs the user to log that determination as a decision entry with an approver — wiring the regulatory clock directly into the decision-log discipline, so the “when did we determine materiality” question that regulators ask has a timestamped, owned answer. The log is append-only JSONL (lines 19-33) with per-line corruption detection that fails loud (lines 28-30) — a crisis log that silently skips a corrupt line is a forensics problem, and it’s BT5’s evidence rules that apply here too.

SEV1 declares at 08:00 (BT4’s severity criteria). By 08:15 the structure convenes: IC, comms lead, legal, scribe — and crisistimeline opens the log. The technical team works their own tasks (BT4’s tactical process) and feeds facts to the log; the comms lead generates the 09:00 sitrep from the log as it stands — three facts, one inference (“lateral movement likely contained” — owned by the IC, labeled inference), four actions, zero public statement yet (legal still assessing). The 10:00 sitrep includes the first decision: perimeter segment isolation, approved by the IC, rationale logged. Legal logs awareness at 08:20 as a fact — the GDPR clock starts; the clocks command’s printout goes into the 10:00 sitrep’s footer: “72h deadline: 72h remaining” — turning an abstract legal obligation into a number every sitrep reader carries.

The stress point arrives at 14:00: a journalist calls referencing a pastebin dump. The comms lead routes it — no one else answers — and the pre-approved holding statement goes out updated with facts from the log (“we are investigating a security incident; at this time our investigation is ongoing”), carefully containing only facts because the inference track stays internal. The materiality determination process convenes at 16:00 (the pre-agreed procedure, not an improvisation): determination logged as a decision with approver and rationale; the SEC clock gets its own entry. By 20:00 containment is verified (fact), the customer notice decision is made and logged, and the day’s sits — sits — the sitreps are the regulator’s first evidence pack, the board’s actual picture, and the post-incident review’s raw material. What the attacker sees, meanwhile, is an organization whose public posture never leaked an operational detail: statements built from the fact track only, because the discipline existed before hour zero.

  1. Trigger — major incident declared (BT4); command structure convenes with named roles
  2. Awareness logged — fact entry starts the GDPR clock; clocks command activates
  3. Sitrep rhythm — hourly, fixed format, facts/inference separated, next-update promised
  4. Decision stream — isolation, notification, disclosure: each with approver and rationale in the log
  5. Regulatory fork — materiality determination via pre-agreed procedure; determination logged; SEC clock starts
  6. External voice — one spokesperson, pre-approved statements updated with facts only
  7. Adversary pressure — impersonation and urgency attacks target the chaos (see Evasion panel); out-of-band channel holds
  8. Close-out — decision log becomes the post-incident record, regulator evidence, and BT20’s comms metrics

The stakeholder matrix is the other half of the machinery: who gets told what, when, by whom, over which channel — at each severity. commatrix validates a matrix (CSV) for the failure modes that surface at hour two of a real incident: empty cells at SEV1, stakeholders with no named owner, channels that die with the primary network, and notification obligations with no clock owner.

Validation over generation: the matrix is authored by humans (comms lead + legal) — the tool’s job is to refuse to bless an incomplete one. SEV1 must be complete: every stakeholder row needs channel, owner, and SLA; SEV2-3 may have gaps but they’re reported. Channel diversity check: if every row’s channel is the same platform, the single-channel-collapse risk is flagged — command needs at least one out-of-band path per stakeholder class. Drill mode: --drill simulates a SEV1 activation and prints the contact sequence with SLA timers, so the quarterly tabletop exercises the matrix, not a memory. Fail-loud on schema problems — a matrix that parses to nothing is worse than none, because it looks like coverage.

#!/usr/bin/env python3
"""commatrix — validate and drill the stakeholder communication matrix.
Input CSV: stakeholder,class,sev1_channel,sev1_owner,sev1_sla_min,
alt_channel,notes
Classes: exec|board|regulator|insurer|customer|employee|partner
Usage: python3 commatrix.py matrix.csv [--drill]
"""
import argparse
import csv
import sys
from pathlib import Path
CLASSES = {"exec", "board", "regulator", "insurer", "customer",
"employee", "partner"}
REQUIRED_COLS = ["stakeholder", "class", "sev1_channel", "sev1_owner",
"sev1_sla_min", "alt_channel"]
def main():
ap = argparse.ArgumentParser(description="communication matrix validator")
ap.add_argument("matrix", type=Path)
ap.add_argument("--drill", action="store_true",
help="simulate SEV1 activation sequence")
args = ap.parse_args()
if not args.matrix.exists():
sys.exit(f"fail-loud: matrix not found: {args.matrix}")
with args.matrix.open(encoding="utf-8", errors="replace") as fh:
reader = csv.DictReader(fh)
missing = [c for c in REQUIRED_COLS if c not in (reader.fieldnames or [])]
if missing:
sys.exit(f"fail-loud: matrix missing columns: {missing}")
rows = list(reader)
if not rows:
sys.exit("fail-loud: empty matrix — coverage must be authored, not assumed")
problems = []
channels = set()
for n, row in enumerate(rows, 2):
who = row["stakeholder"].strip() or f"row {n}"
cls = row["class"].strip().lower()
if cls not in CLASSES:
problems.append(f"{who}: unknown class {cls!r}")
for col in ("sev1_channel", "sev1_owner", "sev1_sla_min"):
if not row[col].strip():
problems.append(f"{who}: SEV1 {col} is empty — "
"SEV1 rows must be complete")
try:
sla = int(row["sev1_sla_min"])
if sla <= 0:
problems.append(f"{who}: SLA must be positive minutes")
except ValueError:
problems.append(f"{who}: SLA not a number: {row['sev1_sla_min']!r}")
channels.add(row["sev1_channel"].strip().lower())
if not row["alt_channel"].strip():
problems.append(f"{who}: no alternate (out-of-band) channel")
distinct = len(channels)
if distinct < 2:
problems.append(f"only {distinct} distinct channel(s) — "
"single-channel collapse risk; add out-of-band paths")
classes_covered = {r["class"].strip().lower() for r in rows}
uncovered = CLASSES - classes_covered
if uncovered:
problems.append(f"no stakeholder rows for classes: {sorted(uncovered)}")
print(f"matrix={len(rows)} rows distinct channels={distinct}")
if problems:
print(f"PROBLEMS ({len(problems)}):")
for p in problems:
print(" -", p)
sys.exit(1)
print("matrix VALID: SEV1-complete, channel-diverse, all classes covered")
if args.drill:
ordered = sorted(rows, key=lambda r: int(r["sev1_sla_min"]))
print("\nSEV1 DRILL SEQUENCE (by SLA):")
for r in ordered:
print(f" T+{r['sev1_sla_min']:>4}min {r['stakeholder']:<22} "
f"owner={r['sev1_owner']:<14} via {r['sev1_channel']}")
print("run the sequence in the tabletop; log actual times vs SLA")
if __name__ == "__main__":
main()

The validation pass (lines 44-66) encodes the four real-world killers as refusals: SEV1-empty cells (a stakeholder with no channel at the worst moment), non-positive or non-numeric SLAs (timer rows that can’t drive a drill), missing alternate channels (single-channel collapse per-stakeholder), and unknown classes (vocabulary drift breaking the class-based routing). The channel-diversity check (lines 68-71) is the systemic version — fewer than two distinct channels across the whole matrix means command itself dies with the platform. Class coverage (lines 73-75) catches the absent stakeholder — the insurer nobody remembered until renewal, the regulator discovered during the incident.

Exit discipline is the tool’s value: invalid matrices exit 1 (line 81), so the matrix file can gate in CI the way BT9 gates rules and podspec gates specs — reviewed quarterly, versioned, and incapable of silently rotting. Drill mode (lines 84-90) sorts the activation sequence by SLA and prints it as T+minutes — the tabletop then runs against actual times, and the delta between SLA and reality is the comms program’s own drift metric, feeding BT20 exactly the way detection coverage does.

validate the authored matrix (quarterly review gate)

Section titled “validate the authored matrix (quarterly review gate)”

$ python3 commatrix.py comms_matrix.csv matrix=8 rows distinct channels=3 matrix VALID: SEV1-complete, channel-diverse, all classes covered

$ python3 commatrix.py comms_matrix_draft.csv; echo “exit=$?” matrix=8 rows distinct channels=1 PROBLEMS (6):

  • CISO: SEV1 sev1_owner is empty — SEV1 rows must be complete
  • CISO: no alternate (out-of-band) channel
  • only 1 distinct channel(s) — single-channel collapse risk; add out-of-band paths … exit=1

$ python3 commatrix.py comms_matrix.csv —drill matrix VALID: SEV1-complete, channel-diverse, all classes covered

SEV1 DRILL SEQUENCE (by SLA): T+ 15min CISO owner=ic-oncall via bridge-phone T+ 30min CEO owner=comms-lead via bridge-phone T+ 60min board-chair owner=ceo via phone-tree …

The matrix and the log generate program telemetry with the same rigor as detection metrics: matrix-validation failures between quarters (drift in stakeholders, owners leaving), drill SLA-vs-actual deltas, sitrep cadence kept or missed during real incidents, and decision-log completeness (the BT4 post-incident review grades it). Two failure tells to watch: decision entries appearing after the sitrep that reported them (retroactive authoring — the log must lead the reporting, or it’s fiction), and inference entries missing during the middle hours of an incident (an assessment-free crisis log means people stopped recording beliefs, which means the record is being sanitized in real time). Both are cultural alarms more than tool alarms — but the tool makes them visible.

  1. Auto-generate the regulator-notification evidence pack from the log (awareness entry, determination decision, timeline slice) per jurisdiction template.
  2. Join with BT4’s incident system: severity declarations and closures auto-log as decision entries.
  3. Add per-audience sitrep rendering (exec: decisions + clocks; technical: actions + facts) from the same log.
  4. Track “time to first sitrep” and “cadence kept %” as first-class BT20 metrics with quarter-over-quarter trend.
  5. Encrypt and witness the log (append-only hash chain) so integrity survives a disputed incident review.
SignalSourceIDNotes
Time to first sitrepcrisistimeline logper incidentCadence health
Cadence kept %Sitrep historyper incidentPromise discipline
Decision log completenessBT4 reviewper incidentApprover coverage
Awareness logged latencycrisis_logper incidentClock honesty
Materiality determination lagLegal processper incidentSEC-window risk
Drill SLA deltacommatrix drillquarterlyTabletop output
Matrix validation driftcommatrix gatequarterlyOwnership churn
Impersonation attemptsOut-of-band verifyper incidentDuring-crisis attacks
Statement fact-auditComms reviewper statementFacts vs inference
Post-incident comms RCABT4 programper incidentLearning loop

Adversaries attack the response itself, deliberately. Command impersonation: during the chaos, “the IC” messages the helpdesk via the compromised chat to reset an account or open a firewall rule — out-of-band verification of command instructions (a callback to the bridge number) is the control; the impersonation playbook mirrors the helpdesk-reset social engineering BT15 covers. Urgency-phishing the responders: real incidents trigger mass “urgent password reset” phishing waves while everyone is distracted — responders’ own MFA and the out-of-band channel discipline are the countermeasures, because the attacker’s second wave rides the first incident’s email storms. Narrative injection: fake “official” statements on social accounts (or on lookalike domains — BT18’s watch covers this) poison the narrative clock and force the real comms lead to spend hours on debunking; pre-agreed statement channels (the one URL/portal where the company actually speaks) cut the ambiguity. Channel siege: DDoS or compromise of the chat/email platform the command runs on — the out-of-band channel exists precisely for this, and it must be pre-provisioned (personal-account workarounds during an incident create policy and evidence nightmares). Regulator-targeting: attackers sometimes weaponize notification itself — reporting “your breach” to regulators first with poisoned details, forcing the company to reconcile its clock against an adversary’s filing; the honest, timestamped decision log is the only durable answer.

⚠️ What caught me: our SEV1 ran textbook for six hours — clean command, hourly sits — and then the CEO’s chief of staff called the helpdesk personally, on the phone, voice verified by name recognition, and ordered a “temporary exception” that punched a hole in our isolation perimeter. It was a real human, really believing they were helping, following instructions someone had relayed to them from a compromised account. No technical control stopped it, because the exception path was designed for exactly that call. Since then, command decisions in SEV1 require a decision-log entry with an approver before any exception executes — crisistimeline’s fail-loud on approver-less decisions is not bureaucracy, it is the only thing that stopped the second phone call. Crisis leadership fails socially before it fails technically.

Run a full communication tabletop in half a day. Setup: author the stakeholder matrix (8 rows, 3 channels), validate with commatrix, and pre-draft two holding statements (one vague, one with facts). Inject: a facilitator runs a ransomware SEV1 script — encrypted file servers, extortion note, a journalist call at T+2h, a regulator email at T+4h. Exercise: the team must log awareness (fact), run clocks, generate hourly sitreps, make three decisions with approvers, route the journalist through the funnel, and take the materiality-determination decision through the pre-agreed procedure. Adversarial twist: the facilitator attempts one command impersonation (“IC here, approve the firewall change”) — the team must out-of-band verify. Review: grade the decision log (completeness, facts/inference separation, cadence kept) and the drill’s SLA-vs-actual deltas. Debrief: which role was missing when the pressure hit — and was it a person gap or a matrix gap?

  • Technical: Walk the four sitrep sections and explain why facts and inference must never share a section — give the failure that separation prevents.
  • Technical: Why does the SEC’s four-business-day disclosure window start at the materiality determination rather than awareness, and what process artifact does that distinction demand?
  • Scenario: Hour two of a SEV1: legal and the IC disagree on whether the event is a notifiable breach, and the 72-hour clock is running. Walk the decision process, not the answer.
  • Scenario: A journalist has credible details you haven’t published. Walk the spokesperson-funnel response and what the pre-approved statement lets you do in the next 30 minutes.
  • Conceptual: Why is a decision log with named approvers a technical control against social engineering, not just paperwork?
  • Deep-dive: Design crisis communications for a fintech with 2M customers: command structure, matrix, regulatory clock management across three jurisdictions, and the tabletop program.
  • Practical: Your last real incident’s post-mortem shows the first sitrep went out 4 hours late and three decisions lack approvers. What do you change — people, process, or tooling — and in what order?

BT4 (Incident Response Process) · BT1 (SOC Operations & Alert Triage) · BT20 (Detection Program Metrics & Maturity) · BT18 (Digital Risk & Brand Protection)