Skip to content

Instantly share code, notes, and snippets.

@subtleGradient
Created May 14, 2026 15:59
Show Gist options
  • Select an option

  • Save subtleGradient/d190e4a72564cecd1c7a1405917c5792 to your computer and use it in GitHub Desktop.

Select an option

Save subtleGradient/d190e4a72564cecd1c7a1405917c5792 to your computer and use it in GitHub Desktop.
macOS gh-token-monitor dead-man triage checklist
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>macOS gh-token-monitor Dead-Man Triage Checklist</title>
<style>
:root {
color-scheme: light dark;
--bg: #0f172a;
--panel: #111827;
--panel-2: #1f2937;
--text: #e5e7eb;
--muted: #a7b0c0;
--line: #374151;
--accent: #93c5fd;
--green: #86efac;
--yellow: #fde68a;
--red: #fca5a5;
--code: #020617;
}
@media (prefers-color-scheme: light) {
:root {
--bg: #f8fafc;
--panel: #ffffff;
--panel-2: #f1f5f9;
--text: #0f172a;
--muted: #475569;
--line: #cbd5e1;
--accent: #1d4ed8;
--green: #166534;
--yellow: #854d0e;
--red: #991b1b;
--code: #0f172a;
}
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font: 16px/1.55 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
main {
max-width: 980px;
margin: 0 auto;
padding: 32px 18px 64px;
}
h1, h2, h3 { line-height: 1.2; }
h1 { margin: 0 0 12px; font-size: clamp(1.9rem, 4vw, 3rem); }
h2 { margin: 36px 0 12px; font-size: 1.55rem; }
h3 { margin: 22px 0 10px; font-size: 1.12rem; }
p { margin: 10px 0; }
a { color: var(--accent); }
.summary, .step, .gate, .danger, .note {
border: 1px solid var(--line);
border-radius: 14px;
padding: 16px;
background: var(--panel);
margin: 16px 0;
}
.summary {
background: linear-gradient(135deg, var(--panel), var(--panel-2));
}
.danger { border-color: var(--red); }
.gate { border-color: var(--yellow); }
.note { border-color: var(--accent); }
.checklist {
display: grid;
gap: 10px;
margin: 12px 0 0;
padding: 0;
list-style: none;
}
.checklist li {
display: grid;
grid-template-columns: 24px 1fr;
gap: 10px;
align-items: start;
}
input[type="checkbox"] {
width: 18px;
height: 18px;
margin-top: 3px;
accent-color: var(--accent);
}
code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
font-size: 0.95em;
}
pre {
overflow-x: auto;
border-radius: 12px;
border: 1px solid var(--line);
padding: 14px;
background: var(--code);
color: #e5e7eb;
font-size: 0.92rem;
line-height: 1.45;
}
.status {
display: inline-block;
padding: 2px 8px;
border-radius: 999px;
font-weight: 700;
letter-spacing: 0.02em;
}
.green { color: var(--green); }
.yellow { color: var(--yellow); }
.red { color: var(--red); }
.muted { color: var(--muted); }
.small { font-size: 0.92rem; }
</style>
</head>
<body>
<main>
<h1>macOS gh-token-monitor Dead-Man Triage Checklist</h1>
<section class="summary">
<p><strong>Goal:</strong> from macOS Recovery or an otherwise offline boot, prove that the reported <code>gh-token-monitor</code> dead-man switch is missing, or move it out of all autoload paths before you revoke or rotate GitHub tokens.</p>
<p><strong>Core invariant:</strong> do not let a compromised user session, LaunchAgent, editor hook, or package lifecycle hook execute before the dead-man path is neutralized.</p>
<p class="muted small">This checklist is derived from <code>CONTEXT.md</code>. It intentionally prefers quarantine over deletion so evidence remains available.</p>
</section>
<section class="danger">
<h2>Hard Stops</h2>
<ul class="checklist">
<li><input type="checkbox"><span>Keep the Mac offline: unplug Ethernet, keep Wi-Fi off, and do not connect to VPN or captive portals.</span></li>
<li><input type="checkbox"><span>Do not revoke GitHub tokens, npm tokens, or OAuth grants yet. Revocation can trigger the reported monitor if it is still active.</span></li>
<li><input type="checkbox"><span>Do not log into the normal user session until the LaunchAgent checks below are green or the suspicious files have been quarantined.</span></li>
<li><input type="checkbox"><span>Do not open VS Code, Claude Code, terminals with project startup hooks, or run <code>npm</code>, <code>pnpm</code>, <code>yarn</code>, <code>bun</code>, <code>npx</code>, <code>gh</code>, or repo scripts from the compromised volume.</span></li>
<li><input type="checkbox"><span>Do not run suspicious files to test them. Read them as text only, or move them to quarantine.</span></li>
</ul>
</section>
<section class="step">
<h2>1. Boot Into Recovery And Mount Data Read/Write</h2>
<ul class="checklist">
<li><input type="checkbox"><span>Boot into macOS Recovery.</span></li>
<li><input type="checkbox"><span>Use Disk Utility to mount and unlock the Data volume if FileVault is enabled.</span></li>
<li><input type="checkbox"><span>Open Terminal from Recovery.</span></li>
<li><input type="checkbox"><span>Confirm the mounted volumes. The compromised user home is under <code>/Volumes/&lt;Data volume&gt;/Users/&lt;user&gt;</code>, not Recovery's <code>~</code>.</span></li>
</ul>
<pre><code>diskutil list
ls -la /Volumes</code></pre>
<p>If the likely Data volume is named <code>Macintosh HD - Data</code>, use it below. Adjust the names if your volume or user differs.</p>
<pre><code>DATA="/Volumes/Macintosh HD - Data"
TARGET_USER="your-short-username"
USER_HOME="$DATA/Users/$TARGET_USER"
test -d "$DATA" &amp;&amp; echo "Data volume found: $DATA"
test -d "$USER_HOME" &amp;&amp; echo "User home found: $USER_HOME"</code></pre>
<div class="gate">
<p><strong>Gate:</strong> continue only when <code>$USER_HOME</code> points to the real user home on the mounted Data volume. If it does not, stop and fix the path.</p>
</div>
</section>
<section class="step">
<h2>2. Create A Quarantine And Evidence Folder</h2>
<p>Use a folder outside LaunchAgent, editor, shell startup, and package-manager paths. This disables autoload while preserving evidence.</p>
<pre><code>Q="$DATA/Users/Shared/gh-token-monitor-quarantine-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$Q"
chmod 700 "$Q"
echo "Quarantine: $Q"</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>The quarantine folder exists on the Data volume.</span></li>
<li><input type="checkbox"><span>The quarantine folder is not inside <code>LaunchAgents</code>, <code>LaunchDaemons</code>, <code>.claude</code>, <code>.vscode</code>, <code>node_modules</code>, or a package cache.</span></li>
</ul>
</section>
<section class="step">
<h2>3. Capture Exact Dead-Man Artifact Evidence</h2>
<p>These are the reported macOS/Linux dead-man paths. Missing files are good. Existing files must be treated as red until moved out of place.</p>
<pre><code>for p in \
"$USER_HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist" \
"$USER_HOME/.config/gh-token-monitor" \
"$USER_HOME/.config/gh-token-monitor/token" \
"$USER_HOME/.local/bin/gh-token-monitor.sh" \
"$DATA/private/var/root/Library/LaunchAgents/com.user.gh-token-monitor.plist" \
"$DATA/private/var/root/.config/gh-token-monitor" \
"$DATA/private/var/root/.local/bin/gh-token-monitor.sh"
do
if [ -e "$p" ]; then
echo "FOUND: $p" | tee -a "$Q/evidence.txt"
ls -laOe "$p" 2&gt;&amp;1 | tee -a "$Q/evidence.txt"
if [ -f "$p" ]; then
shasum -a 256 "$p" 2&gt;&amp;1 | tee -a "$Q/evidence.txt"
fi
else
echo "missing: $p" | tee -a "$Q/evidence.txt"
fi
done</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>Evidence was captured for any found exact-path artifact.</span></li>
<li><input type="checkbox"><span>Any <code>FOUND</code> result is marked red until the quarantine step is completed.</span></li>
</ul>
</section>
<section class="step">
<h2>4. Disable Exact Dead-Man Files</h2>
<p>This step moves the known monitor plist, monitor config directory, token file parent, and monitor script out of executable/autoload locations.</p>
<pre><code>mkdir -p "$Q/exact-artifacts"
for p in \
"$USER_HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist" \
"$USER_HOME/.config/gh-token-monitor" \
"$USER_HOME/.local/bin/gh-token-monitor.sh" \
"$DATA/private/var/root/Library/LaunchAgents/com.user.gh-token-monitor.plist" \
"$DATA/private/var/root/.config/gh-token-monitor" \
"$DATA/private/var/root/.local/bin/gh-token-monitor.sh"
do
if [ -e "$p" ]; then
id=$(/usr/bin/uuidgen 2&gt;/dev/null || /bin/date +%s)
dest="$Q/exact-artifacts/$id-$(basename "$p").disabled"
echo "QUARANTINE: $p -&gt; $dest" | tee -a "$Q/actions.txt"
mv -v "$p" "$dest" 2&gt;&amp;1 | tee -a "$Q/actions.txt"
fi
done</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>The exact LaunchAgent plist is absent from every user and root LaunchAgents path.</span></li>
<li><input type="checkbox"><span>The exact monitor config directory is absent from every user and root home path.</span></li>
<li><input type="checkbox"><span>The exact monitor shell script is absent from every user and root local bin path.</span></li>
</ul>
</section>
<section class="step">
<h2>5. Search All LaunchAgents And LaunchDaemons For Dead-Man Indicators</h2>
<p>Do not rely only on the exact plist name. Search user, system, and root LaunchAgent/LaunchDaemon locations for the known dead-man strings.</p>
<pre><code>PAT='gh-token-monitor|api\.github\.com/user|\.config/gh-token-monitor|\.local/bin/gh-token-monitor\.sh|rm[[:space:]]+-rf|IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner'
for d in \
"$USER_HOME/Library/LaunchAgents" \
"$DATA/Library/LaunchAgents" \
"$DATA/Library/LaunchDaemons" \
"$DATA/private/var/root/Library/LaunchAgents"
do
if [ -d "$d" ]; then
echo "Scanning $d" | tee -a "$Q/launch-scan.txt"
/usr/bin/grep -RInE "$PAT" "$d" 2&gt;&amp;1 | tee -a "$Q/launch-scan.txt"
else
echo "missing directory: $d" | tee -a "$Q/launch-scan.txt"
fi
done</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>No LaunchAgent or LaunchDaemon references <code>gh-token-monitor</code>.</span></li>
<li><input type="checkbox"><span>No LaunchAgent or LaunchDaemon references <code>api.github.com/user</code>.</span></li>
<li><input type="checkbox"><span>No LaunchAgent or LaunchDaemon references <code>.config/gh-token-monitor</code> or <code>.local/bin/gh-token-monitor.sh</code>.</span></li>
<li><input type="checkbox"><span>No LaunchAgent or LaunchDaemon contains <code>rm -rf</code> or <code>IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner</code>.</span></li>
</ul>
</section>
<section class="step">
<h2>6. Quarantine Matching Launch Plists</h2>
<p>If the previous search printed any plist matches, move those plists out of autoload paths. The command below automatically quarantines plist files whose contents match the dead-man pattern.</p>
<pre><code>mkdir -p "$Q/matching-plists"
for f in \
"$USER_HOME"/Library/LaunchAgents/*.plist \
"$DATA"/Library/LaunchAgents/*.plist \
"$DATA"/Library/LaunchDaemons/*.plist \
"$DATA"/private/var/root/Library/LaunchAgents/*.plist
do
[ -e "$f" ] || continue
if /usr/bin/grep -qE "$PAT" "$f"; then
id=$(/usr/bin/uuidgen 2&gt;/dev/null || /bin/date +%s)
dest="$Q/matching-plists/$id-$(basename "$f").disabled"
cp -p "$f" "$dest.evidence-copy"
echo "DISABLE PLIST: $f -&gt; $dest" | tee -a "$Q/actions.txt"
mv -v "$f" "$dest" 2&gt;&amp;1 | tee -a "$Q/actions.txt"
fi
done</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>Every plist that matched the dead-man pattern has been moved to <code>$Q/matching-plists</code>.</span></li>
<li><input type="checkbox"><span>No matching plist remains in <code>~/Library/LaunchAgents</code>, <code>/Library/LaunchAgents</code>, <code>/Library/LaunchDaemons</code>, or root LaunchAgents on the mounted Data volume.</span></li>
</ul>
</section>
<section class="step">
<h2>7. Check Editor Hooks That Could Recreate The Monitor</h2>
<p>The dead-man path is not safe if a tool hook can recreate it after login. Check home-level hooks first, then project hooks before opening editors.</p>
<pre><code>HOOK_PAT='gh-token-monitor|api\.github\.com/user|router_runtime\.js|setup\.mjs|SessionStart|runOn|folderOpen|IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner|rm[[:space:]]+-rf'
for p in \
"$USER_HOME/.claude/settings.json" \
"$USER_HOME/.claude/router_runtime.js" \
"$USER_HOME/.claude/setup.mjs" \
"$USER_HOME/.vscode/tasks.json" \
"$USER_HOME/.vscode/setup.mjs"
do
if [ -e "$p" ]; then
echo "Checking $p" | tee -a "$Q/editor-hook-scan.txt"
/usr/bin/grep -InE "$HOOK_PAT" "$p" 2&gt;&amp;1 | tee -a "$Q/editor-hook-scan.txt"
else
echo "missing: $p" | tee -a "$Q/editor-hook-scan.txt"
fi
done</code></pre>
<p>If a home-level hook matches, quarantine it before any normal login workflows or editor launches:</p>
<pre><code>mkdir -p "$Q/editor-hooks"
for p in \
"$USER_HOME/.claude/settings.json" \
"$USER_HOME/.claude/router_runtime.js" \
"$USER_HOME/.claude/setup.mjs" \
"$USER_HOME/.vscode/tasks.json" \
"$USER_HOME/.vscode/setup.mjs"
do
[ -e "$p" ] || continue
if /usr/bin/grep -qE "$HOOK_PAT" "$p"; then
id=$(/usr/bin/uuidgen 2&gt;/dev/null || /bin/date +%s)
dest="$Q/editor-hooks/$id-$(basename "$p").disabled"
cp -p "$p" "$dest.evidence-copy"
echo "DISABLE HOOK: $p -&gt; $dest" | tee -a "$Q/actions.txt"
mv -v "$p" "$dest" 2&gt;&amp;1 | tee -a "$Q/actions.txt"
fi
done</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>No home-level Claude <code>SessionStart</code> hook can invoke <code>setup.mjs</code>, <code>router_runtime.js</code>, <code>node</code>, <code>bun</code>, <code>curl</code>, or shell.</span></li>
<li><input type="checkbox"><span>No home-level VS Code <code>folderOpen</code> task can invoke <code>setup.mjs</code>, <code>node</code>, <code>bun</code>, <code>curl</code>, or shell.</span></li>
<li><input type="checkbox"><span>Do not open project folders in editors until repo-local <code>.claude</code> and <code>.vscode</code> hooks are checked later.</span></li>
</ul>
</section>
<section class="step">
<h2>8. Check Package Payload Indicators That Could Restore Persistence</h2>
<p>This is a containment check, not a full package-forensics pass. The purpose is to avoid running a package or editor hook that immediately recreates the monitor.</p>
<pre><code>PAYLOAD_PAT='router_init\.js|tanstack_runner\.js|router_runtime\.js|@tanstack/setup|github:tanstack/router#79ac49eedf|voicproducoes|svksjrhjkcejg|IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner|api\.github\.com/user|gh-token-monitor'
for d in \
"$USER_HOME/Work" \
"$USER_HOME/Projects" \
"$USER_HOME/Developer" \
"$USER_HOME/Documents" \
"$USER_HOME/.npm" \
"$USER_HOME/Library/pnpm" \
"$USER_HOME/Library/Caches" \
"$USER_HOME/.cache"
do
if [ -d "$d" ]; then
echo "Scanning $d" | tee -a "$Q/payload-scan.txt"
/usr/bin/grep -RInE "$PAYLOAD_PAT" "$d" 2&gt;&amp;1 | tee -a "$Q/payload-scan.txt"
fi
done</code></pre>
<ul class="checklist">
<li><input type="checkbox"><span>No active project, package cache, or package manager store shows dead-man or payload indicators.</span></li>
<li><input type="checkbox"><span>If indicators are found, do not run package-manager commands from that tree. Quarantine the repo or cache for later analysis from a clean environment.</span></li>
</ul>
</section>
<section class="step">
<h2>9. Re-run The LaunchAgent Verification</h2>
<p>This is the key dead-man gate. After quarantine, the LaunchAgent scan must produce no dead-man matches.</p>
<pre><code>for d in \
"$USER_HOME/Library/LaunchAgents" \
"$DATA/Library/LaunchAgents" \
"$DATA/Library/LaunchDaemons" \
"$DATA/private/var/root/Library/LaunchAgents"
do
if [ -d "$d" ]; then
echo "Verifying $d" | tee -a "$Q/final-launch-verify.txt"
/usr/bin/grep -RInE "$PAT" "$d" 2&gt;&amp;1 | tee -a "$Q/final-launch-verify.txt"
fi
done</code></pre>
<div class="gate">
<p><span class="status green">GREEN</span> No output lines show dead-man strings in active LaunchAgent or LaunchDaemon paths.</p>
<p><span class="status red">RED</span> Any output line still references <code>gh-token-monitor</code>, <code>api.github.com/user</code>, <code>.config/gh-token-monitor</code>, <code>.local/bin/gh-token-monitor.sh</code>, <code>rm -rf</code>, or the campaign string.</p>
<p><span class="status yellow">YELLOW</span> Any path could not be inspected, the Data volume was not mounted read/write, or you are unsure whether <code>$USER_HOME</code> is the real compromised home.</p>
</div>
</section>
<section class="step">
<h2>10. Offline Normal Boot Test</h2>
<p>Only do this after the LaunchAgent gate is green. Keep the network physically disconnected.</p>
<ul class="checklist">
<li><input type="checkbox"><span>Shut down from Recovery.</span></li>
<li><input type="checkbox"><span>Confirm Ethernet is unplugged and Wi-Fi will not auto-join.</span></li>
<li><input type="checkbox"><span>Boot normally and log into the user account.</span></li>
<li><input type="checkbox"><span>Do not open editors, terminals in project directories, browsers with developer sessions, or package managers yet.</span></li>
</ul>
<p>In a plain Terminal from the home directory, verify that the monitor is not loaded or running:</p>
<pre><code>launchctl print gui/$(id -u)/com.user.gh-token-monitor 2&gt;&amp;1
ps aux | /usr/bin/grep -E 'gh-token-monitor|api\.github\.com/user|router_runtime\.js|router_init\.js|tanstack_runner\.js|setup\.mjs' | /usr/bin/grep -v grep</code></pre>
<div class="gate">
<p><span class="status green">GREEN</span> <code>launchctl</code> cannot find <code>com.user.gh-token-monitor</code>, and <code>ps</code> shows no matching process.</p>
<p><span class="status red">RED</span> The service is loaded, a matching process exists, or a new LaunchAgent appears. Go back offline into Recovery and repeat quarantine with the new path.</p>
</div>
</section>
<section class="step">
<h2>11. If You Accidentally Booted Normally Before Triage</h2>
<p>Use this only if you are already in the compromised normal session. It is less ideal than Recovery. Keep the network disconnected.</p>
<pre><code>launchctl bootout gui/$(id -u) "$HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist" 2&gt;/dev/null
launchctl disable gui/$(id -u)/com.user.gh-token-monitor 2&gt;/dev/null
pkill -f 'gh-token-monitor|api\.github\.com/user|router_runtime\.js|router_init\.js|tanstack_runner\.js|setup\.mjs' 2&gt;/dev/null
mkdir -p "$HOME/gh-token-monitor-quarantine"
mv -v "$HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist" "$HOME/gh-token-monitor-quarantine/" 2&gt;/dev/null
mv -v "$HOME/.config/gh-token-monitor" "$HOME/gh-token-monitor-quarantine/" 2&gt;/dev/null
mv -v "$HOME/.local/bin/gh-token-monitor.sh" "$HOME/gh-token-monitor-quarantine/" 2&gt;/dev/null</code></pre>
<p>After this emergency disable, shut down, boot Recovery, and run the full checklist. Do not treat this emergency step as sufficient evidence.</p>
</section>
<section class="step">
<h2>12. Token Revocation Gate</h2>
<p>Only start provider-side token revocation from a clean device after the dead-man gate is green.</p>
<ul class="checklist">
<li><input type="checkbox"><span>Exact dead-man files are absent from active locations.</span></li>
<li><input type="checkbox"><span>LaunchAgent and LaunchDaemon scans are green.</span></li>
<li><input type="checkbox"><span>Home-level editor hooks cannot recreate the monitor.</span></li>
<li><input type="checkbox"><span>Offline normal boot shows no loaded <code>com.user.gh-token-monitor</code> service and no matching process.</span></li>
<li><input type="checkbox"><span>Only now revoke and rotate GitHub tokens, npm tokens, cloud credentials, SSH keys, and other exposed secrets from a clean device.</span></li>
</ul>
</section>
<section class="gate">
<h2>Final Dead-Man Status</h2>
<p><span class="status green">GREEN</span> The dead-man LaunchAgent is missing or quarantined, matching LaunchAgent/LaunchDaemon content is absent, exact monitor artifacts are absent from active paths, editor hooks cannot recreate it, and offline normal boot shows no recurrence.</p>
<p><span class="status yellow">YELLOW</span> Any category was not checked, any scan was incomplete, the Data volume path was uncertain, or you found payload indicators that have not been analyzed.</p>
<p><span class="status red">RED</span> Any active plist, script, token file, process, editor hook, or package payload can still run <code>gh-token-monitor</code>, call <code>api.github.com/user</code>, or delete the home directory.</p>
</section>
</main>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment