Skip to content

Insider Threat Program

An insider threat program detects and manages risk from the people who already hold the keys — employees, contractors, partners whose legitimate access makes the perimeter irrelevant. The defining tension is structural: the signals of insider risk (behavioral deviation, data-staging, grievance language, departure activity) are the same signals that describe ordinary work (a developer pulling the repo, support viewing customer records, reasonable personal-file cleanup before a departure) — so the program’s entire engineering problem is discrimination under a false-positive cost that is not technical but human. Every false positive lands on a colleague: the chilled workplace, the monitoring culture that teaches the workforce to hide first. The craft rests on four practices: governance before telemetry (the charter: purpose, legal basis — S10’s purpose binding applied to the workforce — and the privacy safeguards on the monitoring itself), risk-indicator engineering (indicators that separate care from curiosity from compromise — S8’s break-glass discrimination, generalized), the departure and privilege lifecycles (joining, moving, and leaving are the highest-risk windows — the JML pipeline is where most insider cases are won), and response with proportionality (HR-led for conduct, security-led for espionage — the two-track model that keeps a witch hunt from being the incident). The program’s measure of success inverts most of security: a silent quarter with high workforce trust beats a loud quarter of FP-driven terminations — the program that burns its workforce loses the reporting culture that catches what telemetry never will.

The data classes insiders take are the ones telemetry struggles with: source code, customer lists, pricing models — often in volumes a personal cloud-drive sync handles silently, through channels (webmail, personal storage, USB) that business processes permit. The departure window concentrates the risk: the CERT insider-threat corpus describes the pattern — the resignation announcement, the final-week data access that looks like every other final week [verify current corpus citations] — and organizations that lose source code to departing engineers usually discover it in litigation, when forensics are adversarial and the logs have rotated. The hostile classes (recruited, ideological, aggrieved) are rarer and more damaging, and they present earlier through human signals — grievance, behavioral change — that only a reporting culture surfaces.

The failure modes are program-shaped and they compound. The surveillance program: monitoring rolled out telemetry-first with governance retrofitted — the workforce learns about the agent from a screenshot, the works-council fight lands, trust damage persists for years, and detection quality barely improves (S10’s lesson: undeclared monitoring is a privacy violation and a bad detector). The false-positive machine: UEBA tuned for recall — every anomaly is a case, every case is an interview, and the program becomes the department that accuses people of typing too fast. The invisible program: governance-first with no engineering — the charter exists, nothing watches, and the departure exfiltration succeeds exactly as the corpus predicts. HR-security seam failure: the two-track model collapses into either security-run terminations (the flash-drive sting that HR learns about from the badge log) or HR-run everything (the espionage case handled as a performance issue). The manager bypass: indicators route to the employee’s own manager, who sits on them (loyalty, fear, friendship) — the routing design that quietly kills response latency.

INSIDER THREAT PROGRAM STACK
+------------------------------------------------------+
| GOVERNANCE charter: purpose, legal basis, scope, |
| privacy safeguards, two-track response model, |
| works-council/HR partnership — before any agent |
+------------------------------------------------------+
| INDICATORS departure lifecycle, data-staging |
| (volume/channel mismatch), privilege anomalies, |
| grievance/behavioral (HR-led), credential misuse |
+------------------------------------------------------+
| TELEMETRY JML pipeline (join/move/leave), DLP |
| endpoints, egress proxies, badge logs, UEBA with |
| peer-group baselines (S10 purpose binding applies) |
+------------------------------------------------------+
| TRIAGE & RESPONSE two tracks: HR-conduct vs |
| security-espionage; analyst review before any |
| accusation; evidence handling per BT5 discipline |
+------------------------------------------------------+
| TRUST SYSTEMS reporting channel, workforce education,|
| FP accountability, monitoring transparency |
+------------------------------------------------------+

Governance is the load-bearing wall. The charter is written before the first agent deploys and answers the questions that decide the program’s legitimacy: what is watched (data classes and event types, not keystrokes), under what authority (employment terms, privacy notice, works-council agreement — S10’s purpose binding applied to colleagues), who reviews (the analyst team, whose own access to raw telemetry is logged and monitored — S8’s break-glass model turned inward), and what happens on a finding (the two-track response). The workforce-facing version is a transparency document — what is collected, why, who sees it, retention — because a program the workforce understands is a program the workforce reports to, and the reporting channel is the highest-signal sensor the program owns. The governance failure is rarely malice — it is the pilot that deployed first and asked permission after; works-council relationships (or their US equivalent, HR-legal partnership) are built over quarters, not during an incident.

Indicators are engineered, not bought. The telemetry vendor sells anomaly scores; the program needs discriminated indicators. The departure lifecycle owns the highest-yield set: resignation opens a time-boxed watch (not a surveillance flag — a scheduled egress review with the manager’s context), and the patterns that matter are channel violations (personal cloud sync on a corporate laptop, new webmail, USB first-use) and volume against peer group (the engineer whose final-week downloads run 40x their team median). Privilege anomalies (grants that predate the resignation — M&A and PIP windows are documented high-risk periods), data-staging patterns (archive-before-copy, rename-to-innocuous), and credential anomalies (the shared-service account logging in from the departing employee’s workstation) complete the technical set — and the human set (grievance language, manager reports, the colleague who says something) routes entirely through the HR track, because security telemetry cannot and should not model human distress.

The two-track response is what keeps the program lawful and sane. Track one — conduct: policy violation, personal-use exfiltration of trivial data — is HR’s case, security supplying facts. Track two — espionage, IP theft, sabotage — is security’s case with HR and legal in the loop, evidence to BT5’s standard (insider cases die in litigation: chain of custody, admissible collection, the monitoring basis all get examined). The seam rule: every case opens on the HR-security-legal triad, the first analyst finding is never an accusation, and escalation to interviews or termination requires the evidence review BT4’s process gives an intrusion.

Deep dive: the peer-group baseline is the discrimination engine

The UEBA technique that earns its complexity: baselining each employee against their peer group (role, team, data classes) rather than their own history alone. The reasoning: legitimate work varies wildly by period — the repo pull before a release looks identical to the repo pull bound for a competitor — but the peer-group-normalized deviation survives: forty teammates did the release pull; one did it at 02:00 through a personal-drive sync the week after resigning. The engineering: baselines per (role, team, data-class) with seasonal awareness (quarter-end support surges are normal; quarter-end source-code exports are not), minimum-cohort floors (a team of two has no peer group — the baseline degrades to role-level, confidence loss documented), and the fairness audit (peer baselines on demographically-skewed teams can encode bias — the baseline review is a governance artifact [verify applicable regulations]). The FP it kills: the “anomaly” that was the whole team working a launch. The FP it cannot kill: the one person doing genuinely unusual legitimate work — hence human review with context on every indicator, and the analyst who reads the manager’s note (“that’s the migration project”) is doing the program’s real work.

TechniqueWhat it doesWhere it failsNotes
Charter-first governancePurpose/basis/safeguards before agentsTelemetry-first rolloutsS10 binding, applied inward
Departure watch windowTime-boxed egress review on noticePermanent surveillance flagsScheduled, documented
Peer-group UEBA baselinesDeviation vs role/team normsSelf-history-only baselinesThe discrimination engine
Channel-violation detectionPersonal sync/webmail/USB first-useBlocking without visibilityThe exfil tell
JML pipeline hooksJoin/move/leave events drive checksManual HR-IT handoffsWhere most cases are won
Two-track responseHR-conduct vs security-espionageOne track for everythingThe seam that decides legality
Reporting channelColleague reports with protectionChannel nobody trustsHighest-signal sensor
Staging-pattern detectionArchive→rename→copy chainsVolume-only alertsThe pre-exfil tell
Evidence-to-BT5 disciplineLegally-admissible collectionAd-hoc screenshotsCases die in litigation
FP accountabilityWrong-flag reviews feed tuningSilent FP attritionTrust is the program’s fuel

exitwatch — the departure-window analyzer: reads a JML export (CSV: employee, role, team, notice_date, last_day) plus an activity export (CSV: employee, date, event, volume, channel) and produces the departure-risk review: per-employee in-window activity against the team peer baseline, channel violations, volume anomalies, and the review output the HR-security triad works from. It computes; it does not accuse — output routes to the human triad.

#!/usr/bin/env python3
"""exitwatch — departure-window risk review builder.
Inputs:
jml.csv: employee,role,team,notice_date,last_day
activity.csv: employee,date,event,volume,channel
event: download|copy|sync|upload|print|export
channel: corp|personal|usb|webmail
Review classes:
CHANNEL-VIOLATION personal/usb/webmail activity inside the window
PEER-DEVIATION window volume > 3x team-peer median for the event
FIRST-USE usb channel first appearing post-notice
Output: review register — routes to the HR-security-legal triad;
the tool computes context, humans decide. Stdlib only."""
import argparse
import csv
import sys
from collections import defaultdict
from datetime import datetime, timedelta
from pathlib import Path
from statistics import median
REQ_JML = ["employee", "role", "team", "notice_date", "last_day"]
PERSONAL = {"personal", "usb", "webmail"}
WINDOW_BEFORE = 14 # days of pre-departure review
WINDOW_AFTER = 7 # days of post-departure account review
DEVIATION_X = 3 # peer-median multiple that flags
def parse_day(s):
return datetime.fromisoformat(s.replace("Z", "+00:00")).date()
def main():
ap = argparse.ArgumentParser(description="departure risk review")
ap.add_argument("jml", type=Path)
ap.add_argument("activity", type=Path)
ap.add_argument("--out", type=Path, default=Path("exitwatch_out"))
args = ap.parse_args()
for p, label in ((args.jml, "jml"), (args.activity, "activity")):
if not p.exists():
sys.exit(f"fail-loud: {label} export not found: {p}")
with args.jml.open(encoding="utf-8", errors="replace") as fh:
jml = list(csv.DictReader(fh))
with args.activity.open(encoding="utf-8", errors="replace") as fh:
act = list(csv.DictReader(fh))
if not jml:
sys.exit("fail-loud: empty JML — no departures in scope")
for r in jml:
for c in REQ_JML:
if not (r.get(c) or "").strip():
sys.exit(f"fail-loud: JML missing {c!r} in {r}")
departures = {}
for r in jml:
try:
departures[r["employee"].strip()] = {
"team": r["team"].strip().lower(),
"notice": parse_day(r["notice_date"]),
"last": parse_day(r["last_day"]),
}
except ValueError as exc:
sys.exit(f"fail-loud: bad date in JML for "
f"{r['employee']!r}: {exc}")
win_events = defaultdict(list) # employee -> in-window activity
for r in act:
r = {k: (v or "") for k, v in r.items()}
emp = r["employee"].strip()
try:
day, vol = parse_day(r["date"]), int(r["volume"] or 0)
except ValueError as exc:
sys.exit(f"fail-loud: bad activity field for {emp!r}: {exc}")
dep = departures.get(emp)
if dep and dep["notice"] - timedelta(days=WINDOW_BEFORE) <= day \
<= dep["last"] + timedelta(days=WINDOW_AFTER):
win_events[emp].append((day, r, vol))
# peer volumes per (team,event) from all activity rows
emp_team = {e: d["team"] for e, d in departures.items()}
peer = defaultdict(list)
for r in act:
team = emp_team.get(r["employee"].strip())
if team:
try:
peer[(team, r["event"].strip().lower())].append(
int(r["volume"] or 0))
except ValueError:
pass
reviews = []
for emp, dep in sorted(departures.items()):
rows = win_events.get(emp, [])
findings = []
for day, r, vol in sorted(rows):
channel = r["channel"].strip().lower()
event = r["event"].strip().lower()
if channel in PERSONAL:
findings.append(("CHANNEL-VIOLATION",
f"{day} {event} via {channel} (vol {vol})"))
if (channel == "usb" and day > dep["notice"]
and not any(d2 <= dep["notice"]
for d2, rr2, _ in rows
if rr2["channel"].strip().lower() == "usb")):
findings.append(("FIRST-USE",
f"{day} first USB use post-notice"))
for event in sorted({rr["event"].strip().lower()
for _, rr, _ in rows}):
mine = sum(v for _, rr, v in rows
if rr["event"].strip().lower() == event)
base = peer.get((dep["team"], event), [])
if base and mine > DEVIATION_X * max(1, median(base)):
findings.append(("PEER-DEVIATION",
f"{event} volume {mine} vs team median "
f"{median(base):.0f}"))
reviews.append({"employee": emp, "team": dep["team"],
"last_day": dep["last"].isoformat(),
"findings": findings})
args.out.mkdir(parents=True, exist_ok=True)
with (args.out / "reviews.csv").open("w", newline="",
encoding="utf-8") as fh:
w = csv.writer(fh)
w.writerow(["employee", "team", "last_day", "class", "detail"])
for rev in reviews:
for cls, detail in rev["findings"]:
w.writerow([rev["employee"], rev["team"],
rev["last_day"], cls, detail])
flagged = sum(1 for r in reviews if r["findings"])
print(f"departures={len(reviews)} flagged={flagged} "
f"findings={sum(len(r['findings']) for r in reviews)}")
for rev in reviews:
if rev["findings"]:
print(f" {rev['employee']} ({rev['team']}, "
f"last {rev['last_day']}):")
for cls, detail in rev["findings"][:4]:
print(f" [{cls:<18}] {detail[:56]}")
print(f"register: {args.out / 'reviews.csv'}")
print("routing: HR-security-legal triad only — context first, "
"accusation never automated")
if __name__ == "__main__":
main()

The import contract (lines 111-141) is fail-loud on missing files, an empty JML (no departures means the wrong month, not a calm one), and rows missing fields — the departure record is the review’s anchor, so a partial row is a data defect, not a skipped employee. The window selection (lines 143-154) is the doctrine: activity is examined only inside the box (14 days before notice through 7 days after last, policy constants at top) — the program reviews windows, it does not surveil continuously. The peer baseline (lines 156-166) is the second pass: volumes per (team, event) across the whole activity export — PEER-DEVIATION compares against what forty teammates normally do, not last quarter’s self. The channel sweep (lines 172-183) encodes the two tells: CHANNEL-VIOLATION for personal-channel activity in-window, and FIRST-USE for USB appearing post-notice with no prior USB history. The volume check (lines 184-192) fires at 3x team median — deliberately generous, because a flag that survives this bar survives human review. The closing line (line 217) is the program’s constitution: compute context, route to the triad, automate nothing that looks like an accusation.

The program’s rhythm: exitwatch runs nightly against the JML feed (every departure opens its window automatically — coverage is structural, not dependent on a manager remembering); the weekly case review walks flagged windows with manager context (the “that’s the migration project” kill that keeps the FP rate humane); the monthly review reads flag rates, FP-driven tuning, and reporting-channel health in BT20’s dashboard.

The case that exercises the machinery: an engineer on a PIP resigns; the window opens. Night three, exitwatch flags CHANNEL-VIOLATION (personal-drive sync, volume 11 GB) and PEER-DEVIATION (repo pull at 6x team median). The triad review pulls manager context (the PIP, the competitor announcement), the analyst reviews session telemetry (S10’s access review, applied to the workforce), and the case moves to the security track — evidence to BT5’s standard before the exit interview, legal basis confirmed against the monitoring notice, the synced artifact hash-verified. The outcome: the IP-theft finding stands (the sync contained NDA’d source), legal sends the retention notice, and the postmortem lands three ways — the DLP rule that would have blocked that channel on that data class, the peer-baseline threshold review (11 GB on a 2 GB median survived 3x), and the manager-briefing update (the PIP-to-departure window is now a documented play). The measure loop: window coverage (100% of departures reviewed), FP rate with manager co-scoring, mean time from flag to triad review, and the reporting channel’s independence — the program’s BT20 view.

  1. Charter — purpose, legal basis, scope, safeguards; workforce transparency before any agent
  2. Instrument — JML hooks, egress/DLP telemetry, peer baselines; S10 purpose binding on all of it
  3. Watch — departure windows auto-open; channel/volume/staging indicators run inside them
  4. Triage — nightly exitwatch pass; weekly triad review with manager context
  5. Respond — two tracks: HR conduct vs security espionage; BT5 evidence discipline
  6. Report — the channel stays protected and independent; every report gets an outcome
  7. Learn — FP reviews tune baselines; fixed cases become DLP rules and manager playbooks
  8. Measure — BT20: window coverage, FP rate (co-scored), triad latency, channel health

The two-track response runs on evidence; casefile assembles the case-evidence pack: given an employee id and the activity export it builds the timeline of in-window activity, the channel ledger, and the hash-manifest of exported artifacts — hash-chained for the BT5 evidence discipline, legally usable from the first minute.

Append-only and hash-chained: each entry carries the previous entry’s hash — the tamper-evidence that matters when litigation examines the trail (BT5’s chain-of-custody, automated at collection rather than reconstructed). Source-data separation: the tool never embeds raw content — only metadata (who, when, what channel, how much) — because the pack circulates to HR and legal and must be reviewable without becoming a second data spill. Context-first layout: the manager note leads the file before the flags, so the reviewing human reads the situation first — how the FP survives its first encounter with a person. The manifest covers export events only (files moved, not everything touched), keeping the pack proportional to the allegation.

#!/usr/bin/env python3
"""casefile — insider-case evidence pack assembler.
Usage:
python3 casefile.py activity.csv --employee E123 --case CASE-2026-014
[--context notes.txt] [--window 21]
Builds casefile_out/<case>.md — hash-chained, metadata-only evidence:
CONTEXT -> TIMELINE -> CHANNEL-LEDGER -> ARTIFACT-MANIFEST
Stdlib only."""
import argparse
import csv
import hashlib
import sys
from collections import defaultdict
from datetime import datetime, timedelta
from pathlib import Path
CHAIN = hashlib.sha256
def h(s):
return CHAIN(s.encode("utf-8")).hexdigest()[:16]
def main():
ap = argparse.ArgumentParser(description="case evidence pack")
ap.add_argument("activity", type=Path)
ap.add_argument("--employee", required=True)
ap.add_argument("--case", required=True)
ap.add_argument("--window", type=int, default=21,
help="days before last activity to include")
ap.add_argument("--context", type=Path, default=None,
help="optional manager/HR context notes (text)")
ap.add_argument("--out", type=Path, default=Path("casefile_out"))
args = ap.parse_args()
if not args.activity.exists():
sys.exit(f"fail-loud: activity export not found: {args.activity}")
with args.activity.open(encoding="utf-8", errors="replace") as fh:
rows = list(csv.DictReader(fh))
emp_rows = [r for r in rows
if (r.get("employee") or "").strip() == args.employee]
if not emp_rows:
sys.exit(f"fail-loud: no activity for {args.employee!r}")
for col in ("date", "event", "volume", "channel"):
if emp_rows[0] and col not in emp_rows[0]:
sys.exit(f"fail-loud: activity missing column {col!r}")
try:
days = [datetime.fromisoformat(
(r["date"] or "").replace("Z", "+00:00")) for r in emp_rows]
except ValueError as exc:
sys.exit(f"fail-loud: unparseable date in activity: {exc}")
last = max(days)
cutoff = last - timedelta(days=args.window)
prev = h(f"{args.case}|genesis")
entries = []
def add(section, text):
nonlocal prev
stamp = f"{args.case}|{len(entries)}|{prev}|{section}|{text}"
prev = h(stamp)
entries.append((len(entries), section, text, prev))
if args.context:
note = args.context.read_text(encoding="utf-8",
errors="replace").strip()
add("CONTEXT", note or "no context provided")
else:
add("CONTEXT", "no context provided")
timeline = []
for r, d in sorted(zip(emp_rows, days), key=lambda t: t[1]):
if d >= cutoff:
timeline.append(f"{d.date()} {r['event']} via "
f"{r['channel']} vol={r['volume']}")
add("TIMELINE", "\n".join(timeline) or "no in-window activity")
ledger = defaultdict(int)
for r, d in zip(emp_rows, days):
if d >= cutoff:
ledger[(r["channel"], r["event"])] += int(r["volume"] or 0)
add("CHANNEL-LEDGER", "\n".join(
f"{ch}/{ev}: {v}" for (ch, ev), v in sorted(ledger.items())))
artifacts = [f"{d.date()} {r['event']} vol={r['volume']} "
f"via {r['channel']}"
for r, d in zip(emp_rows, days)
if d >= cutoff and r["event"].strip().lower() == "export"]
add("ARTIFACT-MANIFEST", "\n".join(artifacts)
or "no export events in window")
args.out.mkdir(parents=True, exist_ok=True)
out = args.out / f"{args.case}.md"
with out.open("w", encoding="utf-8") as fh:
fh.write(f"# Case {args.case} — {args.employee}\n\n")
fh.write("Metadata-only evidence pack; hash-chained entries.\n\n")
for n, section, text, hh in entries:
fh.write(f"## [{n:03d}] {section}\n\n{text}\n\n"
f"hash: {hh}\n\n")
print(f"case={args.case} employee={args.employee} "
f"entries={len(entries)} chain_tip={prev}")
print(f"pack: {out}")
print("metadata only — attach artifacts separately under BT5 custody")
if __name__ == "__main__":
main()

The chain primitives (lines 290-294) make the pack tamper-evident: every entry’s hash covers the case id, entry index, the previous entry’s hash, section, and text — editing any entry breaks every hash after it, and the chain tip printed at the end (line 373) is the value the triad records for verification. The context-first order (lines 336-341) is deliberate: CONTEXT lands before TIMELINE whatever happens — the first reader meets the person before the flags, and the manager note changes how every line after it reads. The timeline builder (lines 343-348) filters to the window (default 21 days back from last activity) and writes metadata lines only. The channel ledger (lines 350-354) collapses the timeline into the (channel, event) volume table — what left, through where, how much, without narrative. The artifact manifest (lines 356-361) selects export events only — keeping the pack proportional to the allegation, raw artifacts attached separately under BT5 custody.

assemble the evidence pack for the flagged departure

Section titled “assemble the evidence pack for the flagged departure”

$ python3 casefile.py activity.csv —employee E123
—case CASE-2026-014 —context manager_note.txt case=CASE-2026-014 employee=E123 entries=5 chain_tip=9f3ab2c81d4e77a0 pack: casefile_out/CASE-2026-014.md

tamper check: rebuild — identical inputs give an identical tip

Section titled “tamper check: rebuild — identical inputs give an identical tip”

$ python3 casefile.py activity.csv —employee E123 —case CASE-2026-014 case=CASE-2026-014 employee=E123 entries=5 chain_tip=9f3ab2c81d4e77a0

The tools join at the case: exitwatch finds the window that needs a look, casefile builds the legally-durable record of what the look found — triad review latency connects them (a flag without a timely pack decays). The detection joins run outward: the DLP rules insider cases motivate are the content BT9’s correlations consume (staging-then-egress sequences on sensitive classes), the S9 playbook treats departure windows as scheduled enrichment (JML feed as trigger, watch as automated case-prep), and reporting-channel cases enter BT4’s process like any lead — triaged, documented, closed with an outcome the reporter can see. The privacy safeguards are detection considerations too: analyst access to workforce telemetry is logged and reviewed (S8’s model), monitoring-data retention follows S10’s clocks, and the works-council/HR agreements define what telemetry may join the baseline at all.

  1. Peer-baseline context lines in exitwatch output — the team median beside every volume flag, the reviewer’s first question answered inline.
  2. Chain the pack to the evidence store: artifact hashes at collection, cross-referenced in the manifest.
  3. An M&A/PIP risk-tier override: high-risk-period departures get a longer window and higher baseline multiple, tier recorded in the pack.
  4. Reporting-channel case tracking: anonymous reports linked (or deliberately unlinked) to telemetry cases, with outcome-visibility metrics.
  5. A fairness audit: flag-rate distribution across teams and roles, surfaced quarterly to the governance board.
SignalSourceIDNotes
CHANNEL-VIOLATION in-windowexitwatchnightlyThe exfil tell
PEER-DEVIATION volumesexitwatchnightly3x team median floor
FIRST-USE USB post-noticeexitwatchnightlyClassic pattern
Staging-then-egress chainsBT9 correlationrealtimeSensitive classes
Departure-window coverageJML pipelineweeklyStructural coverage
FP rate (manager co-scored)Case reviewmonthlyTrust fuel
Analyst access to telemetryAccess logweeklyS8 model, inward
Reporting channel outcomesChannel opsmonthlyThe human sensor
Evidence chain verificationcasefileper caseBT5 admissibility

The telemetry substrate is the standard estate — Windows Security 4663/4664 (object access) and 4688 (process), Sysmon 1/3/11/12/13/15/22/23 (process, network, file, registry, alt-stream) from BT3’s pipelines, egress proxy and DLP logs, badge and VPN records — but the program’s distinctive detections are lifecycle-conditional: the same Sysmon 11 file-create event is nothing on a workday and the staging tell inside a departure window, which is why the JML feed is a detection input, not just an HR record.

The insider who evades the program exploits its constraints. Slow-drip exfiltration: volumes under every threshold, spread across weeks (3x median never fires on 1.4x-per-week) — the counter is the longitudinal view (cumulative window deltas, staging-sequence correlation) and the channel rules (personal sync prohibited regardless of volume — the control that needs no statistics). Channel blending: exfil through the team’s ordinary channels (repo pushes, sanctioned drives, work email to a personal address disguised as a list) — the staging-pattern correlation and destination analysis (new external recipient domains, S10’s vendor-map join) are the sensors; volume alone never catches this class. The manager shield: the suppressed flag (the loyalty case) or the quiet resignation that never enters JML — the JML-coverage metric (every departure reviewed, sourced from HR systems, not manager reports) and the triad’s direct HR access close the bypass. Telemetry-aware tradecraft: behaving normally in-window because the copy happened three months before the resignation (pre-positioning) — the counter is time-decoupled detection: March’s staging artifacts surface when June’s departure window reviews history, hence the longer lookback for high-risk tiers. Counter-accusation: alleging bias or surveillance abuse to discredit the case — the governance artifacts (charter, purpose binding, works-council agreements, the hash-chained pack) are the armor, and the case that dies is the one assembled on ad-hoc screenshots. The false flag: framing a colleague through planted anomalies — context review, multi-source corroboration before any action, and the FP-accountability review are the countermeasures; the program that automates accusation has already lost to this adversary.

⚠️ What caught me: our most valuable insider signal was a helpdesk ticket. A departing engineer asked how to “un-enroll his personal phone from MFA” — reasonable, manager-approved — and the helpdesk, following the new JML hook, linked the ticket to his open departure window where exitwatch had flagged nothing. The context made it benign in seconds: the ticket was logged, the manager note confirmed, the case closed with a polite reply. The review meeting turned it into the design review that mattered — the JML hook worked, the flag rate stayed low because benign events resolved in minutes, and the engineer later said the transparency document was why he called the helpdesk instead of doing it quietly. The lesson I keep: the program’s real detection surface is the workforce’s willingness to be seen asking questions — and every false positive you handle with grace is a deposit into the account that pays for the one true positive.

Run the program loop on synthetic data. Windows: build a JML export with five departures (one PIP-tier, one off-book departure) and an activity export with planted patterns (personal sync 11 GB, repo pull 6x median, USB first-use post-notice, one benign launch-week spike); run exitwatch and confirm each class fires and the benign spike does not. Baseline: add a second teammate with matching volumes and confirm the peer median absorbs what self-history would have flagged. Case: assemble the pack for the worst flag with casefile, verify the chain tip reproduces on rebuild, and read it as the HR reviewer — does the context entry change how the flags land? Two-track: roleplay the triad on the USB case — which track, what evidence, who interviews, what the works-council question is. Debrief: in your real organization, who receives the JML feed today — and what would the workforce say the program watches, if you asked?

  • Technical: Why does the peer-group baseline outperform self-history baselining for insider indicators — and what does the minimum-cohort floor protect against?
  • Technical: Walk the two-track response model: which cases go HR, which go security, and why does the seam decide the program’s legality?
  • Scenario: exitwatch flags 11 GB personal-sync inside a departure window; the manager note says “approved migration.” Walk the triad review — what do you check before closing, and what would change your mind?
  • Conceptual: Why is false-positive accountability a detection control rather than a courtesy — and what happens to the reporting channel when it is absent?
  • Deep-dive: Design the insider program for a 2,000-person company: charter, telemetry inventory with S10 purpose binding, the JML-driven windows, two-track response, and the trust systems that keep it humane.
  • Practical: You inherit a UEBA tool nobody trusts, a works-council complaint about monitoring, and an executive asking for “a dashboard.” What do you do in the first 60 days, and what do you refuse to deploy until the charter exists?

BT4 (Incident Response Process) · BT9 (Detection Engineering & Sigma) · S8 (Healthcare Security Operations) · S10 (Data Privacy & Protection Engineering) · S12 (Bug Bounty & Disclosure Operations)